fix(web): user memory
This commit is contained in:
@@ -2246,6 +2246,7 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
context_details: 'Preference Details',
|
||||
supporting_evidence: 'Preference Source',
|
||||
specific_examples: 'Source',
|
||||
wordEmpty: 'Click on a node in the left chart to view preference details'
|
||||
},
|
||||
shortTermDetail: {
|
||||
title: 'Short-term memory is the "workbench" of the AI system, connecting instant conversations with long-term knowledge bases. Through real-time capture, deep retrieval, intelligent extraction and filtering transformation, temporary unstructured information is converted into valuable long-term knowledge.',
|
||||
|
||||
@@ -2345,6 +2345,7 @@ export const zh = {
|
||||
context_details: '偏好详情',
|
||||
supporting_evidence: '偏好来源',
|
||||
specific_examples: '来源',
|
||||
wordEmpty: '点击左侧图表中的节点查看偏好详情'
|
||||
},
|
||||
shortTermDetail: {
|
||||
title: '短期记忆是AI系统的"工作台",连接即时对话与长期知识库。通过实时捕获、深度检索、智能提取和筛选转化,将临时的非结构化信息转化为有价值的长期知识。',
|
||||
|
||||
198
web/src/utils/event.md
Normal file
198
web/src/utils/event.md
Normal file
File diff suppressed because one or more lines are too long
@@ -23,10 +23,20 @@ export function parseSSEToJSON(sseString: string) {
|
||||
currentEvent.event = line.substring(6).trim()
|
||||
} else if (line.startsWith('data:')) {
|
||||
const dataStr = line.substring(5).trim()
|
||||
try {
|
||||
currentEvent.data = JSON.parse(dataStr.replace(/"/g, '"'))
|
||||
} catch {
|
||||
currentEvent.data = dataStr
|
||||
if (dataStr) {
|
||||
try {
|
||||
// 尝试解析为 JSON
|
||||
currentEvent.data = JSON.parse(dataStr)
|
||||
} catch {
|
||||
// JSON 解析失败时,检查是否是被转义的 JSON 字符串
|
||||
try {
|
||||
const unescaped = dataStr.replace(/"/g, '"').replace(/&/g, '&')
|
||||
currentEvent.data = JSON.parse(unescaped)
|
||||
} catch {
|
||||
// 如果仍然失败,保存为原始字符串
|
||||
currentEvent.data = dataStr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ const ExplicitDetailModal = forwardRef<ExplicitDetailModalRef>((_props, ref) =>
|
||||
onCancel={handleClose}
|
||||
>
|
||||
{loading ? <Skeleton active />
|
||||
: <Descriptions column={data.memory_type === 'semantic' ? 1 : 2} classNames={{ label: 'rb:w-20' }}>
|
||||
: <Descriptions column={1} classNames={{ label: 'rb:w-20' }}>
|
||||
{data.emotion && <Descriptions.Item label={t('explicitDetail.emotion')}>
|
||||
<div className="rb:flex rb:items-center rb:gap-2">
|
||||
<div className="rb:w-3 rb:h-3 rb:rounded-full" style={{ backgroundColor: getEmotionColor(data.emotion) }}></div>
|
||||
@@ -88,7 +88,7 @@ const ExplicitDetailModal = forwardRef<ExplicitDetailModalRef>((_props, ref) =>
|
||||
{data.created_at && <Descriptions.Item label={t('explicitDetail.created_at')}>
|
||||
{formatDateTime(data.created_at)}
|
||||
</Descriptions.Item>}
|
||||
{data.content && <Descriptions.Item span="filled" label={t('explicitDetail.content')}>
|
||||
{data.content && <Descriptions.Item label={t('explicitDetail.content')}>
|
||||
{data.content}
|
||||
</Descriptions.Item>}
|
||||
</Descriptions>
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'echarts-wordcloud'
|
||||
import Empty from '@/components/Empty'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import { getImplicitPreferences } from '@/api/memory'
|
||||
import detailEmpty from '@/assets/images/userMemory/detail_empty.png'
|
||||
|
||||
interface PreferenceItem {
|
||||
tag_name: string;
|
||||
@@ -164,7 +165,12 @@ const Preferences: FC = () => {
|
||||
bodyClassName='rb:p-3! rb:h-[326px]'
|
||||
>
|
||||
{selectedWord === null
|
||||
? <Empty size={88} className="rb:h-full!" />
|
||||
? <Empty
|
||||
url={detailEmpty}
|
||||
subTitle={t('implicitDetail.wordEmpty')}
|
||||
className="rb:h-full rb:mx-10 rb:text-center"
|
||||
size={[197.81, 150]}
|
||||
/>
|
||||
: <>
|
||||
<div className="rb:leading-5 rb:mb-1 rb:font-medium">{t('implicitDetail.context_details')}</div>
|
||||
<div className="rb:text-[#5B6167] rb:leading-5 rb:font-regular">{data[selectedWord].context_details}</div>
|
||||
|
||||
@@ -39,6 +39,7 @@ const TAG_COLORS: Record<string, "processing" | "success" | "warning" | "error"
|
||||
learning: "warning",
|
||||
decision: "warning",
|
||||
important_event: "error",
|
||||
default: 'default'
|
||||
}
|
||||
const BG_COLORS: Record<string, string> = {
|
||||
conversation: "rb:bg-[#155EEF]",
|
||||
@@ -46,10 +47,12 @@ const BG_COLORS: Record<string, string> = {
|
||||
learning: "rb:bg-[#FF5D34]",
|
||||
decision: "rb:bg-[#FF5D34]",
|
||||
important_event: "rb:bg-[#5B6167]",
|
||||
default: 'rb:bg-[#F0F3F8] rb:text-[#5B6167]!'
|
||||
}
|
||||
|
||||
// Map display types to internal keys
|
||||
const getTypeKey = (type: string): string => {
|
||||
if (!type) return 'default'
|
||||
const typeMap: Record<string, string> = {
|
||||
'Learning': 'learning',
|
||||
'Project/Work': 'project_work',
|
||||
@@ -176,6 +179,7 @@ const EpisodicDetail: FC = () => {
|
||||
<RbCard
|
||||
title={<>{t('episodicDetail.curResult')}<span className="rb:text-[#5B6167] rb:font-regular!"> ({data.total || 0}{t('episodicDetail.unix')})</span></>}
|
||||
headerType="borderless"
|
||||
bodyClassName="rb:h-[calc(100vh-349px)] rb:overflow-y-auto"
|
||||
>
|
||||
{loading
|
||||
? <Skeleton active />
|
||||
@@ -192,9 +196,12 @@ const EpisodicDetail: FC = () => {
|
||||
})}
|
||||
onClick={() => setSelected(vo)}
|
||||
>
|
||||
<div className={clsx("rb:bg-[#369F21] rb:rounded-lg rb:text-[#FFFFFF] rb:size-6 rb:text-[12px] rb:leading-6 rb:text-center rb:mr-3", BG_COLORS[getTypeKey(vo.type)])}>{index + 1}</div>
|
||||
<div className="rb:flex-1">
|
||||
<div className="rb:flex rb:items-center rb:justify-between">{vo.title} <Tag color={TAG_COLORS[getTypeKey(vo.type)]}>{t(`episodicDetail.${getTypeKey(vo.type)}`)}</Tag></div>
|
||||
<div className={clsx("rb:rounded-lg rb:text-[#FFFFFF] rb:size-6 rb:text-[12px] rb:leading-6 rb:text-center rb:mr-3", BG_COLORS[getTypeKey(vo.type)])}>{index + 1}</div>
|
||||
<div className="rb:flex-1 rb:w-[calc(100%-36px)]">
|
||||
<div className="rb:flex rb:items-center rb:justify-between">
|
||||
<div className="rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap rb:flex-1">{vo.title}</div>
|
||||
{vo.type && <Tag color={TAG_COLORS[getTypeKey(vo.type)]}>{t(`episodicDetail.${getTypeKey(vo.type)}`)}</Tag>}
|
||||
</div>
|
||||
<div className="rb:text-[#5B6167] rb:text-[12px]">{formatDateTime(vo.created_at)}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -202,13 +209,13 @@ const EpisodicDetail: FC = () => {
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
||||
</RbCard>
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<RbCard
|
||||
title={selected?.title}
|
||||
headerType="borderless"
|
||||
bodyClassName="rb:h-[calc(100vh-349px)] rb:overflow-y-auto"
|
||||
>
|
||||
{detailLoading
|
||||
? <Skeleton active />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type FC, useEffect, useState, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { List, Skeleton, Row, Col } from 'antd'
|
||||
import { Skeleton, Row, Col } from 'antd'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import {
|
||||
getExplicitMemory,
|
||||
@@ -65,7 +65,7 @@ const ExplicitDetail: FC = () => {
|
||||
{loading ?
|
||||
<Skeleton active />
|
||||
: data.episodic_memories?.length > 0 ? (
|
||||
<Row gutter={16}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{data.episodic_memories.map(item => (
|
||||
<Col key={item.id} span={6}>
|
||||
<RbCard
|
||||
@@ -85,7 +85,7 @@ const ExplicitDetail: FC = () => {
|
||||
{loading ?
|
||||
<Skeleton active />
|
||||
: data.semantic_memories?.length > 0 ? (
|
||||
<Row gutter={16}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{data.semantic_memories.map(item => (
|
||||
<Col key={item.id} span={6}>
|
||||
<RbCard
|
||||
|
||||
@@ -113,7 +113,7 @@ const WorkingDetail: FC = () => {
|
||||
<div className={clsx("rb:p-[8px_13px] rb:rounded-lg rb:leading-5 rb:cursor-pointer rb:hover:bg-[#F0F3F8]", {
|
||||
'rb:bg-[#FFFFFF] rb:shadow-[0px_2px_4px_0px_rgba(0,0,0,0.15)] rb:font-medium rb:hover:bg-[#FFFFFF]!': item.id === selected?.id,
|
||||
})}
|
||||
onClick={() => getDetail(item.id)}
|
||||
onClick={() => setSelected(item)}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
|
||||
@@ -62,10 +62,10 @@ const Detail: FC = () => {
|
||||
{type === 'FORGETTING_MANAGEMENT' && <ForgetDetail />}
|
||||
{type === 'IMPLICIT_MEMORY' && <ImplicitDetail />}
|
||||
{type === 'SHORT_TERM_MEMORY' && <ShortTermDetail />}
|
||||
{type === 'PERCEPTUAL_MEMORY' && <PerceptualDetail />} {/** TODO */}
|
||||
{type === 'PERCEPTUAL_MEMORY' && <PerceptualDetail />}
|
||||
{type === 'EPISODIC_MEMORY' && <EpisodicDetail />}
|
||||
{type === 'WORKING_MEMORY' && <WorkingDetail />} {/** TODO */}
|
||||
{type === 'EXPLICIT_MEMORY' && <ExplicitDetail />} {/** TODO */}
|
||||
{type === 'WORKING_MEMORY' && <WorkingDetail />}
|
||||
{type === 'EXPLICIT_MEMORY' && <ExplicitDetail />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user