Browse Source

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)
tags/v0.20.1
chanx 2 months ago
parent
commit
5f5c6a7990
No account linked to committer's email address

+ 7
- 2
web/src/components/ui/accordion.tsx View File

function AccordionTrigger({ function AccordionTrigger({
className, className,
children, children,
hideDownIcon = false,
...props ...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
}: React.ComponentProps<typeof AccordionPrimitive.Trigger> & {
hideDownIcon?: boolean;
}) {
return ( return (
<AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger
{...props} {...props}
> >
{children} {children}
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
{!hideDownIcon && (
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
)}
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
); );

+ 1
- 0
web/src/locales/en.ts View File

logTimeline: { logTimeline: {
begin: 'Ready to begin', begin: 'Ready to begin',
agent: 'Agent is thinking', agent: 'Agent is thinking',
userFillUp: 'Waiting for you',
retrieval: 'Looking up knowledge', retrieval: 'Looking up knowledge',
message: 'Agent says', message: 'Agent says',
awaitResponse: 'Waiting for you', awaitResponse: 'Waiting for you',

+ 1
- 0
web/src/locales/zh.ts View File

subject: '主题', subject: '主题',
logTimeline: { logTimeline: {
begin: '准备开始', begin: '准备开始',
userFillUp: '等你输入',
agent: '智能体正在思考', agent: '智能体正在思考',
retrieval: '查找知识', retrieval: '查找知识',
message: '回复', message: '回复',

+ 8
- 41
web/src/pages/agent/chat/box.tsx View File

useUploadCanvasFileWithProgress, useUploadCanvasFileWithProgress,
} from '@/hooks/use-agent-request'; } from '@/hooks/use-agent-request';
import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
import { Message } from '@/interfaces/database/chat';
import { buildMessageUuidWithRole } from '@/utils/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 { useParams } from 'umi';
import DebugContent from '../debug-content'; import DebugContent from '../debug-content';
import { BeginQuery } from '../interface';
import { buildBeginQueryWithObject } from '../utils';
import { useAwaitCompentData } from '../hooks/use-chat-logic';


function AgentChatBox() { function AgentChatBox() {
const { const {
const { data: canvasInfo } = useFetchAgent(); const { data: canvasInfo } = useFetchAgent();
const { id: canvasId } = useParams(); const { id: canvasId } = useParams();
const { uploadCanvasFile, loading } = useUploadCanvasFileWithProgress(); 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,
};
});
},
[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 { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
derivedMessages,
sendFormMessage,
canvasId: canvasId as string,
});


const handleUploadFile: NonNullable<FileUploadProps['onUpload']> = const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
useCallback( useCallback(
}, },
[appendUploadResponseList, uploadCanvasFile], [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 ( return (
<> <>
<section className="flex flex-1 flex-col px-5 h-[90vh]"> <section className="flex flex-1 flex-col px-5 h-[90vh]">

+ 2
- 2
web/src/pages/agent/chat/use-send-agent-message.ts View File



const sendFormMessage = useCallback( const sendFormMessage = useCallback(
(body: { id?: string; inputs: Record<string, BeginQuery> }) => { (body: { id?: string; inputs: Record<string, BeginQuery> }) => {
send(body);
send({ ...body, session_id: sessionId });
addNewestOneQuestion({ addNewestOneQuestion({
content: Object.entries(body.inputs) content: Object.entries(body.inputs)
.map(([key, val]) => `${key}: ${val.value}`) .map(([key, val]) => `${key}: ${val.value}`)
role: MessageType.User, role: MessageType.User,
}); });
}, },
[addNewestOneQuestion, send],
[addNewestOneQuestion, send, sessionId],
); );


// reset session // reset session

+ 60
- 0
web/src/pages/agent/hooks/use-chat-logic.ts View File

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

+ 38
- 17
web/src/pages/agent/log-sheet/toolTimelineItem.tsx View File

import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { Operator } from '../constant'; import { Operator } from '../constant';
import OperatorIcon from '../operator-icon';
import OperatorIcon, { SVGIconMap } from '../operator-icon';
import { import {
JsonViewer, JsonViewer,
toLowerCaseStringAndDeleteChar, toLowerCaseStringAndDeleteChar,
typeMap, typeMap,
} from './workFlowTimeline'; } 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) => {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
})
.join(' ');
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();
});
return resultStrArr;
}; };
const changeToolName = (toolName: any) => { const changeToolName = (toolName: any) => {
const name = 'Agent ' + capitalizeWords(toolName);
const name = 'Agent ' + capitalizeWords(toolName).join(' ');
return name; return name;
}; };
const ToolTimelineItem = ({ const ToolTimelineItem = ({
return ( return (
<> <>
{filteredTools?.map((tool, idx) => { {filteredTools?.map((tool, idx) => {
const toolName = capitalizeWords(tool.tool_name, '_').join('');

return ( return (
<TimelineItem <TimelineItem
key={'tool_' + idx} key={'tool_' + idx}
<div className="size-6 flex items-center justify-center"> <div className="size-6 flex items-center justify-center">
<OperatorIcon <OperatorIcon
className="size-4" className="size-4"
name={'Agent' as Operator}
name={
(SVGIconMap[toolName as IToolIcon]
? toolName
: 'Agent') as Operator
}
></OperatorIcon> ></OperatorIcon>
</div> </div>
</div> </div>
className="bg-background-card px-3" className="bg-background-card px-3"
> >
<AccordionItem value={idx.toString()}> <AccordionItem value={idx.toString()}>
<AccordionTrigger>
<AccordionTrigger
hideDownIcon={isShare && isEmpty(tool.arguments)}
>
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
{!isShare && ( {!isShare && (
<span> <span>
{parentName(tool.path) + ' '} {parentName(tool.path) + ' '}
{capitalizeWords(tool.tool_name, '_')}
{capitalizeWords(tool.tool_name, '_').join(' ')}
</span> </span>
)} )}
{isShare && ( {isShare && (
</span> </span>
<span <span
className={cn( 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> <span className="sr-only">Online</span>
)} )}
{isShare && !isEmpty(tool.arguments) && ( {isShare && !isEmpty(tool.arguments) && (
<AccordionContent> <AccordionContent>
<div className="space-y-2">
<div className="space-y-2 bg-muted p-2">
{tool && {tool &&
tool.arguments && tool.arguments &&
Object.entries(tool.arguments).length && Object.entries(tool.arguments).length &&
<div className="text-sm font-medium leading-none"> <div className="text-sm font-medium leading-none">
{key} {key}
</div> </div>
<div className="text-sm text-muted-foreground">
{val || ''}
<div className="text-sm text-muted-foreground mt-1">
{val as string}
</div> </div>
</div> </div>
); );

+ 18
- 5
web/src/pages/agent/log-sheet/workFlowTimeline.tsx View File

src={data} src={data}
displaySize displaySize
collapseStringsAfterLength={100000000000} 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> </section>
); );
httpRequest: t('flow.logTimeline.httpRequest'), httpRequest: t('flow.logTimeline.httpRequest'),
wenCai: t('flow.logTimeline.wenCai'), wenCai: t('flow.logTimeline.wenCai'),
yahooFinance: t('flow.logTimeline.yahooFinance'), yahooFinance: t('flow.logTimeline.yahooFinance'),
userFillUp: t('flow.logTimeline.userFillUp'),
}; };
export const toLowerCaseStringAndDeleteChar = ( export const toLowerCaseStringAndDeleteChar = (
str: string, str: string,
char: string = '_', char: string = '_',
) => str.toLowerCase().replace(/ /g, '').replaceAll(char, ''); ) => 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( function getInputsOrOutputs(
nodeEventList: INodeData[], nodeEventList: INodeData[],
field: 'inputs' | 'outputs', field: 'inputs' | 'outputs',
className="bg-background-card px-3" className="bg-background-card px-3"
> >
<AccordionItem value={idx.toString()}> <AccordionItem value={idx.toString()}>
<AccordionTrigger>
<AccordionTrigger
hideDownIcon={isShare && !x.data?.thoughts}
>
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<span> <span>
{!isShare && getNodeName(x.data?.component_name)} {!isShare && getNodeName(x.data?.component_name)}
{isShare && {isShare &&
typeMap[
(typeMapLowerCase[
toLowerCaseStringAndDeleteChar( toLowerCaseStringAndDeleteChar(
nodeLabel, nodeLabel,
) as keyof typeof typeMap ) as keyof typeof typeMap
]}
] ??
nodeLabel)}
</span> </span>
<span className="text-text-sub-title text-xs"> <span className="text-text-sub-title text-xs">
{x.data.elapsed_time?.toString().slice(0, 6)} {x.data.elapsed_time?.toString().slice(0, 6)}
{isShare && x.data?.thoughts && ( {isShare && x.data?.thoughts && (
<AccordionContent> <AccordionContent>
<div className="space-y-2"> <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> <HightLightMarkdown>
{x.data.thoughts || ''} {x.data.thoughts || ''}
</HightLightMarkdown> </HightLightMarkdown>

+ 1
- 1
web/src/pages/agent/operator-icon.tsx View File

[Operator.Email]: 'sendemail-0', [Operator.Email]: 'sendemail-0',
}; };


const SVGIconMap = {
export const SVGIconMap = {
[Operator.ArXiv]: ArxivIcon, [Operator.ArXiv]: ArxivIcon,
[Operator.GitHub]: GithubIcon, [Operator.GitHub]: GithubIcon,
[Operator.Bing]: BingIcon, [Operator.Bing]: BingIcon,

+ 1
- 1
web/src/pages/agents/agent-log-page.tsx View File

<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<h1 className="text-2xl font-bold mb-4">Log</h1> <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"> <div className="flex items-center space-x-2">
<span>ID/Title</span> <span>ID/Title</span>
<SearchInput <SearchInput

+ 33
- 5
web/src/pages/next-chats/share/index.tsx View File

} from '@/hooks/use-agent-request'; } from '@/hooks/use-agent-request';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import i18n from '@/locales/config'; import i18n from '@/locales/config';
import DebugContent from '@/pages/agent/debug-content';
import { useCacheChatLog } from '@/pages/agent/hooks/use-cache-chat-log'; 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 { IInputs } from '@/pages/agent/interface';
import { useSendButtonDisabled } from '@/pages/chat/hooks'; import { useSendButtonDisabled } from '@/pages/chat/hooks';
import { buildMessageUuidWithRole } from '@/utils/chat'; import { buildMessageUuidWithRole } from '@/utils/chat';
appendUploadResponseList, appendUploadResponseList,
parameterDialogVisible, parameterDialogVisible,
showParameterDialog, showParameterDialog,
sendFormMessage,
ok, ok,
resetSession, resetSession,
} = useSendNextSharedMessage(addEventList); } = useSendNextSharedMessage(addEventList);

const { buildInputList, handleOk, isWaitting } = useAwaitCompentData({
derivedMessages,
sendFormMessage,
canvasId: conversationId as string,
});
const sendDisabled = useSendButtonDisabled(value); const sendDisabled = useSendButtonDisabled(value);
const appConf = useFetchAppConf(); const appConf = useFetchAppConf();
const { data: inputsData } = useFetchExternalAgentInputs(); const { data: inputsData } = useFetchExternalAgentInputs();
showLoudspeaker={false} showLoudspeaker={false}
showLog={false} showLog={false}
sendLoading={sendLoading} 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> </div>
<NextMessageInput <NextMessageInput
isShared isShared
value={value} value={value}
disabled={hasError}
sendDisabled={sendDisabled}
disabled={hasError || isWaitting}
sendDisabled={sendDisabled || isWaitting}
conversationId={conversationId} conversationId={conversationId}
onInputChange={handleInputChange} onInputChange={handleInputChange}
onPressEnter={handlePressEnter} onPressEnter={handlePressEnter}
sendLoading={sendLoading} sendLoading={sendLoading}
stopOutputMessage={stopOutputMessage} stopOutputMessage={stopOutputMessage}
onUpload={handleUploadFile} onUpload={handleUploadFile}
isUploading={loading}
isUploading={loading || isWaitting}
></NextMessageInput> ></NextMessageInput>
</div> </div>
</div> </div>

+ 1
- 1
web/tailwind.css View File

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



Loading…
Cancel
Save