feat(chat): 添加停止输出消息功能 (#12)
在聊天组件中添加了停止输出消息的功能,用户可以在消息生成过程中中断输出。同时,优化了搜索输入框的样式,隐藏了默认的搜索按钮,并替换为自定义的发送和停止按钮。 修改参照:https://github.com/infiniflow/ragflow/pull/6723/files
This commit is contained in:
parent
a51b3168a0
commit
4f5be71eb3
|
@ -53,101 +53,101 @@ jobs:
|
||||||
version: ">=0.8.2"
|
version: ">=0.8.2"
|
||||||
args: "check"
|
args: "check"
|
||||||
|
|
||||||
- name: Build ragflow:nightly-slim
|
# - name: Build ragflow:nightly-slim
|
||||||
run: |
|
# run: |
|
||||||
RUNNER_WORKSPACE_PREFIX=${RUNNER_WORKSPACE_PREFIX:-$HOME}
|
# RUNNER_WORKSPACE_PREFIX=${RUNNER_WORKSPACE_PREFIX:-$HOME}
|
||||||
sudo docker pull ubuntu:22.04
|
# sudo docker pull ubuntu:22.04
|
||||||
sudo docker build --progress=plain --build-arg LIGHTEN=1 --build-arg NEED_MIRROR=1 -f Dockerfile -t infiniflow/ragflow:nightly-slim .
|
# sudo docker build --progress=plain --build-arg LIGHTEN=1 --build-arg NEED_MIRROR=1 -f Dockerfile -t infiniflow/ragflow:nightly-slim .
|
||||||
|
|
||||||
- name: Build ragflow:nightly
|
# - name: Build ragflow:nightly
|
||||||
run: |
|
# run: |
|
||||||
sudo docker build --progress=plain --build-arg NEED_MIRROR=1 -f Dockerfile -t infiniflow/ragflow:nightly .
|
# sudo docker build --progress=plain --build-arg NEED_MIRROR=1 -f Dockerfile -t infiniflow/ragflow:nightly .
|
||||||
|
|
||||||
- name: Start ragflow:nightly-slim
|
# - name: Start ragflow:nightly-slim
|
||||||
run: |
|
# run: |
|
||||||
echo -e "\nRAGFLOW_IMAGE=infiniflow/ragflow:nightly-slim" >> docker/.env
|
# echo -e "\nRAGFLOW_IMAGE=infiniflow/ragflow:nightly-slim" >> docker/.env
|
||||||
sudo docker compose -f docker/docker-compose.yml up -d
|
# sudo docker compose -f docker/docker-compose.yml up -d
|
||||||
|
|
||||||
- name: Stop ragflow:nightly-slim
|
# - name: Stop ragflow:nightly-slim
|
||||||
if: always() # always run this step even if previous steps failed
|
# if: always() # always run this step even if previous steps failed
|
||||||
run: |
|
# run: |
|
||||||
sudo docker compose -f docker/docker-compose.yml down -v
|
# sudo docker compose -f docker/docker-compose.yml down -v
|
||||||
|
|
||||||
- name: Start ragflow:nightly
|
# - name: Start ragflow:nightly
|
||||||
run: |
|
# run: |
|
||||||
echo -e "\nRAGFLOW_IMAGE=infiniflow/ragflow:nightly" >> docker/.env
|
# echo -e "\nRAGFLOW_IMAGE=infiniflow/ragflow:nightly" >> docker/.env
|
||||||
sudo docker compose -f docker/docker-compose.yml up -d
|
# sudo docker compose -f docker/docker-compose.yml up -d
|
||||||
|
|
||||||
- name: Run sdk tests against Elasticsearch
|
# - name: Run sdk tests against Elasticsearch
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_sdk_api && pytest -s --tb=short get_email.py t_dataset.py t_chat.py t_session.py t_document.py t_chunk.py
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_sdk_api && pytest -s --tb=short get_email.py t_dataset.py t_chat.py t_session.py t_document.py t_chunk.py
|
||||||
|
|
||||||
- name: Run frontend api tests against Elasticsearch
|
# - name: Run frontend api tests against Elasticsearch
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_frontend_api && pytest -s --tb=short get_email.py test_dataset.py
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_frontend_api && pytest -s --tb=short get_email.py test_dataset.py
|
||||||
|
|
||||||
- name: Run http api tests against Elasticsearch
|
# - name: Run http api tests against Elasticsearch
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_http_api && pytest -s --tb=short -m "not slow"
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_http_api && pytest -s --tb=short -m "not slow"
|
||||||
|
|
||||||
- name: Stop ragflow:nightly
|
# - name: Stop ragflow:nightly
|
||||||
if: always() # always run this step even if previous steps failed
|
# if: always() # always run this step even if previous steps failed
|
||||||
run: |
|
# run: |
|
||||||
sudo docker compose -f docker/docker-compose.yml down -v
|
# sudo docker compose -f docker/docker-compose.yml down -v
|
||||||
|
|
||||||
- name: Start ragflow:nightly
|
# - name: Start ragflow:nightly
|
||||||
run: |
|
# run: |
|
||||||
sudo DOC_ENGINE=infinity docker compose -f docker/docker-compose.yml up -d
|
# sudo DOC_ENGINE=infinity docker compose -f docker/docker-compose.yml up -d
|
||||||
|
|
||||||
- name: Run sdk tests against Infinity
|
# - name: Run sdk tests against Infinity
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_sdk_api && pytest -s --tb=short get_email.py t_dataset.py t_chat.py t_session.py t_document.py t_chunk.py
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_sdk_api && pytest -s --tb=short get_email.py t_dataset.py t_chat.py t_session.py t_document.py t_chunk.py
|
||||||
|
|
||||||
- name: Run frontend api tests against Infinity
|
# - name: Run frontend api tests against Infinity
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_frontend_api && pytest -s --tb=short get_email.py test_dataset.py
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_frontend_api && pytest -s --tb=short get_email.py test_dataset.py
|
||||||
|
|
||||||
- name: Run http api tests against Infinity
|
# - name: Run http api tests against Infinity
|
||||||
run: |
|
# run: |
|
||||||
export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
# export http_proxy=""; export https_proxy=""; export no_proxy=""; export HTTP_PROXY=""; export HTTPS_PROXY=""; export NO_PROXY=""
|
||||||
export HOST_ADDRESS=http://host.docker.internal:9380
|
# export HOST_ADDRESS=http://host.docker.internal:9380
|
||||||
until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
# until sudo docker exec ragflow-server curl -s --connect-timeout 5 ${HOST_ADDRESS} > /dev/null; do
|
||||||
echo "Waiting for service to be available..."
|
# echo "Waiting for service to be available..."
|
||||||
sleep 5
|
# sleep 5
|
||||||
done
|
# done
|
||||||
cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_http_api && DOC_ENGINE=infinity pytest -s --tb=short -m "not slow"
|
# cd sdk/python && uv sync --python 3.10 --frozen && uv pip install . && source .venv/bin/activate && cd test/test_http_api && DOC_ENGINE=infinity pytest -s --tb=short -m "not slow"
|
||||||
|
|
||||||
- name: Stop ragflow:nightly
|
# - name: Stop ragflow:nightly
|
||||||
if: always() # always run this step even if previous steps failed
|
# if: always() # always run this step even if previous steps failed
|
||||||
run: |
|
# run: |
|
||||||
sudo DOC_ENGINE=infinity docker compose -f docker/docker-compose.yml down -v
|
# sudo DOC_ENGINE=infinity docker compose -f docker/docker-compose.yml down -v
|
||||||
|
|
|
@ -128,6 +128,10 @@ This repository is available under the [Ragflow
|
||||||
|
|
||||||
- [v3-admin-vite](https://github.com/un-pany/v3-admin-vite)
|
- [v3-admin-vite](https://github.com/un-pany/v3-admin-vite)
|
||||||
|
|
||||||
|
## 更新信息获取
|
||||||
|
|
||||||
|
主要更新日志会在我的微信公众号[我有一计]上发布,欢迎关注。
|
||||||
|
|
||||||
## Star History
|
## Star History
|
||||||
|
|
||||||

|

|
|
@ -11,8 +11,6 @@ import {
|
||||||
CloseCircleOutlined,
|
CloseCircleOutlined,
|
||||||
InfoCircleOutlined,
|
InfoCircleOutlined,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
PaperClipOutlined,
|
|
||||||
SendOutlined,
|
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import type { GetProp, UploadFile } from 'antd';
|
import type { GetProp, UploadFile } from 'antd';
|
||||||
import {
|
import {
|
||||||
|
@ -29,6 +27,7 @@ import {
|
||||||
UploadProps,
|
UploadProps,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
|
import { CircleStop, Paperclip, SendHorizontal } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
memo,
|
memo,
|
||||||
|
@ -72,6 +71,7 @@ interface IProps {
|
||||||
isShared?: boolean;
|
isShared?: boolean;
|
||||||
showUploadIcon?: boolean;
|
showUploadIcon?: boolean;
|
||||||
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
|
stopOutputMessage?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBase64 = (file: FileType): Promise<string> =>
|
const getBase64 = (file: FileType): Promise<string> =>
|
||||||
|
@ -94,6 +94,7 @@ const MessageInput = ({
|
||||||
showUploadIcon = true,
|
showUploadIcon = true,
|
||||||
createConversationBeforeUploadDocument,
|
createConversationBeforeUploadDocument,
|
||||||
uploadMethod = 'upload_and_parse',
|
uploadMethod = 'upload_and_parse',
|
||||||
|
stopOutputMessage,
|
||||||
}: IProps) => {
|
}: IProps) => {
|
||||||
const { t } = useTranslate('chat');
|
const { t } = useTranslate('chat');
|
||||||
const { removeDocument } = useRemoveNextDocument();
|
const { removeDocument } = useRemoveNextDocument();
|
||||||
|
@ -150,6 +151,14 @@ const MessageInput = ({
|
||||||
|
|
||||||
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
const isUploadingFile = fileList.some((x) => x.status === 'uploading');
|
||||||
|
|
||||||
|
const handlePressEnter = useCallback(async () => {
|
||||||
|
if (isUploadingFile) return;
|
||||||
|
const ids = getFileIds(fileList.filter((x) => isUploadSuccess(x)));
|
||||||
|
|
||||||
|
onPressEnter(ids);
|
||||||
|
setFileList([]);
|
||||||
|
}, [fileList, onPressEnter, isUploadingFile]);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
async (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
async (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
// check if it was shift + enter
|
// check if it was shift + enter
|
||||||
|
@ -160,22 +169,9 @@ const MessageInput = ({
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
handlePressEnter();
|
handlePressEnter();
|
||||||
},
|
},
|
||||||
[fileList, onPressEnter, isUploadingFile],
|
[sendDisabled, isUploadingFile, sendLoading, handlePressEnter],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePressEnter = useCallback(async () => {
|
|
||||||
if (isUploadingFile) return;
|
|
||||||
const ids = getFileIds(fileList.filter((x) => isUploadSuccess(x)));
|
|
||||||
|
|
||||||
onPressEnter(ids);
|
|
||||||
setFileList([]);
|
|
||||||
}, [fileList, onPressEnter, isUploadingFile]);
|
|
||||||
|
|
||||||
const [isComposing, setIsComposing] = useState(false);
|
|
||||||
|
|
||||||
const handleCompositionStart = () => setIsComposing(true);
|
|
||||||
const handleCompositionEnd = () => setIsComposing(false);
|
|
||||||
|
|
||||||
const handleRemove = useCallback(
|
const handleRemove = useCallback(
|
||||||
async (file: UploadFile) => {
|
async (file: UploadFile) => {
|
||||||
const ids = get(file, 'response.data', []);
|
const ids = get(file, 'response.data', []);
|
||||||
|
@ -199,6 +195,10 @@ const MessageInput = ({
|
||||||
[removeDocument, deleteDocument, isShared],
|
[removeDocument, deleteDocument, isShared],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleStopOutputMessage = useCallback(() => {
|
||||||
|
stopOutputMessage?.();
|
||||||
|
}, [stopOutputMessage]);
|
||||||
|
|
||||||
const getDocumentInfoById = useCallback(
|
const getDocumentInfoById = useCallback(
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
return documentInfos.find((x) => x.id === id);
|
return documentInfos.find((x) => x.id === id);
|
||||||
|
@ -238,8 +238,6 @@ const MessageInput = ({
|
||||||
autoSize={{ minRows: 2, maxRows: 10 }}
|
autoSize={{ minRows: 2, maxRows: 10 }}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={handleCompositionEnd}
|
|
||||||
/>
|
/>
|
||||||
<Divider style={{ margin: '5px 30px 10px 0px' }} />
|
<Divider style={{ margin: '5px 30px 10px 0px' }} />
|
||||||
<Flex justify="space-between" align="center">
|
<Flex justify="space-between" align="center">
|
||||||
|
@ -342,18 +340,24 @@ const MessageInput = ({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type={'primary'} disabled={disabled}>
|
<Button type={'primary'} disabled={disabled}>
|
||||||
<PaperClipOutlined />
|
<Paperclip className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</Upload>
|
</Upload>
|
||||||
)}
|
)}
|
||||||
<Button
|
{sendLoading ? (
|
||||||
type="primary"
|
<Button onClick={handleStopOutputMessage}>
|
||||||
onClick={handlePressEnter}
|
<CircleStop className="size-5" />
|
||||||
loading={sendLoading}
|
</Button>
|
||||||
disabled={sendDisabled || isUploadingFile || sendLoading}
|
) : (
|
||||||
>
|
<Button
|
||||||
<SendOutlined />
|
type="primary"
|
||||||
</Button>
|
onClick={handlePressEnter}
|
||||||
|
loading={sendLoading}
|
||||||
|
disabled={sendDisabled || isUploadingFile || sendLoading}
|
||||||
|
>
|
||||||
|
<SendHorizontal className="size-5" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
@ -160,6 +160,11 @@ export const useSendMessageWithSse = (
|
||||||
const [answer, setAnswer] = useState<IAnswer>({} as IAnswer);
|
const [answer, setAnswer] = useState<IAnswer>({} as IAnswer);
|
||||||
const [done, setDone] = useState(true);
|
const [done, setDone] = useState(true);
|
||||||
const timer = useRef<any>();
|
const timer = useRef<any>();
|
||||||
|
const sseRef = useRef<AbortController>();
|
||||||
|
|
||||||
|
const initializeSseRef = useCallback(() => {
|
||||||
|
sseRef.current = new AbortController();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const resetAnswer = useCallback(() => {
|
const resetAnswer = useCallback(() => {
|
||||||
if (timer.current) {
|
if (timer.current) {
|
||||||
|
@ -176,6 +181,7 @@ export const useSendMessageWithSse = (
|
||||||
body: any,
|
body: any,
|
||||||
controller?: AbortController,
|
controller?: AbortController,
|
||||||
): Promise<{ response: Response; data: ResponseType } | undefined> => {
|
): Promise<{ response: Response; data: ResponseType } | undefined> => {
|
||||||
|
initializeSseRef();
|
||||||
try {
|
try {
|
||||||
setDone(false);
|
setDone(false);
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
|
@ -185,7 +191,7 @@ export const useSendMessageWithSse = (
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
signal: controller?.signal,
|
signal: controller?.signal || sseRef.current?.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = response.clone().json();
|
const res = response.clone().json();
|
||||||
|
@ -230,10 +236,14 @@ export const useSendMessageWithSse = (
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[url, resetAnswer],
|
[initializeSseRef, url, resetAnswer],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { send, answer, done, setDone, resetAnswer };
|
const stopOutputMessage = useCallback(() => {
|
||||||
|
sseRef.current?.abort();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { send, answer, done, setDone, resetAnswer, stopOutputMessage };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSpeechWithSse = (url: string = api.tts) => {
|
export const useSpeechWithSse = (url: string = api.tts) => {
|
||||||
|
|
|
@ -40,6 +40,7 @@ const ChatContainer = ({ controller }: IProps) => {
|
||||||
handlePressEnter,
|
handlePressEnter,
|
||||||
regenerateMessage,
|
regenerateMessage,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
|
stopOutputMessage,
|
||||||
} = useSendNextMessage(controller);
|
} = useSendNextMessage(controller);
|
||||||
|
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
|
@ -100,6 +101,7 @@ const ChatContainer = ({ controller }: IProps) => {
|
||||||
createConversationBeforeUploadDocument={
|
createConversationBeforeUploadDocument={
|
||||||
createConversationBeforeUploadDocument
|
createConversationBeforeUploadDocument
|
||||||
}
|
}
|
||||||
|
stopOutputMessage={stopOutputMessage}
|
||||||
></MessageInput>
|
></MessageInput>
|
||||||
</Flex>
|
</Flex>
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|
|
@ -375,6 +375,10 @@ export const useSendNextMessage = (controller: AbortController) => {
|
||||||
const { setConversationIsNew, getConversationIsNew } =
|
const { setConversationIsNew, getConversationIsNew } =
|
||||||
useSetChatRouteParams();
|
useSetChatRouteParams();
|
||||||
|
|
||||||
|
const stopOutputMessage = useCallback(() => {
|
||||||
|
controller.abort();
|
||||||
|
}, [controller]);
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
const sendMessage = useCallback(
|
||||||
async ({
|
async ({
|
||||||
message,
|
message,
|
||||||
|
@ -490,6 +494,7 @@ export const useSendNextMessage = (controller: AbortController) => {
|
||||||
ref,
|
ref,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
|
stopOutputMessage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ const ChatContainer = () => {
|
||||||
ref,
|
ref,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
hasError,
|
hasError,
|
||||||
|
stopOutputMessage,
|
||||||
} = useSendSharedMessage();
|
} = useSendSharedMessage();
|
||||||
const sendDisabled = useSendButtonDisabled(value);
|
const sendDisabled = useSendButtonDisabled(value);
|
||||||
|
|
||||||
|
@ -105,6 +106,7 @@ const ChatContainer = () => {
|
||||||
sendLoading={sendLoading}
|
sendLoading={sendLoading}
|
||||||
uploadMethod="external_upload_and_parse"
|
uploadMethod="external_upload_and_parse"
|
||||||
showUploadIcon={false}
|
showUploadIcon={false}
|
||||||
|
stopOutputMessage={stopOutputMessage}
|
||||||
></MessageInput>
|
></MessageInput>
|
||||||
</Flex>
|
</Flex>
|
||||||
{visible && (
|
{visible && (
|
||||||
|
|
|
@ -49,7 +49,7 @@ export const useSendSharedMessage = () => {
|
||||||
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, stopOutputMessage } = useSendMessageWithSse(
|
||||||
`/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`,
|
`/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`,
|
||||||
);
|
);
|
||||||
const {
|
const {
|
||||||
|
@ -144,5 +144,6 @@ export const useSendSharedMessage = () => {
|
||||||
loading: false,
|
loading: false,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
hasError,
|
hasError,
|
||||||
|
stopOutputMessage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,7 @@ const FlowChatBox = () => {
|
||||||
ref,
|
ref,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
reference,
|
reference,
|
||||||
|
stopOutputMessage,
|
||||||
} = useSendNextMessage();
|
} = useSendNextMessage();
|
||||||
|
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
|
@ -75,6 +76,7 @@ const FlowChatBox = () => {
|
||||||
conversationId=""
|
conversationId=""
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
|
stopOutputMessage={stopOutputMessage}
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|
|
@ -57,7 +57,9 @@ export const useSendNextMessage = () => {
|
||||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
const { refetch } = useFetchFlow();
|
const { refetch } = useFetchFlow();
|
||||||
|
|
||||||
const { send, answer, done } = useSendMessageWithSse(api.runCanvas);
|
const { send, answer, done, stopOutputMessage } = useSendMessageWithSse(
|
||||||
|
api.runCanvas,
|
||||||
|
);
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
const sendMessage = useCallback(
|
||||||
async ({ message }: { message: Message; messages?: Message[] }) => {
|
async ({ message }: { message: Message; messages?: Message[] }) => {
|
||||||
|
@ -134,5 +136,6 @@ export const useSendNextMessage = () => {
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
ref,
|
ref,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
|
stopOutputMessage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,9 @@ import {
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export const useSendQuestion = (kbIds: string[]) => {
|
export const useSendQuestion = (kbIds: string[]) => {
|
||||||
const { send, answer, done } = useSendMessageWithSse(api.ask);
|
const { send, answer, done, stopOutputMessage } = useSendMessageWithSse(
|
||||||
|
api.ask,
|
||||||
|
);
|
||||||
const { testChunk, loading } = useTestChunkRetrieval();
|
const { testChunk, loading } = useTestChunkRetrieval();
|
||||||
const [sendingLoading, setSendingLoading] = useState(false);
|
const [sendingLoading, setSendingLoading] = useState(false);
|
||||||
const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer);
|
const [currentAnswer, setCurrentAnswer] = useState({} as IAnswer);
|
||||||
|
@ -116,6 +118,7 @@ export const useSendQuestion = (kbIds: string[]) => {
|
||||||
isFirstRender,
|
isFirstRender,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
isSearchStrEmpty: isEmpty(trim(searchStr)),
|
isSearchStrEmpty: isEmpty(trim(searchStr)),
|
||||||
|
stopOutputMessage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,12 @@
|
||||||
.input();
|
.input();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.searchInput {
|
||||||
|
:global(.ant-input-search-button) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.appIcon {
|
.appIcon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
|
||||||
import { IReference } from '@/interfaces/database/chat';
|
import { IReference } from '@/interfaces/database/chat';
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Divider,
|
Divider,
|
||||||
Flex,
|
Flex,
|
||||||
|
@ -28,9 +29,11 @@ import {
|
||||||
Tag,
|
Tag,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
import DOMPurify from 'dompurify';
|
import DOMPurify from 'dompurify';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import { useMemo, useState } from 'react';
|
import { CircleStop, SendHorizontal } from 'lucide-react';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import MarkdownContent from '../chat/markdown-content';
|
import MarkdownContent from '../chat/markdown-content';
|
||||||
import { useSendQuestion, useShowMindMapDrawer } from './hooks';
|
import { useSendQuestion, useShowMindMapDrawer } from './hooks';
|
||||||
|
@ -64,6 +67,7 @@ const SearchPage = () => {
|
||||||
isFirstRender,
|
isFirstRender,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
isSearchStrEmpty,
|
isSearchStrEmpty,
|
||||||
|
stopOutputMessage,
|
||||||
} = useSendQuestion(checkedWithoutEmbeddingIdList);
|
} = useSendQuestion(checkedWithoutEmbeddingIdList);
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
@ -81,18 +85,35 @@ const SearchPage = () => {
|
||||||
handleTestChunk(selectedDocumentIds, pageNumber, pageSize);
|
handleTestChunk(selectedDocumentIds, pageNumber, pageSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSearch = useCallback(() => {
|
||||||
|
sendQuestion(searchStr);
|
||||||
|
}, [searchStr, sendQuestion]);
|
||||||
|
|
||||||
const InputSearch = (
|
const InputSearch = (
|
||||||
<Search
|
<Search
|
||||||
value={searchStr}
|
value={searchStr}
|
||||||
onChange={handleSearchStrChange}
|
onChange={handleSearchStrChange}
|
||||||
placeholder={t('header.search')}
|
placeholder={t('header.search')}
|
||||||
allowClear
|
allowClear
|
||||||
enterButton
|
addonAfter={
|
||||||
|
sendingLoading ? (
|
||||||
|
<Button onClick={stopOutputMessage}>
|
||||||
|
<CircleStop />
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button onClick={handleSearch}>
|
||||||
|
<SendHorizontal className="size-5 text-blue-500" />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
onSearch={sendQuestion}
|
onSearch={sendQuestion}
|
||||||
size="large"
|
size="large"
|
||||||
loading={sendingLoading}
|
loading={sendingLoading}
|
||||||
disabled={checkedWithoutEmbeddingIdList.length === 0}
|
disabled={checkedWithoutEmbeddingIdList.length === 0}
|
||||||
className={isFirstRender ? styles.globalInput : styles.partialInput}
|
className={classNames(
|
||||||
|
styles.searchInput,
|
||||||
|
isFirstRender ? styles.globalInput : styles.partialInput,
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue