/* * @Author: ZhaoYing * @Date: 2026-02-03 17:09:03 * @Last Modified by: ZhaoYing * @Last Modified time: 2026-04-20 16:59:25 */ /** * Memory Conversation Page * Interactive conversation interface with memory analysis * Supports deep thinking, normal reply, and quick reply modes */ import { type FC, type ReactNode, useState } from 'react' import { useTranslation } from 'react-i18next' import { Col, Row, App, Skeleton, Segmented, Tooltip, Flex, Image } from 'antd' import dayjs from 'dayjs' import type { AnyObject } from 'antd/es/_util/type'; import ConversationEmptyIcon from '@/assets/images/conversation/conversationEmpty.svg' import AnalysisEmptyIcon from '@/assets/images/conversation/analysisEmpty.png' import { readService, userMemoryListUrl } from '@/api/memory' import Empty from '@/components/Empty' import DebounceSelect from '@/components/DebounceSelect' import Markdown from '@/components/Markdown' import type { Data } from '@/views/UserMemory/types' import type { DefaultOptionType } from 'antd/es/select' import Chat from '@/components/Chat' import type { ChatItem } from '@/components/Chat/types' import RbCard from '@/components/RbCard/Card'; import styles from './index.module.css' import ResultCard from '@/components/RbCard/ResultCard' import AudioPlayer from '@/views/UserMemoryDetail/components/AudioPlayer' import VideoPlayer from '@/views/UserMemoryDetail/components/VideoPlayer' /** Search mode configuration */ const searchSwitchList = [ { icon:
, value: '0', key: 'deepThinking' }, { icon:
, value: '1', key: 'normalReply' }, { icon:
, value: '2', key: 'quickReply' }, ] /** * Test parameters for conversation API */ export interface TestParams { /** End user identifier */ end_user_id: string; /** User message content */ message: string; /** Search mode switch (0: deep thinking, 1: normal, 2: quick) */ search_switch: string; /** Conversation history */ history: { role: string; content: string }[]; /** Enable web keyword */ web_search?: boolean; /** Enable memory function */ memory?: boolean; /** Conversation ID */ conversation_id?: string; } /** * Data item in analysis logs */ interface DataItem { id: string; question: string; type: string; reason?: string; } /** * Log item for conversation analysis */ export interface LogItem { type: string; title: string; data?: DataItem[] | AnyObject; raw_results?: string | Record; raw_result?: Array; summary?: string; query?: string; reason?: string; result?: string; original_query?: string; index?: number; result_count?: number; total?: number; } /** * Content wrapper component for analysis items */ const ContentWrapper: FC<{ children: ReactNode }> = ({ children }) => (
{children}
) const MemoryConversation: FC = () => { const { t } = useTranslation() const { message } = App.useApp(); const [userId, setUserId] = useState() const [loading, setLoading] = useState(false) const [chatData, setChatData] = useState([]) const [logs, setLogs] = useState([]) const [search_switch, setSearchSwitch] = useState('0') const [msg, setMsg] = useState('') const [expandedLogs, setExpandedLogs] = useState>({}) /** Handle message send */ const handleSend = () => { if(!userId) { message.warning(t('common.inputPlaceholder', { title: t('memoryConversation.userID') })) return } setChatData(prev => [...prev, { content: msg, created_at: new Date().getTime(), role: 'user' }]) setLoading(true) setExpandedLogs({}) readService({ message: msg, end_user_id: userId, search_switch: search_switch, history: [], }) .then(res => { const response = res as { answer: string; intermediate_outputs: LogItem[] } setChatData(prev => [...prev, { content: response.answer || '-', created_at: new Date().getTime(), role: 'assistant' }]) setLogs(response.intermediate_outputs) setExpandedLogs(Object.fromEntries(response.intermediate_outputs.map((_, i) => [i, true]))) }) .finally(() => { setLoading(false) }) } /** Handle keyword mode change */ const handleChange = (value: string) => { setSearchSwitch(value) } const handleDownload = (file_path?: string) => { if (!file_path) return window.open(file_path, '_blank') } return ( <> (items as Data[]).map(item => ({ ...item, 'end_user.id': item.end_user?.id, label: item.end_user?.other_name || item.end_user?.id, value: item.end_user?.id, }))} placeholder={t('memoryConversation.searchPlaceholder')} style={{ width: '100%', marginBottom: '16px' }} onChange={(opt: DefaultOptionType) => setUserId(opt?.value as string)} variant="borderless" className="rb:bg-white rb:rounded-lg" showSearch /> } className="rb:pt-0!" contentClassName='rb:h-[calc(100%-144px)]' data={chatData} onChange={setMsg} onSend={handleSend} loading={loading} labelFormat={(item) => dayjs(item.created_at).locale('en').format('MMMM D, YYYY [at] h:mm A')} > ({ ...item, icon: {item.icon} }))} shape="round" className={styles.segmented} onChange={handleChange} /> {loading ? : !logs || logs.length === 0 ? : {logs.map((log, logIndex) => ( setExpandedLogs(prev => ({ ...prev, [logIndex]: !prev[logIndex] }))} extra={log.type === 'verification' &&
{log.result}
} > {log.type === 'problem_split' && Array.isArray(log.data) && log.data.length > 0 ? {log.data.map(vo => ( <>
{vo.id}. {vo.question}
))}
: log.type === 'problem_extension' && log.data && Object.keys(log.data).length > 0 ? {Object.keys(log.data).map((key: string) => ( <>
{key}
{(log.data as Record)[key].map((item, index) => (
{item}
))}
))}
: log.type === 'search_result' && log.result ? : log.type === 'retrieval_summary' && log.summary ?
{log.summary}
: log.type === 'verification' ?
{log.query}
{log.reason}
{log.result}
: log.type === 'output_type' ?
{log.query}
{log.summary}
: log.type === 'input_summary' && log.raw_results ?
{log.query}
{log.summary}
{typeof log.raw_results === 'string' ? : <> {log.raw_results.reranked_results?.statements.length > 0 && log.raw_results.reranked_results?.statements.map((item: { statement: string; } , index: number) => (
{item.statement}
))} {log.raw_results.reranked_results?.summaries.length > 0 && log.raw_results.reranked_results?.summaries.map((item: { content: string; }, index: number) => (
{item.content}
))} }
: log.type === 'perceptual_retrieve' && log.data && log.data?.length > 0 ? {log.data.map((vo: any) => ( {vo.file_path ? <> {/(jpg|jpeg|png|gif|webp|svg)$/i.test(vo.file_type) ? {vo.file_name} : /(mp4|webm|ogg|mov)$/i.test(vo.file_type) ? : /(mp3|wav|ogg|m4a|aac)$/i.test(vo.file_type) ? :
{vo.file_name}
-
handleDownload(vo.file_path)} >
} :
} {['summary', 'keywords', 'topic', 'domain', 'scene', 'speaker_count', 'section_count'].map(key => { const value = vo[key] if (value) { return (
{t(`perceptualDetail.${key}`)}
{typeof value === 'string' ?
{value}
: Array.isArray(value) ? {value.map((vo, index) =>
{vo}
)}
: '-' }
) } return null })}
))}
: null }
))}
}
) } export default MemoryConversation