Merge pull request #997 from SuanmoSuanyangTechnology/feature/memory_ui_zy
Feature/memory UI zy
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
import { type FC, useRef } from 'react';
|
import { type FC, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { Flex, Button } from 'antd';
|
import { Flex, Button, Form } from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
|
|
||||||
import { getAppLogsUrl } from '@/api/application';
|
import { getAppLogsUrl } from '@/api/application';
|
||||||
@@ -15,11 +15,14 @@ import Table from '@/components/Table'
|
|||||||
import { formatDateTime } from '@/utils/format';
|
import { formatDateTime } from '@/utils/format';
|
||||||
import type { LogItem, LogDetailModalRef } from './types'
|
import type { LogItem, LogDetailModalRef } from './types'
|
||||||
import LogDetailModal from './components/LogDetailModal'
|
import LogDetailModal from './components/LogDetailModal'
|
||||||
|
import SearchInput from '@/components/SearchInput'
|
||||||
|
|
||||||
const Statistics: FC = () => {
|
const Statistics: FC = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const logDetailRef = useRef<LogDetailModalRef>(null);
|
const logDetailRef = useRef<LogDetailModalRef>(null);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const values = Form.useWatch([], form);
|
||||||
|
|
||||||
const handleViewDetail = (item: LogItem) => {
|
const handleViewDetail = (item: LogItem) => {
|
||||||
logDetailRef.current?.handleOpen(item);
|
logDetailRef.current?.handleOpen(item);
|
||||||
@@ -62,15 +65,26 @@ const Statistics: FC = () => {
|
|||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
<div className="rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
|
<div className="rb:bg-white rb:rounded-lg rb:pt-3 rb:px-3">
|
||||||
|
<Flex justify="flex-end" className="rb:mb-3!">
|
||||||
|
<Form form={form}>
|
||||||
|
<Form.Item name="keyword" noStyle>
|
||||||
|
<SearchInput
|
||||||
|
placeholder={t('application.logSearchPlaceholder')}
|
||||||
|
variant="outlined"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Flex>
|
||||||
<Table<LogItem>
|
<Table<LogItem>
|
||||||
apiUrl={getAppLogsUrl(id || '')}
|
apiUrl={getAppLogsUrl(id || '')}
|
||||||
apiParams={{
|
apiParams={{
|
||||||
is_draft: false,
|
is_draft: false,
|
||||||
|
...(values ?? {})
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
isScroll={true}
|
isScroll={true}
|
||||||
scrollY="calc(100vh - 214px)"
|
scrollY="calc(100vh - 242px)"
|
||||||
/>
|
/>
|
||||||
<LogDetailModal ref={logDetailRef} />
|
<LogDetailModal ref={logDetailRef} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-13 17:27:52
|
* @Date: 2026-03-13 17:27:52
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-07 21:48:30
|
* @Last Modified time: 2026-04-24 18:14:25
|
||||||
*/
|
*/
|
||||||
import { type FC, useState, useRef, useEffect } from 'react'
|
import { type FC, useState, useRef, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@@ -59,6 +59,7 @@ interface NodeData {
|
|||||||
node_type?: string;
|
node_type?: string;
|
||||||
input?: any;
|
input?: any;
|
||||||
output?: any;
|
output?: any;
|
||||||
|
process?: any;
|
||||||
elapsed_time?: string;
|
elapsed_time?: string;
|
||||||
error?: any;
|
error?: any;
|
||||||
state: Record<string, any>;
|
state: Record<string, any>;
|
||||||
@@ -485,7 +486,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateWorkflowNodeEndMessage = (data: NodeData) => {
|
const updateWorkflowNodeEndMessage = (data: NodeData) => {
|
||||||
const { node_id, input, output, error, elapsed_time, status } = data;
|
const { node_id, input, output, process, error, elapsed_time, status } = data;
|
||||||
setChatList(prev => {
|
setChatList(prev => {
|
||||||
const newList = [...prev]
|
const newList = [...prev]
|
||||||
const lastIndex = newList.length - 1
|
const lastIndex = newList.length - 1
|
||||||
@@ -498,6 +499,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
content: {
|
content: {
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
|
process,
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
status: status || 'completed',
|
status: status || 'completed',
|
||||||
@@ -514,7 +516,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateWorkflowCycleMessage = (data: NodeData) => {
|
const updateWorkflowCycleMessage = (data: NodeData) => {
|
||||||
const { node_id, cycle_id, cycle_idx, input, output, error, elapsed_time, status } = data;
|
const { node_id, cycle_id, cycle_idx, input, output, process, error, elapsed_time, status } = data;
|
||||||
const { nodes } = config as WorkflowConfig
|
const { nodes } = config as WorkflowConfig
|
||||||
const node = nodes.find(n => n.id === node_id);
|
const node = nodes.find(n => n.id === node_id);
|
||||||
const { name, type } = node || {}
|
const { name, type } = node || {}
|
||||||
@@ -538,6 +540,7 @@ const TestChat: FC<TestChatProps> = ({
|
|||||||
cycle_idx,
|
cycle_idx,
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
|
process,
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
status: status || 'completed',
|
status: status || 'completed',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-03-24 16:31:24
|
* @Date: 2026-03-24 16:31:24
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-03-24 16:31:24
|
* @Last Modified time: 2026-04-24 17:49:58
|
||||||
*/
|
*/
|
||||||
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
import { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
|
||||||
import { Flex, Button, Empty, Skeleton } from 'antd';
|
import { Flex, Button, Empty, Skeleton } from 'antd';
|
||||||
@@ -14,6 +14,12 @@ import { getAppLogDetail } from '@/api/application'
|
|||||||
import ChatContent from '@/components/Chat/ChatContent'
|
import ChatContent from '@/components/Chat/ChatContent'
|
||||||
import { formatDateTime } from '@/utils/format'
|
import { formatDateTime } from '@/utils/format'
|
||||||
import type { ChatItem } from '@/components/Chat/types'
|
import type { ChatItem } from '@/components/Chat/types'
|
||||||
|
import Runtime from '@/views/Workflow/components/Chat/Runtime'
|
||||||
|
import { nodeLibrary } from '@/views/Workflow/constant'
|
||||||
|
|
||||||
|
const nodeIconMap = Object.fromEntries(
|
||||||
|
nodeLibrary.flatMap(c => c.nodes.map(n => [n.type, n.icon]))
|
||||||
|
)
|
||||||
|
|
||||||
/** Log detail data with conversation messages */
|
/** Log detail data with conversation messages */
|
||||||
type Data = LogItem & {
|
type Data = LogItem & {
|
||||||
@@ -54,7 +60,30 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
|
|||||||
if (!vo) return
|
if (!vo) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
getAppLogDetail(vo.app_id, vo.id).then(res => {
|
getAppLogDetail(vo.app_id, vo.id).then(res => {
|
||||||
setData(res as Data)
|
const { node_executions_map, messages, ...rest } = res as Data;
|
||||||
|
let hasSubContentMessages = messages
|
||||||
|
if (messages && messages.length > 0 && node_executions_map && Object.keys(node_executions_map).length > 0) {
|
||||||
|
hasSubContentMessages = messages.map(item => {
|
||||||
|
if (item.id && node_executions_map[item.id]) {
|
||||||
|
item.subContent = node_executions_map[item.id]?.map(({ input, output, cycle_items = [], error, process, ...node }: any) => {
|
||||||
|
const converted: any = { ...node, icon: nodeIconMap[node.node_type], content: { input, output, process, error } }
|
||||||
|
if (node.node_type === 'loop' && Array.isArray(cycle_items) && cycle_items.length > 0) {
|
||||||
|
converted.subContent = cycle_items.map(({ input: cInput, output: cOutput, error: cError, process: cProcess, ...cNode }: any) => ({
|
||||||
|
...cNode,
|
||||||
|
icon: nodeIconMap[cNode.node_type],
|
||||||
|
content: { input: cInput, output: cOutput, process: cProcess, error: cError }
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return converted
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return { ...item }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setData({
|
||||||
|
...rest,
|
||||||
|
messages: hasSubContentMessages
|
||||||
|
})
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -66,6 +95,8 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
|
|||||||
handleClose
|
handleClose
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
console.log('data', data)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RbModal
|
<RbModal
|
||||||
title={<>
|
title={<>
|
||||||
@@ -92,6 +123,7 @@ const LogDetailModal = forwardRef<LogDetailModalRef>((_props, ref) => {
|
|||||||
data={data.messages || []}
|
data={data.messages || []}
|
||||||
streamLoading={false}
|
streamLoading={false}
|
||||||
labelFormat={(item) => formatDateTime(item.created_at)}
|
labelFormat={(item) => formatDateTime(item.created_at)}
|
||||||
|
renderRuntime={(item, index) => <Runtime item={item} index={index} />}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-06 21:10:56
|
* @Date: 2026-02-06 21:10:56
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-21 14:59:13
|
* @Last Modified time: 2026-04-24 18:13:22
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Workflow Chat Component
|
* Workflow Chat Component
|
||||||
@@ -66,7 +66,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
const [fileList, setFileList] = useState<any[]>([])
|
const [fileList, setFileList] = useState<any[]>([])
|
||||||
const [message, setMessage] = useState<string | undefined>(undefined)
|
const [message, setMessage] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
console.log('abortRef', abortRef)
|
console.log('abortRef', abortRef, chatList)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the chat drawer and loads workflow variables from the start node
|
* Opens the chat drawer and loads workflow variables from the start node
|
||||||
@@ -185,7 +185,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
*/
|
*/
|
||||||
const handleStreamMessage = (data: SSEMessage[]) => {
|
const handleStreamMessage = (data: SSEMessage[]) => {
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
const { content, conversation_id, node_id, cycle_id, cycle_idx, input, output, error, elapsed_time, status, citations } = item.data as {
|
const { content, conversation_id, node_id, cycle_id, cycle_idx, input, output, process, error, elapsed_time, status, citations } = item.data as {
|
||||||
content: string;
|
content: string;
|
||||||
conversation_id: string | null;
|
conversation_id: string | null;
|
||||||
cycle_id: string;
|
cycle_id: string;
|
||||||
@@ -193,6 +193,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
node_id: string;
|
node_id: string;
|
||||||
node_name?: string;
|
node_name?: string;
|
||||||
node_type?: string;
|
node_type?: string;
|
||||||
|
process?: any;
|
||||||
input?: any;
|
input?: any;
|
||||||
output?: any;
|
output?: any;
|
||||||
elapsed_time?: string;
|
elapsed_time?: string;
|
||||||
@@ -277,6 +278,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
content: {
|
content: {
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
|
process,
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
status: status || 'completed',
|
status: status || 'completed',
|
||||||
@@ -305,13 +307,14 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef; data: Work
|
|||||||
cycle_id,
|
cycle_id,
|
||||||
cycle_idx,
|
cycle_idx,
|
||||||
node_id,
|
node_id,
|
||||||
node_name: name,
|
node_name: type === 'cycle-start' ? t('workflow.cycle-start') : name,
|
||||||
node_type: type,
|
node_type: type,
|
||||||
icon,
|
icon,
|
||||||
content: {
|
content: {
|
||||||
cycle_idx,
|
cycle_idx,
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
|
process,
|
||||||
error,
|
error,
|
||||||
},
|
},
|
||||||
status: status || 'completed',
|
status: status || 'completed',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-24 17:57:08
|
* @Date: 2026-02-24 17:57:08
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-20 15:33:48
|
* @Last Modified time: 2026-04-24 18:04:31
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Runtime Component
|
* Runtime Component
|
||||||
@@ -184,27 +184,30 @@ const Runtime: FC<{ item: ChatItem; index: number;}> = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{/* Display input and output data as JSON code blocks */}
|
{/* Display input and output data as JSON code blocks */}
|
||||||
{['input', 'output'].map(key => (
|
{['input', 'process', 'output'].map(key => {
|
||||||
<div key={key} className="rb:bg-[#EBEBEB] rb:rounded-lg">
|
if (vo.node_type !== 'http-request' && key === 'process') return null
|
||||||
<div className="rb:py-2 rb:px-3 rb:flex rb:justify-between rb:items-center rb:text-[12px]">
|
return (
|
||||||
{isLoop ? t(`workflow.runtime.${key}_cycle_vars`) : t(`workflow.${key}_result`)}
|
<div key={key} className="rb:bg-[#EBEBEB] rb:rounded-lg">
|
||||||
<Button
|
<div className="rb:py-2 rb:px-3 rb:flex rb:justify-between rb:items-center rb:text-[12px]">
|
||||||
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
{isLoop ? t(`workflow.runtime.${key}_cycle_vars`) : t(`workflow.${key}_result`)}
|
||||||
size="small"
|
<Button
|
||||||
onClick={() => handleCopy(typeof vo.content === 'object' && vo.content?.[key] ? JSON.stringify(vo.content[key], null, 2) : '{}')}
|
className="rb:py-0! rb:px-1! rb:text-[12px]!"
|
||||||
>{t('common.copy')}</Button>
|
size="small"
|
||||||
|
onClick={() => handleCopy(typeof vo.content === 'object' && vo.content?.[key] ? JSON.stringify(vo.content[key], null, 2) : '{}')}
|
||||||
|
>{t('common.copy')}</Button>
|
||||||
|
</div>
|
||||||
|
<div className="rb:max-h-40 rb:overflow-auto">
|
||||||
|
<CodeBlock
|
||||||
|
size="small"
|
||||||
|
value={typeof vo.content === 'object' && vo.content?.[key] ? JSON.stringify(vo.content[key], null, 2) : '{}'}
|
||||||
|
needCopy={false}
|
||||||
|
showLineNumbers={true}
|
||||||
|
background="#EBEBEB"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="rb:max-h-40 rb:overflow-auto">
|
)
|
||||||
<CodeBlock
|
})}
|
||||||
size="small"
|
|
||||||
value={typeof vo.content === 'object' && vo.content?.[key] ? JSON.stringify(vo.content[key], null, 2) : '{}'}
|
|
||||||
needCopy={false}
|
|
||||||
showLineNumbers={true}
|
|
||||||
background="#EBEBEB"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}]}
|
}]}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @Author: ZhaoYing
|
* @Author: ZhaoYing
|
||||||
* @Date: 2026-02-03 15:17:48
|
* @Date: 2026-02-03 15:17:48
|
||||||
* @Last Modified by: ZhaoYing
|
* @Last Modified by: ZhaoYing
|
||||||
* @Last Modified time: 2026-04-20 16:00:26
|
* @Last Modified time: 2026-04-24 17:21:09
|
||||||
*/
|
*/
|
||||||
import { Clipboard, Graph, Keyboard, MiniMap, Node, Snapline, History, type Edge } from '@antv/x6';
|
import { Clipboard, Graph, Keyboard, MiniMap, Node, Snapline, History, type Edge } from '@antv/x6';
|
||||||
import type { HistoryCommand as Command } from '@antv/x6/lib/plugin/history/type';
|
import type { HistoryCommand as Command } from '@antv/x6/lib/plugin/history/type';
|
||||||
@@ -1492,7 +1492,7 @@ export const useWorkflowGraph = ({
|
|||||||
// Reset all node execution status first
|
// Reset all node execution status first
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
const data = node.getData();
|
const data = node.getData();
|
||||||
if (typeof data.status === 'string') {
|
if (typeof data.executionStatus === 'string') {
|
||||||
node.setData({ ...data, executionStatus: undefined });
|
node.setData({ ...data, executionStatus: undefined });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user