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:
chanx
2025-08-04 18:06:56 +08:00
committed by GitHub
parent 53618d13bb
commit 5f5c6a7990
12 changed files with 171 additions and 75 deletions

View File

@ -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>
);

View File

@ -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',

View File

@ -1265,6 +1265,7 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
subject: '主题',
logTimeline: {
begin: '准备开始',
userFillUp: '等你输入',
agent: '智能体正在思考',
retrieval: '查找知识',
message: '回复',

View File

@ -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]">

View File

@ -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

View 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 };

View File

@ -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>
);

View File

@ -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>

View File

@ -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,

View File

@ -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

View File

@ -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>

View File

@ -246,7 +246,7 @@
@layer utilities {
.scrollbar-auto {
/* hide scrollbar */
scrollbar-width: thin;
scrollbar-width: none;
scrollbar-color: transparent transparent;
}