### 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
| @@ -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} | |||
| <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.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, | |||
| }; | |||
| }); | |||
| }, | |||
| [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']> = | |||
| 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 | |||
| @@ -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) => { | |||
| 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 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; | |||
| } | |||