From 01bf799a5942c8fe6b07036aa50f5d5c3221c622 Mon Sep 17 00:00:00 2001
From: chanx <1243304602@qq.com>
Date: Fri, 1 Aug 2025 18:32:38 +0800
Subject: [PATCH] Fix: Fixed share-log UI issues and log-template bugs (#9166)
### What problem does this PR solve?
Fix: Fixed share-log UI issues and log-template bugs #3221
### Type of change
- [x] Bug Fix (non-breaking change which fixes an issue)
---
.../components/next-message-item/index.less | 1 +
.../components/next-message-item/index.tsx | 136 ++++++++++++------
web/src/components/ui/button.tsx | 1 +
web/src/hooks/use-send-message.ts | 1 +
web/src/locales/en.ts | 23 +++
web/src/locales/zh.ts | 22 +++
.../agent/log-sheet/toolTimelineItem.tsx | 45 ++++--
.../agent/log-sheet/workFlowTimeline.tsx | 91 ++++++++++--
.../pages/agents/agent-log-detail-modal.tsx | 26 ++--
web/src/pages/agents/agent-log-page.tsx | 8 +-
web/src/pages/agents/agent-templates.tsx | 22 +--
web/src/pages/next-chats/share/index.tsx | 9 +-
web/tailwind.css | 35 +++++
13 files changed, 331 insertions(+), 89 deletions(-)
diff --git a/web/src/components/next-message-item/index.less b/web/src/components/next-message-item/index.less
index 448c7e7f1..17cb36165 100644
--- a/web/src/components/next-message-item/index.less
+++ b/web/src/components/next-message-item/index.less
@@ -9,6 +9,7 @@
.messageItemContent {
display: inline-flex;
gap: 20px;
+ width: 100%;
}
.messageItemContentReverse {
flex-direction: row-reverse;
diff --git a/web/src/components/next-message-item/index.tsx b/web/src/components/next-message-item/index.tsx
index 5a7c38380..6aeca0c4f 100644
--- a/web/src/components/next-message-item/index.tsx
+++ b/web/src/components/next-message-item/index.tsx
@@ -14,16 +14,18 @@ import {
} from 'react';
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
-import { INodeEvent } from '@/hooks/use-send-message';
+import { INodeEvent, MessageEventType } from '@/hooks/use-send-message';
import { cn } from '@/lib/utils';
import { AgentChatContext } from '@/pages/agent/context';
import { WorkFlowTimeline } from '@/pages/agent/log-sheet/workFlowTimeline';
import { IMessage } from '@/pages/chat/interface';
import { isEmpty } from 'lodash';
+import { Atom, ChevronDown, ChevronUp } from 'lucide-react';
import IndentedTreeModal from '../indented-tree/modal';
import MarkdownContent from '../next-markdown-content';
import { RAGFlowAvatar } from '../ragflow-avatar';
import { useTheme } from '../theme-provider';
+import { Button } from '../ui/button';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';
import { ReferenceDocumentList } from './reference-document-list';
@@ -50,6 +52,7 @@ interface IProps
showLikeButton?: boolean;
showLoudspeaker?: boolean;
showLog?: boolean;
+ isShare?: boolean;
}
function MessageItem({
@@ -71,13 +74,14 @@ function MessageItem({
visibleAvatar = true,
children,
showLog,
+ isShare,
}: IProps) {
const { theme } = useTheme();
const isAssistant = item.role === MessageType.Assistant;
const isUser = item.role === MessageType.User;
- const { visible, hideModal, showModal } = useSetModalState();
- const [clickedDocumentId, setClickedDocumentId] = useState('');
-
+ const { visible, hideModal } = useSetModalState();
+ const [clickedDocumentId] = useState('');
+ const [showThinking, setShowThinking] = useState(false);
const { setLastSendLoadingFunc } = useContext(AgentChatContext);
useEffect(() => {
@@ -101,6 +105,16 @@ function MessageItem({
setCurrentMessageId(item.id);
}
}, [item.id, setCurrentMessageId]);
+
+ const startedNodeList = useCallback(
+ (item: IMessage) => {
+ const finish = currentEventListWithoutMessageById?.(item.id)?.some(
+ (item) => item.event === MessageEventType.WorkflowFinished,
+ );
+ return !finish && loading;
+ },
+ [currentEventListWithoutMessageById, loading],
+ );
return (
) : avatarDialog || agentName ? (
-
+
) : (
))}
-
- {isAssistant ? (
-
- ) : (
-
+
+ {isShare && isAssistant && (
+
)}
+
+ {isAssistant ? (
+ <>
+ {isShare && !sendLoading && !isEmpty(item.content) && (
+
+ )}
+ {!isShare && (
+
+ )}
+ >
+ ) : (
+
+ )}
- {/*
{isAssistant ? '' : nickname} */}
+ {/*
{isAssistant ? '' : nickname} */}
+
+
+ {isAssistant &&
+ currentEventListWithoutMessageById &&
+ showThinking && (
+
+
+
+ )}
{!isShare && 'running...'}>
) : (
)}
- {isAssistant && currentEventListWithoutMessageById && (
-
-
-
- )}
+
{isUser && (
)}
diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx
index c8790d0a0..44eeb6458 100644
--- a/web/src/components/ui/button.tsx
+++ b/web/src/components/ui/button.tsx
@@ -23,6 +23,7 @@ const buttonVariants = cva(
'bg-colors-background-sentiment-solid-primary text-colors-text-persist-light hover:bg-colors-background-sentiment-solid-primary/80',
icon: 'bg-colors-background-inverse-standard text-foreground hover:bg-colors-background-inverse-standard/80',
dashed: 'border border-dashed border-input hover:bg-accent',
+ transparent: 'bg-transparent hover:bg-accent border',
},
size: {
default: 'h-8 px-2.5 py-1.5 ',
diff --git a/web/src/hooks/use-send-message.ts b/web/src/hooks/use-send-message.ts
index 3be9eab32..ecf961fdd 100644
--- a/web/src/hooks/use-send-message.ts
+++ b/web/src/hooks/use-send-message.ts
@@ -36,6 +36,7 @@ export interface INodeData {
error: null | string;
elapsed_time: number;
created_at: number;
+ thoughts: string;
}
export interface IInputData {
diff --git a/web/src/locales/en.ts b/web/src/locales/en.ts
index 79c299a6a..10469b125 100644
--- a/web/src/locales/en.ts
+++ b/web/src/locales/en.ts
@@ -1317,6 +1317,29 @@ This delimiter is used to split the input text into several text pieces echo of
file: 'File upload',
integer: 'Number',
boolean: 'Boolean',
+
+ logTimeline: {
+ begin: 'Ready to begin',
+ agent: 'Agent is thinking',
+ retrieval: 'Looking up knowledge',
+ message: 'Agent says',
+ awaitResponse: 'Waiting for you',
+ switch: 'Choosing the best path',
+ iteration: 'Batch processing',
+ categorize: 'Categorising info',
+ code: 'Running a quick script',
+ textProcessing: 'Tidying up text',
+ tavilySearch: 'Searching the web',
+ tavilyExtract: 'Reading the page',
+ exeSQL: 'Querying database',
+ google: 'Searching the web',
+ wikipedia: 'Searching Wikipedia',
+ googleScholar: 'Academic search',
+ gitHub: 'Searching GitHub',
+ email: 'Sending email',
+ httpRequest: 'Calling an API',
+ wenCai: 'Querying financial data',
+ },
goto: 'Fail Branch',
comment: 'Default Value',
},
diff --git a/web/src/locales/zh.ts b/web/src/locales/zh.ts
index c6f43fe3e..dc91b1e44 100644
--- a/web/src/locales/zh.ts
+++ b/web/src/locales/zh.ts
@@ -1261,6 +1261,28 @@ General:实体和关系提取提示来自 GitHub - microsoft/graphrag:基于
import: '导入',
export: '导出',
subject: '主题',
+ logTimeline: {
+ begin: '准备开始',
+ agent: '智能体正在思考',
+ retrieval: '查找知识',
+ message: '回复',
+ awaitResponse: '等你输入',
+ switch: '选择最佳路线',
+ iteration: '批量处理',
+ categorize: '信息归类',
+ code: '运行小段代码',
+ textProcessing: '整理文字',
+ tavilySearch: '正在网上搜索',
+ tavilyExtract: '读取网页内容',
+ exeSQL: '查询数据库',
+ google: '正在网上搜索',
+ wikipedia: '搜索维基百科',
+ googleScholar: '学术检索',
+ gitHub: '搜索',
+ email: '发送邮件',
+ httpRequest: '请求接口',
+ wenCai: '查询财务数据',
+ },
},
footer: {
profile: 'All rights reserved @ React',
diff --git a/web/src/pages/agent/log-sheet/toolTimelineItem.tsx b/web/src/pages/agent/log-sheet/toolTimelineItem.tsx
index 66ff3cc8d..dac7c0817 100644
--- a/web/src/pages/agent/log-sheet/toolTimelineItem.tsx
+++ b/web/src/pages/agent/log-sheet/toolTimelineItem.tsx
@@ -14,14 +14,20 @@ import {
import { cn } from '@/lib/utils';
import { Operator } from '../constant';
import OperatorIcon from '../operator-icon';
-import { JsonViewer } from './workFlowTimeline';
+import {
+ JsonViewer,
+ toLowerCaseStringAndDeleteChar,
+ typeMap,
+} from './workFlowTimeline';
const ToolTimelineItem = ({
tools,
sendLoading = false,
+ isShare = false,
}: {
tools: Record[];
sendLoading: boolean;
+ isShare?: boolean;
}) => {
if (!tools || tools.length === 0 || !Array.isArray(tools)) return null;
const blackList = ['add_memory', 'gen_citations'];
@@ -110,10 +116,23 @@ const ToolTimelineItem = ({
-
- {parentName(tool.path) + ' '}
- {capitalizeWords(tool.tool_name, '_')}
-
+ {!isShare && (
+
+ {parentName(tool.path) + ' '}
+ {capitalizeWords(tool.tool_name, '_')}
+
+ )}
+ {isShare && (
+
+ {
+ typeMap[
+ toLowerCaseStringAndDeleteChar(
+ tool.tool_name,
+ ) as keyof typeof typeMap
+ ]
+ }
+
+ )}
{/* 0:00
{x.data.elapsed_time?.toString().slice(0, 6)} */}
@@ -129,10 +148,18 @@ const ToolTimelineItem = ({
-
+ {!isShare && (
+
+ )}
+ {isShare && (
+
+ )}
diff --git a/web/src/pages/agent/log-sheet/workFlowTimeline.tsx b/web/src/pages/agent/log-sheet/workFlowTimeline.tsx
index 008ae553e..84aa19d5d 100644
--- a/web/src/pages/agent/log-sheet/workFlowTimeline.tsx
+++ b/web/src/pages/agent/log-sheet/workFlowTimeline.tsx
@@ -1,3 +1,4 @@
+import HightLightMarkdown from '@/components/highlight-markdown';
import {
Timeline,
TimelineContent,
@@ -31,7 +32,11 @@ import ToolTimelineItem from './toolTimelineItem';
type LogFlowTimelineProps = Pick<
ReturnType,
'currentEventListWithoutMessage' | 'currentMessageId'
-> & { canvasId?: string; sendLoading: boolean };
+> & {
+ canvasId?: string;
+ sendLoading: boolean;
+ isShare?: boolean;
+};
export function JsonViewer({
data,
title,
@@ -46,12 +51,41 @@ export function JsonViewer({
src={data}
displaySize
collapseStringsAfterLength={100000000000}
- className="w-full h-[200px] break-words overflow-auto p-2 bg-background-card"
+ className="w-full h-[200px] break-words overflow-auto scrollbar-auto p-2 bg-slate-800"
/>
);
}
-
+export const typeMap = {
+ begin: t('flow.logTimeline.begin'),
+ agent: t('flow.logTimeline.agent'),
+ retrieval: t('flow.logTimeline.retrieval'),
+ message: t('flow.logTimeline.message'),
+ awaitResponse: t('flow.logTimeline.awaitResponse'),
+ switch: t('flow.logTimeline.switch'),
+ iteration: t('flow.logTimeline.iteration'),
+ categorize: t('flow.logTimeline.categorize'),
+ code: t('flow.logTimeline.code'),
+ textProcessing: t('flow.logTimeline.textProcessing'),
+ tavilySearch: t('flow.logTimeline.tavilySearch'),
+ tavilyExtract: t('flow.logTimeline.tavilyExtract'),
+ exeSQL: t('flow.logTimeline.exeSQL'),
+ google: t('flow.logTimeline.google'),
+ duckDuckGo: t('flow.logTimeline.google'),
+ wikipedia: t('flow.logTimeline.wikipedia'),
+ googleScholar: t('flow.logTimeline.googleScholar'),
+ arXiv: t('flow.logTimeline.googleScholar'),
+ pubMed: t('flow.logTimeline.googleScholar'),
+ gitHub: t('flow.logTimeline.gitHub'),
+ email: t('flow.logTimeline.email'),
+ httpRequest: t('flow.logTimeline.httpRequest'),
+ wenCai: t('flow.logTimeline.wenCai'),
+ yahooFinance: t('flow.logTimeline.yahooFinance'),
+};
+export const toLowerCaseStringAndDeleteChar = (
+ str: string,
+ char: string = '_',
+) => str.toLowerCase().replace(/ /g, '').replaceAll(char, '');
function getInputsOrOutputs(
nodeEventList: INodeData[],
field: 'inputs' | 'outputs',
@@ -69,6 +103,7 @@ export const WorkFlowTimeline = ({
currentMessageId,
canvasId,
sendLoading,
+ isShare,
}: LogFlowTimelineProps) => {
// const getNode = useGraphStore((state) => state.getNode);
const [isStopFetchTrace, setISStopFetchTrace] = useState(false);
@@ -146,6 +181,7 @@ export const WorkFlowTimeline = ({
},
[currentEventListWithoutMessage],
);
+
return (
{startedNodeList?.map((x, idx) => {
@@ -213,7 +249,15 @@ export const WorkFlowTimeline = ({
- {getNodeName(x.data?.component_name)}
+
+ {!isShare && getNodeName(x.data?.component_name)}
+ {isShare &&
+ typeMap[
+ toLowerCaseStringAndDeleteChar(
+ nodeLabel,
+ ) as keyof typeof typeMap
+ ]}
+
{x.data.elapsed_time?.toString().slice(0, 6)}
@@ -228,16 +272,36 @@ export const WorkFlowTimeline = ({
-
-
-
+ {!isShare && (
+
+
+ {!isShare && (
+ <>
+
-
-
-
+
+ >
+ )}
+
+
+ )}
+ {isShare && x.data?.thoughts && (
+
+
+
+
+ {x.data.thoughts || ''}
+
+
+
+
+ )}
@@ -248,6 +312,7 @@ export const WorkFlowTimeline = ({
key={'tool_' + idx}
tools={filterTrace(x.data.component_id)}
sendLoading={sendLoading}
+ isShare={isShare}
>
)}
>
diff --git a/web/src/pages/agents/agent-log-detail-modal.tsx b/web/src/pages/agents/agent-log-detail-modal.tsx
index fd184f736..1d8a32eb0 100644
--- a/web/src/pages/agents/agent-log-detail-modal.tsx
+++ b/web/src/pages/agents/agent-log-detail-modal.tsx
@@ -25,21 +25,25 @@ export const AgentLogDetailModal: React.FC = ({
const { data: canvasInfo } = useFetchAgent();
const shortMessage = useMemo(() => {
- const content = derivedMessages[0]?.content || '';
+ if (derivedMessages?.length) {
+ const content = derivedMessages[0]?.content || '';
- const chineseCharCount = (content.match(/[\u4e00-\u9fa5]/g) || []).length;
- const totalLength = content.length;
+ const chineseCharCount = (content.match(/[\u4e00-\u9fa5]/g) || []).length;
+ const totalLength = content.length;
- if (chineseCharCount > 0) {
- if (totalLength > 15) {
- return content.substring(0, 15) + '...';
+ if (chineseCharCount > 0) {
+ if (totalLength > 15) {
+ return content.substring(0, 15) + '...';
+ }
+ } else {
+ if (totalLength > 30) {
+ return content.substring(0, 30) + '...';
+ }
}
+ return content;
} else {
- if (totalLength > 30) {
- return content.substring(0, 30) + '...';
- }
+ return '';
}
- return content;
}, [derivedMessages]);
return (
@@ -52,7 +56,7 @@ export const AgentLogDetailModal: React.FC = ({
className="!w-[900px]"
>
-
+
{derivedMessages?.map((message, i) => {
return (
{
setPagination((pre) => {
return {
...pre,
- current: current ?? pre.current,
+ current: current ?? pre.pageSize ? 1 : pre.current,
pageSize: pageSize ?? pre.pageSize,
};
});
@@ -196,8 +196,10 @@ const AgentLogPage: React.FC = () => {
const [openModal, setOpenModal] = useState(false);
const [modalData, setModalData] = useState();
const showLogDetail = (item: IAgentLogResponse) => {
- setModalData(item);
- setOpenModal(true);
+ if (item?.round) {
+ setModalData(item);
+ setOpenModal(true);
+ }
};
return (
diff --git a/web/src/pages/agents/agent-templates.tsx b/web/src/pages/agents/agent-templates.tsx
index 954feb258..e6d914bd1 100644
--- a/web/src/pages/agents/agent-templates.tsx
+++ b/web/src/pages/agents/agent-templates.tsx
@@ -11,7 +11,7 @@ import { useSetModalState } from '@/hooks/common-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useFetchAgentTemplates, useSetAgent } from '@/hooks/use-agent-request';
import { IFlowTemplate } from '@/interfaces/database/flow';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { CreateAgentDialog } from './create-agent-dialog';
import { TemplateCard } from './template-card';
@@ -70,15 +70,19 @@ export default function AgentTemplates() {
],
);
const handleSiderBarChange = (keyword: string) => {
- const tempList = list.filter(
- (item, index) =>
- item.canvas_type
- ?.toLocaleLowerCase()
- .includes(keyword?.toLocaleLowerCase()) || index === 0,
- );
- setTemplateList(tempList);
setSelectMenuItem(keyword);
};
+
+ const tempListFilter = useMemo(() => {
+ if (!selectMenuItem) {
+ return templateList;
+ }
+ return templateList.filter(
+ (item, index) =>
+ item.canvas_type?.toLocaleLowerCase() ===
+ selectMenuItem?.toLocaleLowerCase() || index === 0,
+ );
+ }, [selectMenuItem, templateList]);
return (
@@ -104,7 +108,7 @@ export default function AgentTemplates() {
- {templateList?.map((x, index) => {
+ {tempListFilter?.map((x, index) => {
return (
{
const {
sharedId: conversationId,
- from,
locale,
visibleAvatar,
} = useGetSharedChatSearchParams();
@@ -61,7 +60,6 @@ const ChatContainer = () => {
resetSession,
} = useSendNextSharedMessage(addEventList);
- // const { data } = useFetchExternalAgentInputs();
const sendDisabled = useSendButtonDisabled(value);
const appConf = useFetchAppConf();
const { data: inputsData } = useFetchExternalAgentInputs();
@@ -95,7 +93,7 @@ const ChatContainer = () => {
}, [inputsData, setAgentInfo]);
React.useEffect(() => {
- if (!isEmpty(inputsData)) {
+ if (inputsData && inputsData.inputs && !isEmpty(inputsData.inputs)) {
showParameterDialog();
}
}, [inputsData, showParameterDialog]);
@@ -141,7 +139,9 @@ const ChatContainer = () => {
{derivedMessages?.map((message, i) => {
@@ -162,6 +162,7 @@ const ChatContainer = () => {
sendLoading &&
derivedMessages?.length - 1 === i
}
+ isShare={true}
avatarDialog={agentInfo.avatar}
agentName={agentInfo.title}
index={i}
diff --git a/web/tailwind.css b/web/tailwind.css
index c5e765e05..a488b7766 100644
--- a/web/tailwind.css
+++ b/web/tailwind.css
@@ -239,3 +239,38 @@
max-width: none;
}
}
+
+@layer utilities {
+ .scrollbar-auto {
+ /* hide scrollbar */
+ scrollbar-width: thin;
+ scrollbar-color: transparent transparent;
+ }
+
+ .scrollbar-auto::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ .scrollbar-auto::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ .scrollbar-auto::-webkit-scrollbar-thumb {
+ background-color: transparent;
+ border-radius: 3px;
+ transition: background-color 0.2s ease;
+ }
+
+ .scrollbar-auto:hover::-webkit-scrollbar-thumb,
+ .scrollbar-auto:focus::-webkit-scrollbar-thumb,
+ .scrollbar-auto:active::-webkit-scrollbar-thumb {
+ background-color: rgba(0, 0, 0, 0.3);
+ }
+
+ .dark .scrollbar-auto:hover::-webkit-scrollbar-thumb,
+ .dark .scrollbar-auto:focus::-webkit-scrollbar-thumb,
+ .dark .scrollbar-auto:active::-webkit-scrollbar-thumb {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+}