mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-02 00:25:06 +08:00
Feat: Add a web search button to the chat box on the chat page. (#12786)
### What problem does this PR solve? Feat: Add a web search button to the chat box on the chat page. ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -934,6 +934,7 @@ async def chatbots_inputs(dialog_id):
|
|||||||
"title": dialog.name,
|
"title": dialog.name,
|
||||||
"avatar": dialog.icon,
|
"avatar": dialog.icon,
|
||||||
"prologue": dialog.prompt_config.get("prologue", ""),
|
"prologue": dialog.prompt_config.get("prologue", ""),
|
||||||
|
"has_tavily_key": bool(dialog.prompt_config.get("tavily_api_key", "").strip()),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -268,7 +268,7 @@ const FloatingChatWidget = () => {
|
|||||||
|
|
||||||
// Wait for state to update, then send
|
// Wait for state to update, then send
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
handlePressEnter({ enableThinking: false });
|
handlePressEnter({ enableThinking: false, enableInternet: false });
|
||||||
// Clear our local input after sending
|
// Clear our local input after sending
|
||||||
setInputValue('');
|
setInputValue('');
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|||||||
@ -16,7 +16,15 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { CircleStop, Paperclip, Send, Upload, X } from 'lucide-react';
|
import {
|
||||||
|
Atom,
|
||||||
|
CircleStop,
|
||||||
|
Globe,
|
||||||
|
Paperclip,
|
||||||
|
Send,
|
||||||
|
Upload,
|
||||||
|
X,
|
||||||
|
} from 'lucide-react';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@ -32,13 +40,20 @@ interface NextMessageInputProps {
|
|||||||
isShared?: boolean;
|
isShared?: boolean;
|
||||||
showUploadIcon?: boolean;
|
showUploadIcon?: boolean;
|
||||||
isUploading?: boolean;
|
isUploading?: boolean;
|
||||||
onPressEnter({ enableThinking }: { enableThinking: boolean }): void;
|
onPressEnter({
|
||||||
|
enableThinking,
|
||||||
|
enableInternet,
|
||||||
|
}: {
|
||||||
|
enableThinking: boolean;
|
||||||
|
enableInternet: boolean;
|
||||||
|
}): void;
|
||||||
onInputChange: React.ChangeEventHandler<HTMLTextAreaElement>;
|
onInputChange: React.ChangeEventHandler<HTMLTextAreaElement>;
|
||||||
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
stopOutputMessage?(): void;
|
stopOutputMessage?(): void;
|
||||||
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
||||||
removeFile?(file: File): void;
|
removeFile?(file: File): void;
|
||||||
showReasoning?: boolean;
|
showReasoning?: boolean;
|
||||||
|
showInternet?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NextMessageInputOnPressEnterParameter = Parameters<
|
export type NextMessageInputOnPressEnterParameter = Parameters<
|
||||||
@ -58,6 +73,7 @@ export function NextMessageInput({
|
|||||||
onPressEnter,
|
onPressEnter,
|
||||||
removeFile,
|
removeFile,
|
||||||
showReasoning = false,
|
showReasoning = false,
|
||||||
|
showInternet = false,
|
||||||
}: NextMessageInputProps) {
|
}: NextMessageInputProps) {
|
||||||
const [files, setFiles] = React.useState<File[]>([]);
|
const [files, setFiles] = React.useState<File[]>([]);
|
||||||
const [audioInputValue, setAudioInputValue] = React.useState<string | null>(
|
const [audioInputValue, setAudioInputValue] = React.useState<string | null>(
|
||||||
@ -65,11 +81,23 @@ export function NextMessageInput({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [enableThinking, setEnableThinking] = useState(false);
|
const [enableThinking, setEnableThinking] = useState(false);
|
||||||
|
const [enableInternet, setEnableInternet] = useState(false);
|
||||||
|
|
||||||
const handleThinkingToggle = useCallback(() => {
|
const handleThinkingToggle = useCallback(() => {
|
||||||
setEnableThinking((prev) => !prev);
|
setEnableThinking((prev) => !prev);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleInternetToggle = useCallback(() => {
|
||||||
|
setEnableInternet((prev) => !prev);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const pressEnter = useCallback(() => {
|
||||||
|
onPressEnter({
|
||||||
|
enableThinking,
|
||||||
|
enableInternet: showInternet ? enableInternet : false,
|
||||||
|
});
|
||||||
|
}, [onPressEnter, enableThinking, enableInternet, showInternet]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (audioInputValue !== null) {
|
if (audioInputValue !== null) {
|
||||||
onInputChange({
|
onInputChange({
|
||||||
@ -77,11 +105,19 @@ export function NextMessageInput({
|
|||||||
} as React.ChangeEvent<HTMLTextAreaElement>);
|
} as React.ChangeEvent<HTMLTextAreaElement>);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onPressEnter({ enableThinking });
|
pressEnter();
|
||||||
setAudioInputValue(null);
|
setAudioInputValue(null);
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
}, [audioInputValue, onInputChange, onPressEnter, enableThinking]);
|
}, [
|
||||||
|
audioInputValue,
|
||||||
|
onInputChange,
|
||||||
|
onPressEnter,
|
||||||
|
enableThinking,
|
||||||
|
enableInternet,
|
||||||
|
showInternet,
|
||||||
|
pressEnter,
|
||||||
|
]);
|
||||||
|
|
||||||
const onFileReject = React.useCallback((file: File, message: string) => {
|
const onFileReject = React.useCallback((file: File, message: string) => {
|
||||||
toast(message, {
|
toast(message, {
|
||||||
@ -91,9 +127,9 @@ export function NextMessageInput({
|
|||||||
|
|
||||||
const submit = React.useCallback(() => {
|
const submit = React.useCallback(() => {
|
||||||
if (isUploading) return;
|
if (isUploading) return;
|
||||||
onPressEnter({ enableThinking });
|
pressEnter();
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
}, [isUploading, onPressEnter, enableThinking]);
|
}, [isUploading, pressEnter]);
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
@ -205,9 +241,25 @@ export function NextMessageInput({
|
|||||||
)}
|
)}
|
||||||
onClick={handleThinkingToggle}
|
onClick={handleThinkingToggle}
|
||||||
>
|
>
|
||||||
|
<Atom />
|
||||||
<span>Thinking</span>
|
<span>Thinking</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
{showInternet && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
className={cn(
|
||||||
|
'rounded-sm h-7 focus-visible:bg-none! hover:bg-none!',
|
||||||
|
{
|
||||||
|
'bg-accent-primary text-white': enableInternet,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
onClick={handleInternetToggle}
|
||||||
|
>
|
||||||
|
<Globe />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{sendLoading ? (
|
{sendLoading ? (
|
||||||
<Button onClick={stopOutputMessage} className="size-5 rounded-sm">
|
<Button onClick={stopOutputMessage} className="size-5 rounded-sm">
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export interface PromptConfig {
|
|||||||
use_kg: boolean;
|
use_kg: boolean;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
cross_languages?: Array<string>;
|
cross_languages?: Array<string>;
|
||||||
|
tavily_api_key?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Parameter {
|
export interface Parameter {
|
||||||
@ -100,6 +101,7 @@ export interface Message {
|
|||||||
chatBoxId?: string;
|
chatBoxId?: string;
|
||||||
attachment?: IAttachment;
|
attachment?: IAttachment;
|
||||||
reasoning?: boolean;
|
reasoning?: boolean;
|
||||||
|
internet?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IReferenceChunk {
|
export interface IReferenceChunk {
|
||||||
@ -183,6 +185,7 @@ export interface IExternalChatInfo {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
title: string;
|
title: string;
|
||||||
prologue?: string;
|
prologue?: string;
|
||||||
|
has_tavily_key?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMessage extends Message {
|
export interface IMessage extends Message {
|
||||||
|
|||||||
@ -291,6 +291,7 @@ export const useSendAgentMessage = ({
|
|||||||
|
|
||||||
params.session_id = sessionId;
|
params.session_id = sessionId;
|
||||||
params.reasoning = message.reasoning;
|
params.reasoning = message.reasoning;
|
||||||
|
params.internet = message.internet;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -358,13 +359,21 @@ export const useSendAgentMessage = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const handlePressEnter = useCallback(
|
const handlePressEnter = useCallback(
|
||||||
(...[{ enableThinking }]: NextMessageInputOnPressEnterParameter) => {
|
(
|
||||||
|
...[
|
||||||
|
{ enableThinking, enableInternet },
|
||||||
|
]: NextMessageInputOnPressEnterParameter
|
||||||
|
) => {
|
||||||
if (trim(value) === '') return;
|
if (trim(value) === '') return;
|
||||||
const msgBody = buildRequestBody(value);
|
const msgBody = buildRequestBody(value);
|
||||||
if (done) {
|
if (done) {
|
||||||
setValue('');
|
setValue('');
|
||||||
sendMessage({
|
sendMessage({
|
||||||
message: { ...msgBody, reasoning: enableThinking },
|
message: {
|
||||||
|
...msgBody,
|
||||||
|
reasoning: enableThinking,
|
||||||
|
internet: enableInternet,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
addNewestOneQuestion({ ...msgBody, files: fileList });
|
addNewestOneQuestion({ ...msgBody, files: fileList });
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import { useSendMessage } from '../../hooks/use-send-chat-message';
|
|||||||
import { useSendMultipleChatMessage } from '../../hooks/use-send-multiple-message';
|
import { useSendMultipleChatMessage } from '../../hooks/use-send-multiple-message';
|
||||||
import { buildMessageItemReference } from '../../utils';
|
import { buildMessageItemReference } from '../../utils';
|
||||||
import { useAddChatBox } from '../use-add-box';
|
import { useAddChatBox } from '../use-add-box';
|
||||||
|
import { useShowInternet } from '../use-show-internet';
|
||||||
import { useSetDefaultModel } from './use-set-default-model';
|
import { useSetDefaultModel } from './use-set-default-model';
|
||||||
|
|
||||||
type MultipleChatBoxProps = {
|
type MultipleChatBoxProps = {
|
||||||
@ -226,6 +227,8 @@ export function MultipleChatBox({
|
|||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
|
||||||
|
const showInternet = useShowInternet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="h-full flex flex-col px-5">
|
<section className="h-full flex flex-col px-5">
|
||||||
<div className="flex gap-4 flex-1 px-5 pb-14 min-h-0">
|
<div className="flex gap-4 flex-1 px-5 pb-14 min-h-0">
|
||||||
@ -261,6 +264,7 @@ export function MultipleChatBox({
|
|||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
onUpload={handleUploadFile}
|
onUpload={handleUploadFile}
|
||||||
showReasoning
|
showReasoning
|
||||||
|
showInternet={showInternet}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{visible && (
|
{visible && (
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import {
|
|||||||
import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation';
|
import { useCreateConversationBeforeUploadDocument } from '../../hooks/use-create-conversation';
|
||||||
import { useSendMessage } from '../../hooks/use-send-chat-message';
|
import { useSendMessage } from '../../hooks/use-send-chat-message';
|
||||||
import { buildMessageItemReference } from '../../utils';
|
import { buildMessageItemReference } from '../../utils';
|
||||||
|
import { useShowInternet } from '../use-show-internet';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
controller: AbortController;
|
controller: AbortController;
|
||||||
@ -55,6 +56,8 @@ export function SingleChatBox({
|
|||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
|
||||||
|
const showInternet = useShowInternet();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const messages = conversation?.message;
|
const messages = conversation?.message;
|
||||||
if (Array.isArray(messages)) {
|
if (Array.isArray(messages)) {
|
||||||
@ -120,6 +123,7 @@ export function SingleChatBox({
|
|||||||
isUploading={isUploading}
|
isUploading={isUploading}
|
||||||
removeFile={removeFile}
|
removeFile={removeFile}
|
||||||
showReasoning
|
showReasoning
|
||||||
|
showInternet={showInternet}
|
||||||
/>
|
/>
|
||||||
{visible && (
|
{visible && (
|
||||||
<PdfSheet
|
<PdfSheet
|
||||||
|
|||||||
8
web/src/pages/next-chats/chat/use-show-internet.ts
Normal file
8
web/src/pages/next-chats/chat/use-show-internet.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { useFetchDialog } from '@/hooks/use-chat-request';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
export function useShowInternet() {
|
||||||
|
const { data: currentDialog } = useFetchDialog();
|
||||||
|
|
||||||
|
return !isEmpty(currentDialog?.prompt_config?.tavily_api_key);
|
||||||
|
}
|
||||||
@ -135,7 +135,11 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
useCreateConversationBeforeSendMessage();
|
useCreateConversationBeforeSendMessage();
|
||||||
|
|
||||||
const handlePressEnter = useCallback(
|
const handlePressEnter = useCallback(
|
||||||
async (...[{ enableThinking }]: NextMessageInputOnPressEnterParameter) => {
|
async (
|
||||||
|
...[
|
||||||
|
{ enableThinking, enableInternet },
|
||||||
|
]: NextMessageInputOnPressEnterParameter
|
||||||
|
) => {
|
||||||
if (trim(value) === '') return;
|
if (trim(value) === '') return;
|
||||||
|
|
||||||
const data = await createConversationBeforeSendMessage(value);
|
const data = await createConversationBeforeSendMessage(value);
|
||||||
@ -168,6 +172,7 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
files: files,
|
files: files,
|
||||||
conversationId: targetConversationId,
|
conversationId: targetConversationId,
|
||||||
reasoning: enableThinking,
|
reasoning: enableThinking,
|
||||||
|
internet: enableInternet,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -177,7 +177,11 @@ export function useSendMultipleChatMessage(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handlePressEnter = useCallback(
|
const handlePressEnter = useCallback(
|
||||||
async (...[{ enableThinking }]: NextMessageInputOnPressEnterParameter) => {
|
async (
|
||||||
|
...[
|
||||||
|
{ enableThinking, enableInternet },
|
||||||
|
]: NextMessageInputOnPressEnterParameter
|
||||||
|
) => {
|
||||||
if (trim(value) === '') return;
|
if (trim(value) === '') return;
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
|
|
||||||
@ -214,6 +218,7 @@ export function useSendMultipleChatMessage(
|
|||||||
files,
|
files,
|
||||||
conversationId: targetConversationId,
|
conversationId: targetConversationId,
|
||||||
reasoning: enableThinking,
|
reasoning: enableThinking,
|
||||||
|
internet: enableInternet,
|
||||||
},
|
},
|
||||||
chatBoxId,
|
chatBoxId,
|
||||||
currentConversationId: targetConversationId,
|
currentConversationId: targetConversationId,
|
||||||
|
|||||||
@ -73,6 +73,7 @@ export const useSendSharedMessage = () => {
|
|||||||
question: message.content,
|
question: message.content,
|
||||||
session_id: get(derivedMessages, '0.session_id'),
|
session_id: get(derivedMessages, '0.session_id'),
|
||||||
reasoning: message.reasoning,
|
reasoning: message.reasoning,
|
||||||
|
internet: message.internet,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCompletionError(res)) {
|
if (isCompletionError(res)) {
|
||||||
@ -119,7 +120,11 @@ export const useSendSharedMessage = () => {
|
|||||||
}, [answer, addNewestAnswer]);
|
}, [answer, addNewestAnswer]);
|
||||||
|
|
||||||
const handlePressEnter = useCallback(
|
const handlePressEnter = useCallback(
|
||||||
(...[{ enableThinking }]: NextMessageInputOnPressEnterParameter) => {
|
(
|
||||||
|
...[
|
||||||
|
{ enableThinking, enableInternet },
|
||||||
|
]: NextMessageInputOnPressEnterParameter
|
||||||
|
) => {
|
||||||
if (trim(value) === '') return;
|
if (trim(value) === '') return;
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
if (done) {
|
if (done) {
|
||||||
@ -135,6 +140,7 @@ export const useSendSharedMessage = () => {
|
|||||||
id,
|
id,
|
||||||
role: MessageType.User,
|
role: MessageType.User,
|
||||||
reasoning: enableThinking,
|
reasoning: enableThinking,
|
||||||
|
internet: enableInternet,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -124,6 +124,7 @@ const ChatContainer = () => {
|
|||||||
showUploadIcon={false}
|
showUploadIcon={false}
|
||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
showReasoning
|
showReasoning
|
||||||
|
showInternet={chatInfo?.has_tavily_key}
|
||||||
></NextMessageInput>
|
></NextMessageInput>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user