mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix: Fixed the loss of Await Response function on the share page and other style issues #3221 (#9216)
### What problem does this PR solve? Fix: Fixed the loss of Await Response function on the share page and other style issues #3221 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -28,8 +28,11 @@ function AccordionItem({
|
||||
function AccordionTrigger({
|
||||
className,
|
||||
children,
|
||||
hideDownIcon = false,
|
||||
...props
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
|
||||
}: React.ComponentProps<typeof AccordionPrimitive.Trigger> & {
|
||||
hideDownIcon?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
@ -41,7 +44,9 @@ function AccordionTrigger({
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{!hideDownIcon && (
|
||||
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
|
||||
)}
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
);
|
||||
|
||||
@ -1322,6 +1322,7 @@ This delimiter is used to split the input text into several text pieces echo of
|
||||
logTimeline: {
|
||||
begin: 'Ready to begin',
|
||||
agent: 'Agent is thinking',
|
||||
userFillUp: 'Waiting for you',
|
||||
retrieval: 'Looking up knowledge',
|
||||
message: 'Agent says',
|
||||
awaitResponse: 'Waiting for you',
|
||||
|
||||
@ -1265,6 +1265,7 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
|
||||
subject: '主题',
|
||||
logTimeline: {
|
||||
begin: '准备开始',
|
||||
userFillUp: '等你输入',
|
||||
agent: '智能体正在思考',
|
||||
retrieval: '查找知识',
|
||||
message: '回复',
|
||||
|
||||
@ -13,14 +13,11 @@ import {
|
||||
useUploadCanvasFileWithProgress,
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { Message } from '@/interfaces/database/chat';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import { get } from 'lodash';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useParams } from 'umi';
|
||||
import DebugContent from '../debug-content';
|
||||
import { BeginQuery } from '../interface';
|
||||
import { buildBeginQueryWithObject } from '../utils';
|
||||
import { useAwaitCompentData } from '../hooks/use-chat-logic';
|
||||
|
||||
function AgentChatBox() {
|
||||
const {
|
||||
@ -43,33 +40,12 @@ function AgentChatBox() {
|
||||
const { data: canvasInfo } = useFetchAgent();
|
||||
const { id: canvasId } = useParams();
|
||||
const { uploadCanvasFile, loading } = useUploadCanvasFileWithProgress();
|
||||
const getInputs = useCallback((message: Message) => {
|
||||
return get(message, 'data.inputs', {}) as Record<string, BeginQuery>;
|
||||
}, []);
|
||||
|
||||
const buildInputList = useCallback(
|
||||
(message: Message) => {
|
||||
return Object.entries(getInputs(message)).map(([key, val]) => {
|
||||
return {
|
||||
...val,
|
||||
key,
|
||||
};
|
||||
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
|
||||
derivedMessages,
|
||||
sendFormMessage,
|
||||
canvasId: canvasId as string,
|
||||
});
|
||||
},
|
||||
[getInputs],
|
||||
);
|
||||
|
||||
const handleOk = useCallback(
|
||||
(message: Message) => (values: BeginQuery[]) => {
|
||||
const inputs = getInputs(message);
|
||||
const nextInputs = buildBeginQueryWithObject(inputs, values);
|
||||
sendFormMessage({
|
||||
inputs: nextInputs,
|
||||
id: canvasId,
|
||||
});
|
||||
},
|
||||
[canvasId, getInputs, sendFormMessage],
|
||||
);
|
||||
|
||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||
useCallback(
|
||||
@ -79,16 +55,7 @@ function AgentChatBox() {
|
||||
},
|
||||
[appendUploadResponseList, uploadCanvasFile],
|
||||
);
|
||||
const isWaitting = useMemo(() => {
|
||||
const temp = derivedMessages?.some((message, i) => {
|
||||
const flag =
|
||||
message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 === i &&
|
||||
message.data;
|
||||
return flag;
|
||||
});
|
||||
return temp;
|
||||
}, [derivedMessages]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
||||
|
||||
@ -269,7 +269,7 @@ export const useSendAgentMessage = (
|
||||
|
||||
const sendFormMessage = useCallback(
|
||||
(body: { id?: string; inputs: Record<string, BeginQuery> }) => {
|
||||
send(body);
|
||||
send({ ...body, session_id: sessionId });
|
||||
addNewestOneQuestion({
|
||||
content: Object.entries(body.inputs)
|
||||
.map(([key, val]) => `${key}: ${val.value}`)
|
||||
@ -277,7 +277,7 @@ export const useSendAgentMessage = (
|
||||
role: MessageType.User,
|
||||
});
|
||||
},
|
||||
[addNewestOneQuestion, send],
|
||||
[addNewestOneQuestion, send, sessionId],
|
||||
);
|
||||
|
||||
// reset session
|
||||
|
||||
60
web/src/pages/agent/hooks/use-chat-logic.ts
Normal file
60
web/src/pages/agent/hooks/use-chat-logic.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { Message } from '@/interfaces/database/chat';
|
||||
import { IMessage } from '@/pages/chat/interface';
|
||||
import { get } from 'lodash';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { BeginQuery } from '../interface';
|
||||
import { buildBeginQueryWithObject } from '../utils';
|
||||
type IAwaitCompentData = {
|
||||
derivedMessages: IMessage[];
|
||||
sendFormMessage: (params: {
|
||||
inputs: Record<string, BeginQuery>;
|
||||
id: string;
|
||||
}) => void;
|
||||
canvasId: string;
|
||||
};
|
||||
const useAwaitCompentData = (props: IAwaitCompentData) => {
|
||||
const { derivedMessages, sendFormMessage, canvasId } = props;
|
||||
|
||||
const getInputs = useCallback((message: Message) => {
|
||||
return get(message, 'data.inputs', {}) as Record<string, BeginQuery>;
|
||||
}, []);
|
||||
|
||||
const buildInputList = useCallback(
|
||||
(message: Message) => {
|
||||
return Object.entries(getInputs(message)).map(([key, val]) => {
|
||||
return {
|
||||
...val,
|
||||
key,
|
||||
};
|
||||
});
|
||||
},
|
||||
[getInputs],
|
||||
);
|
||||
|
||||
const handleOk = useCallback(
|
||||
(message: Message) => (values: BeginQuery[]) => {
|
||||
const inputs = getInputs(message);
|
||||
const nextInputs = buildBeginQueryWithObject(inputs, values);
|
||||
sendFormMessage({
|
||||
inputs: nextInputs,
|
||||
id: canvasId,
|
||||
});
|
||||
},
|
||||
[getInputs, sendFormMessage, canvasId],
|
||||
);
|
||||
|
||||
const isWaitting = useMemo(() => {
|
||||
const temp = derivedMessages?.some((message, i) => {
|
||||
const flag =
|
||||
message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 === i &&
|
||||
message.data;
|
||||
return flag;
|
||||
});
|
||||
return temp;
|
||||
}, [derivedMessages]);
|
||||
return { getInputs, buildInputList, handleOk, isWaitting };
|
||||
};
|
||||
|
||||
export { useAwaitCompentData };
|
||||
@ -14,24 +14,37 @@ import {
|
||||
import { cn } from '@/lib/utils';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Operator } from '../constant';
|
||||
import OperatorIcon from '../operator-icon';
|
||||
import OperatorIcon, { SVGIconMap } from '../operator-icon';
|
||||
import {
|
||||
JsonViewer,
|
||||
toLowerCaseStringAndDeleteChar,
|
||||
typeMap,
|
||||
} from './workFlowTimeline';
|
||||
const capitalizeWords = (str: string, separator: string = '_'): string => {
|
||||
if (!str) return '';
|
||||
type IToolIcon =
|
||||
| Operator.ArXiv
|
||||
| Operator.GitHub
|
||||
| Operator.Bing
|
||||
| Operator.DuckDuckGo
|
||||
| Operator.Google
|
||||
| Operator.GoogleScholar
|
||||
| Operator.PubMed
|
||||
| Operator.TavilyExtract
|
||||
| Operator.TavilySearch
|
||||
| Operator.Wikipedia
|
||||
| Operator.YahooFinance
|
||||
| Operator.WenCai
|
||||
| Operator.Crawler;
|
||||
|
||||
return str
|
||||
.split(separator)
|
||||
.map((word) => {
|
||||
const capitalizeWords = (str: string, separator: string = '_'): string[] => {
|
||||
if (!str) return [''];
|
||||
|
||||
const resultStrArr = str.split(separator).map((word) => {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
})
|
||||
.join(' ');
|
||||
});
|
||||
return resultStrArr;
|
||||
};
|
||||
const changeToolName = (toolName: any) => {
|
||||
const name = 'Agent ' + capitalizeWords(toolName);
|
||||
const name = 'Agent ' + capitalizeWords(toolName).join(' ');
|
||||
return name;
|
||||
};
|
||||
const ToolTimelineItem = ({
|
||||
@ -61,6 +74,8 @@ const ToolTimelineItem = ({
|
||||
return (
|
||||
<>
|
||||
{filteredTools?.map((tool, idx) => {
|
||||
const toolName = capitalizeWords(tool.tool_name, '_').join('');
|
||||
|
||||
return (
|
||||
<TimelineItem
|
||||
key={'tool_' + idx}
|
||||
@ -105,7 +120,11 @@ const ToolTimelineItem = ({
|
||||
<div className="size-6 flex items-center justify-center">
|
||||
<OperatorIcon
|
||||
className="size-4"
|
||||
name={'Agent' as Operator}
|
||||
name={
|
||||
(SVGIconMap[toolName as IToolIcon]
|
||||
? toolName
|
||||
: 'Agent') as Operator
|
||||
}
|
||||
></OperatorIcon>
|
||||
</div>
|
||||
</div>
|
||||
@ -119,12 +138,14 @@ const ToolTimelineItem = ({
|
||||
className="bg-background-card px-3"
|
||||
>
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<AccordionTrigger
|
||||
hideDownIcon={isShare && isEmpty(tool.arguments)}
|
||||
>
|
||||
<div className="flex gap-2 items-center">
|
||||
{!isShare && (
|
||||
<span>
|
||||
{parentName(tool.path) + ' '}
|
||||
{capitalizeWords(tool.tool_name, '_')}
|
||||
{capitalizeWords(tool.tool_name, '_').join(' ')}
|
||||
</span>
|
||||
)}
|
||||
{isShare && (
|
||||
@ -142,7 +163,7 @@ const ToolTimelineItem = ({
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'border-background -end-1 -top-1 size-2 rounded-full border-2 bg-dot-green',
|
||||
'border-background -end-1 -top-1 size-2 rounded-full bg-dot-green',
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Online</span>
|
||||
@ -161,7 +182,7 @@ const ToolTimelineItem = ({
|
||||
)}
|
||||
{isShare && !isEmpty(tool.arguments) && (
|
||||
<AccordionContent>
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-2 bg-muted p-2">
|
||||
{tool &&
|
||||
tool.arguments &&
|
||||
Object.entries(tool.arguments).length &&
|
||||
@ -171,8 +192,8 @@ const ToolTimelineItem = ({
|
||||
<div className="text-sm font-medium leading-none">
|
||||
{key}
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{val || ''}
|
||||
<div className="text-sm text-muted-foreground mt-1">
|
||||
{val as string}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -51,7 +51,7 @@ export function JsonViewer({
|
||||
src={data}
|
||||
displaySize
|
||||
collapseStringsAfterLength={100000000000}
|
||||
className="w-full h-[200px] break-words overflow-auto scrollbar-auto p-2 bg-slate-800"
|
||||
className="w-full h-[200px] break-words overflow-auto scrollbar-auto p-2 bg-muted"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
@ -81,11 +81,21 @@ export const typeMap = {
|
||||
httpRequest: t('flow.logTimeline.httpRequest'),
|
||||
wenCai: t('flow.logTimeline.wenCai'),
|
||||
yahooFinance: t('flow.logTimeline.yahooFinance'),
|
||||
userFillUp: t('flow.logTimeline.userFillUp'),
|
||||
};
|
||||
export const toLowerCaseStringAndDeleteChar = (
|
||||
str: string,
|
||||
char: string = '_',
|
||||
) => str.toLowerCase().replace(/ /g, '').replaceAll(char, '');
|
||||
|
||||
// Convert all keys in typeMap to lowercase and output the new typeMap
|
||||
export const typeMapLowerCase = Object.fromEntries(
|
||||
Object.entries(typeMap).map(([key, value]) => [
|
||||
toLowerCaseStringAndDeleteChar(key),
|
||||
value,
|
||||
]),
|
||||
);
|
||||
|
||||
function getInputsOrOutputs(
|
||||
nodeEventList: INodeData[],
|
||||
field: 'inputs' | 'outputs',
|
||||
@ -247,16 +257,19 @@ export const WorkFlowTimeline = ({
|
||||
className="bg-background-card px-3"
|
||||
>
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<AccordionTrigger
|
||||
hideDownIcon={isShare && !x.data?.thoughts}
|
||||
>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>
|
||||
{!isShare && getNodeName(x.data?.component_name)}
|
||||
{isShare &&
|
||||
typeMap[
|
||||
(typeMapLowerCase[
|
||||
toLowerCaseStringAndDeleteChar(
|
||||
nodeLabel,
|
||||
) as keyof typeof typeMap
|
||||
]}
|
||||
] ??
|
||||
nodeLabel)}
|
||||
</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
@ -294,7 +307,7 @@ export const WorkFlowTimeline = ({
|
||||
{isShare && x.data?.thoughts && (
|
||||
<AccordionContent>
|
||||
<div className="space-y-2">
|
||||
<div className="w-full h-[200px] break-words overflow-auto scrollbar-auto p-2 bg-slate-800">
|
||||
<div className="w-full h-[200px] break-words overflow-auto scrollbar-auto p-2 bg-muted">
|
||||
<HightLightMarkdown>
|
||||
{x.data.thoughts || ''}
|
||||
</HightLightMarkdown>
|
||||
|
||||
@ -38,7 +38,7 @@ export const OperatorIconMap = {
|
||||
[Operator.Email]: 'sendemail-0',
|
||||
};
|
||||
|
||||
const SVGIconMap = {
|
||||
export const SVGIconMap = {
|
||||
[Operator.ArXiv]: ArxivIcon,
|
||||
[Operator.GitHub]: GithubIcon,
|
||||
[Operator.Bing]: BingIcon,
|
||||
|
||||
@ -231,7 +231,7 @@ const AgentLogPage: React.FC = () => {
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold mb-4">Log</h1>
|
||||
|
||||
<div className="flex justify-end space-x-2 mb-4">
|
||||
<div className="flex justify-end space-x-2 mb-4 text-foreground">
|
||||
<div className="flex items-center space-x-2">
|
||||
<span>ID/Title</span>
|
||||
<SearchInput
|
||||
|
||||
@ -13,7 +13,9 @@ import {
|
||||
} from '@/hooks/use-agent-request';
|
||||
import { cn } from '@/lib/utils';
|
||||
import i18n from '@/locales/config';
|
||||
import DebugContent from '@/pages/agent/debug-content';
|
||||
import { useCacheChatLog } from '@/pages/agent/hooks/use-cache-chat-log';
|
||||
import { useAwaitCompentData } from '@/pages/agent/hooks/use-chat-logic';
|
||||
import { IInputs } from '@/pages/agent/interface';
|
||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
@ -56,10 +58,15 @@ const ChatContainer = () => {
|
||||
appendUploadResponseList,
|
||||
parameterDialogVisible,
|
||||
showParameterDialog,
|
||||
sendFormMessage,
|
||||
ok,
|
||||
resetSession,
|
||||
} = useSendNextSharedMessage(addEventList);
|
||||
|
||||
const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
|
||||
derivedMessages,
|
||||
sendFormMessage,
|
||||
canvasId: conversationId as string,
|
||||
});
|
||||
const sendDisabled = useSendButtonDisabled(value);
|
||||
const appConf = useFetchAppConf();
|
||||
const { data: inputsData } = useFetchExternalAgentInputs();
|
||||
@ -171,7 +178,28 @@ const ChatContainer = () => {
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
sendLoading={sendLoading}
|
||||
></MessageItem>
|
||||
>
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 === i && (
|
||||
<DebugContent
|
||||
parameters={buildInputList(message)}
|
||||
message={message}
|
||||
ok={handleOk(message)}
|
||||
isNext={false}
|
||||
btnText={'Submit'}
|
||||
></DebugContent>
|
||||
)}
|
||||
{message.role === MessageType.Assistant &&
|
||||
derivedMessages.length - 1 !== i && (
|
||||
<div>
|
||||
<div>{message?.data?.tips}</div>
|
||||
|
||||
<div>
|
||||
{buildInputList(message)?.map((item) => item.value)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
@ -182,15 +210,15 @@ const ChatContainer = () => {
|
||||
<NextMessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
disabled={hasError || isWaitting}
|
||||
sendDisabled={sendDisabled || isWaitting}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
onUpload={handleUploadFile}
|
||||
isUploading={loading}
|
||||
isUploading={loading || isWaitting}
|
||||
></NextMessageInput>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -246,7 +246,7 @@
|
||||
@layer utilities {
|
||||
.scrollbar-auto {
|
||||
/* hide scrollbar */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-width: none;
|
||||
scrollbar-color: transparent transparent;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user