feat(web): memory manage & memory detail ui upgrade
14
web/src/assets/images/common/arrow_up.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>下拉</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆管理-记忆提取引擎-2" transform="translate(-806, -166)">
|
||||
<g id="文本" transform="translate(252, 154)">
|
||||
<g id="下拉" transform="translate(562, 20) scale(1, -1) translate(-562, -20)translate(554, 12)">
|
||||
<rect id="矩形" opacity="0.236839658" fill-rule="nonzero" x="0" y="0" width="16" height="16"></rect>
|
||||
<polyline id="路径" stroke="#5B6167" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="12 6 8 10 4 6"></polyline>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 920 B |
20
web/src/assets/images/common/check_green.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>完成</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆管理-记忆提取引擎-2" transform="translate(-1288, -508)" fill-rule="nonzero">
|
||||
<g id="编组-26" transform="translate(846, 102)">
|
||||
<g id="编组-48" transform="translate(12, 362)">
|
||||
<g id="1" transform="translate(0, 32)">
|
||||
<g id="编组-31" transform="translate(422, 8)">
|
||||
<g id="完成" transform="translate(8, 4)">
|
||||
<rect id="矩形" opacity="0.205054874" x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M8,1 C4.13401843,1 1,4.13399462 1,8 C1,11.8660054 4.13401843,15 8,15 C11.8660404,15 15,11.8660054 15,8 C15,4.13400935 11.8660697,1 8,1 L8,1 Z M11.8488905,6.3221368 L7.34274977,10.9336514 C7.13545231,11.145815 6.79915605,11.145815 6.59188798,10.9336514 L4.15112422,8.43567652 C3.9436945,8.22349821 3.9436945,7.87942608 4.15112422,7.66718889 C4.35840699,7.45493698 4.69471795,7.45493698 4.90198602,7.66718889 L6.96733357,9.78080221 L11.0978817,5.55359028 C11.3053114,5.34135309 11.6416371,5.34135309 11.8489052,5.55359028 C12.0563349,5.76581275 12.0563349,6.10989961 11.8489052,6.3221368 L11.8488905,6.3221368 Z" id="形状" fill="#369F21"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
19
web/src/assets/images/common/save.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>保存</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
|
||||
<g id="记忆管理-记忆提取引擎" transform="translate(-1269, -122)" stroke="#171719">
|
||||
<g id="编组-26" transform="translate(846, 102)">
|
||||
<g id="副操作" transform="translate(414, 11)">
|
||||
<g id="保存" transform="translate(9, 9)">
|
||||
<g id="编组-12" transform="translate(1.925, 1.75)">
|
||||
<path d="M1.5,0 L7.33055996,0 L7.33055996,0 L10.15,2.91453026 L10.15,9 C10.15,9.82842712 9.47842712,10.5 8.65,10.5 L1.5,10.5 C0.671572875,10.5 0,9.82842712 0,9 L0,1.5 C0,0.671572875 0.671572875,0 1.5,0 Z" id="矩形"></path>
|
||||
<polyline id="路径-12" stroke-linecap="round" points="2.45 0 2.45 3.5 6.825 3.5"></polyline>
|
||||
<rect id="矩形" stroke-linecap="round" x="2.45" y="6.125" width="5.25" height="4.375"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
BIN
web/src/assets/images/empty/noData.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
18
web/src/assets/images/memory/clock_orange.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>时间戳</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆管理-记忆提取引擎" transform="translate(-1303, -376)" fill-rule="nonzero">
|
||||
<g id="编组-26" transform="translate(846, 102)">
|
||||
<g id="1" transform="translate(12, 262)">
|
||||
<g id="编组-31" transform="translate(437, 8)">
|
||||
<g id="时间戳" transform="translate(8, 4)">
|
||||
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
|
||||
<path d="M8.33332812,1.5 C11.9233281,1.5 14.8333281,4.41 14.8333281,8 C14.8333281,11.59 11.9233281,14.5 8.33332812,14.5 C4.74332813,14.5 1.83332812,11.59 1.83332812,8 C1.83332812,4.41 4.74332813,1.5 8.33332812,1.5 Z M8.33332812,2.5 C5.29567188,2.5 2.83332812,4.96234375 2.83332812,8 C2.83332812,11.0376562 5.29567188,13.5 8.33332812,13.5 C11.3709844,13.5 13.8333281,11.0376562 13.8333281,8 C13.8333281,4.96234375 11.371,2.5 8.33332812,2.5 Z M8.83332812,4.322 L8.83332812,7.775 L11.3833281,10.0336719 L10.7206719,10.782 L7.83332812,8.22532813 L7.83332812,4.322 L8.83332812,4.322 Z" id="形状" fill="#FF5D34"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
15
web/src/assets/images/memory/debug.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>配置管理</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆管理-记忆提取引擎" transform="translate(-1344, -122)" fill="#FFFFFF" fill-rule="nonzero" stroke="#FFFFFF" stroke-width="0.3">
|
||||
<g id="编组-26" transform="translate(846, 102)">
|
||||
<g id="副操作备份" transform="translate(490, 11)">
|
||||
<g id="配置管理" transform="translate(8, 9)">
|
||||
<path d="M7.42879146,7.0538149 L7.42879146,1.42856378 C7.42879146,1.19188803 7.23488102,1 6.99819188,1 C6.76150275,1 6.56757891,1.19188803 6.56757891,1.42856378 L6.56757891,7.0546319 C5.83214429,7.24550203 5.28435198,7.91628644 5.28435198,8.71456315 C5.28435198,9.51404527 5.83108621,10.1856467 6.57135586,10.3753516 C6.56898461,10.3931451 6.56772319,10.4110689 6.56757891,10.4290192 L6.56757891,12.5718916 C6.56757891,12.8085807 6.76150275,13.0004822 6.99817849,13.0004822 C7.23485423,13.0004822 7.42879146,12.8085807 7.42879146,12.5718916 L7.42879146,10.4290325 C7.42845218,10.411363 7.42702981,10.393731 7.42453235,10.3762355 C8.16639582,10.1875486 8.71563466,9.51502299 8.71563466,8.7146703 C8.71578195,7.91518818 8.16904773,7.24362695 7.42879146,7.0538283 L7.42879146,7.0538149 Z M7.60587973,9.32065717 C7.31671051,9.609836 6.8635355,9.65447605 6.52350043,9.42727705 C6.18346536,9.20007806 6.05127565,8.76431872 6.20777383,8.38649396 C6.36427201,8.0086692 6.76587074,7.79400949 7.1669665,7.87379309 C7.56806226,7.95357669 7.85694345,8.30558254 7.85694345,8.71453636 C7.85758337,8.9420223 7.76717752,9.16030816 7.60587973,9.32072414 L7.60587973,9.32065717 Z M3.13814456,2.76797625 C3.14055877,2.75020331 3.14186492,2.73229739 3.14205545,2.71436225 L3.14205545,1.42859056 C3.14205545,1.19210844 2.95034881,1.0004018 2.71386669,1.0004018 C2.47738457,1.0004018 2.28567793,1.19210844 2.28567793,1.42859056 L2.28567793,2.71436225 C2.28534183,2.73252066 2.28614702,2.75068225 2.28808875,2.76873968 C1.54783249,2.95840439 1,3.62996562 1,4.4295415 C1,5.22911737 1.54792625,5.90038395 2.28818251,6.09028975 C2.28629436,6.10780474 2.28547584,6.12541856 2.28573151,6.14303317 L2.28573151,12.5719184 C2.28573151,12.8084005 2.47743814,13.0001071 2.71392026,13.0001071 C2.95040239,13.0001071 3.14210902,12.8084005 3.14210902,12.5719184 L3.14210902,6.14305996 C3.14191505,6.12566525 3.14067166,6.1082981 3.13838564,6.09105317 C3.88019554,5.90235278 4.42884504,5.22982722 4.42884504,4.4295281 C4.42884504,3.62922898 3.88004822,2.9565561 3.13814456,2.76796286 L3.13814456,2.76797625 Z M3.31996071,5.03568909 C3.03078814,5.32485849 2.57761807,5.36949099 2.23758857,5.14229141 C1.89755906,4.91509182 1.76537191,4.47933761 1.92186671,4.10151611 C2.07836152,3.7236946 2.47995285,3.50903305 2.88104582,3.58880862 C3.28213879,3.6685842 3.57103772,4.02057858 3.57103772,4.4295281 C3.571667,4.65700012 3.48126198,4.87526953 3.31997411,5.0356757 L3.31996071,5.03568909 Z M13,4.4295415 C13,3.62894772 12.4502255,2.95656949 11.7081745,2.76797625 C11.710058,2.75017021 11.7108363,2.73226456 11.7105049,2.71436225 L11.7105049,1.42859056 C11.7105049,1.19210844 11.5187983,1.0004018 11.2823162,1.0004018 C11.0458341,1.0004018 10.8541274,1.19210844 10.8541274,1.42859056 L10.8541274,2.71436225 C10.8543217,2.73255453 10.8556637,2.75071642 10.8581455,2.76873968 C10.1178758,2.95840439 9.57080673,3.62996562 9.57080673,4.4295415 C9.57080673,5.22911737 10.1175276,5.90062503 10.8577972,6.09032993 C10.8554298,6.1080924 10.8541684,6.12598481 10.8540203,6.14390375 L10.8540203,12.5719184 C10.8540203,12.8084005 11.0457269,13.0001071 11.282209,13.0001071 C11.5186911,13.0001071 11.7103978,12.8084005 11.7103978,12.5719184 L11.7103978,6.14389035 C11.7107305,6.12626252 11.709988,6.1086308 11.7081745,6.09109335 C12.450279,5.90245993 13,5.2300817 13,4.4295281 L13,4.4295415 Z M11.8917452,5.03563552 C11.6025705,5.32476645 11.1494259,5.36936451 10.8094336,5.14215559 C10.4694413,4.91494666 10.3372926,4.47921076 10.4938036,4.10142382 C10.6503147,3.72363689 11.0518954,3.50901645 11.4529577,3.58881444 C11.85402,3.66861244 12.1428487,4.02060432 12.1428487,4.4295281 C12.1434533,4.65698737 12.0530346,4.87523566 11.8917452,5.03562213 L11.8917452,5.03563552 Z" id="形状"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -1,13 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>我</title>
|
||||
<g id="V1.0版" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="红熊空间-记忆库-用户记忆-详情" transform="translate(-272, -458)" stroke="#212332" stroke-width="1.5">
|
||||
<g id="个人信息" transform="translate(256, 80)">
|
||||
<g id="关于我" transform="translate(0, 361)">
|
||||
<g id="我" transform="translate(16, 17)">
|
||||
<circle id="椭圆形" cx="10" cy="6" r="3"></circle>
|
||||
<path d="M10,17 C13.8659932,17 17,16.078564 17,14.5 C17,12.921436 13.8659932,12 10,12 C6.13400675,12 3,12.9530326 3,14.5 C3,16.0469674 6.13400675,17 10,17 Z" id="椭圆形"></path>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆库-个人记忆(RAG)" transform="translate(-268, -153)" stroke="#171719" stroke-width="1.2">
|
||||
<g id="编组-13" transform="translate(252, 64)">
|
||||
<g id="编组-21" transform="translate(12, 88)">
|
||||
<g id="编组-32" transform="translate(4, 0)">
|
||||
<g id="我" transform="translate(0, 1)">
|
||||
<circle id="椭圆形" cx="9" cy="5.4" r="2.7"></circle>
|
||||
<path d="M9,15.3 C12.4793939,15.3 15.3,14.4707076 15.3,13.05 C15.3,11.6292924 12.4793939,10.8 9,10.8 C5.52060608,10.8 2.7,11.6577293 2.7,13.05 C2.7,14.4422707 5.52060608,15.3 9,15.3 Z" id="椭圆形"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 1.1 KiB |
BIN
web/src/assets/images/userMemory/ai.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
18
web/src/assets/images/userMemory/me.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>我</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="记忆库-个人记忆(RAG)" transform="translate(-268, -153)" stroke="#171719" stroke-width="1.2">
|
||||
<g id="编组-13" transform="translate(252, 64)">
|
||||
<g id="编组-21" transform="translate(12, 88)">
|
||||
<g id="编组-32" transform="translate(4, 0)">
|
||||
<g id="我" transform="translate(0, 1)">
|
||||
<circle id="椭圆形" cx="9" cy="5.4" r="2.7"></circle>
|
||||
<path d="M9,15.3 C12.4793939,15.3 15.3,14.4707076 15.3,13.05 C15.3,11.6292924 12.4793939,10.8 9,10.8 C5.52060608,10.8 2.7,11.6577293 2.7,13.05 C2.7,14.4422707 5.52060608,15.3 9,15.3 Z" id="椭圆形"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,12 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>热点洞察</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="个人记忆" transform="translate(-40, -317)" fill="#171719" fill-rule="nonzero">
|
||||
<g id="编组" transform="translate(12, 12)">
|
||||
<g id="编组-11" transform="translate(16, 104)">
|
||||
<g id="热点洞察" transform="translate(12, 201)">
|
||||
<path d="M3.9387755,13.4 C5.06122447,13.4 5.87755102,14.3 5.87755102,15.3 C5.87755102,15.7 5.77551019,16.1 5.57142857,16.4 C6.89795919,18.6 9.44897958,20.1 12.2040816,20.1 C12.8163265,20.1 13.4285714,20 14.0408163,19.9 C14.5510204,19.8 15.0612245,20.1 15.2653061,20.6 C15.3673469,21.1 15.0612245,21.6 14.5510204,21.8 C13.8367347,21.9 13.0204082,22 12.2040816,22 C8.63265306,22 5.46938774,20.1 3.73469387,17.2 C2.7142857,17.1 2,16.3 2,15.3 C2,14.3 2.91836735,13.4 3.9387755,13.4 L3.9387755,13.4 Z M19.3469388,5.9 C21.0816327,7.7 22,9.99999999 22,12.4 C22,14.1 21.5918367,15.7 20.7755102,17.1 C20.9795918,17.4 21.0816327,17.7 21.0816327,18.1 C21.0816327,19.2 20.1632653,20 19.1428572,20 C18.122449,20 17.1020408,19.2 17.1020408,18.2 C17.1020408,17.2 17.9183673,16.4 18.9387755,16.3 L19.0408163,16.3 L19.1428572,16 C19.7551021,14.9 20.0612245,13.7 20.0612245,12.5 C20.0612245,10.5 19.244898,8.7 17.9183674,7.30000001 C17.5102041,6.9 17.6122449,6.3 17.9183674,6 C18.3265306,5.50000001 18.9387755,5.6 19.3469388,5.9 L19.3469388,5.9 Z M12.2040816,8.7 C14.3469388,8.7 16.0816327,10.4 16.0816327,12.5 C16.0816327,14.6 14.3469388,16.3 12.2040816,16.3 C10.0612245,16.3 8.32653061,14.6 8.32653061,12.5 C8.32653061,10.4 10.0612245,8.7 12.2040816,8.7 Z M12.2040816,10.6 C11.0816327,10.6 10.2653061,11.5 10.2653061,12.5 C10.2653061,13.5 11.1836735,14.4 12.2040816,14.4 C13.2244898,14.4 14.1428571,13.5 14.1428571,12.5 C14.1428571,11.5 13.3265306,10.6 12.2040816,10.6 Z M14.1428571,2 C15.2653061,2 16.0816327,2.9 16.0816327,3.90000001 C16.0816327,4.90000001 15.1632653,5.80000001 14.1428571,5.80000001 C13.4285714,5.80000001 12.8163265,5.40000001 12.5102041,4.90000001 L12.2040816,4.90000001 C8.63265306,4.90000001 5.57142857,7.2 4.65306122,10.5 C4.55102039,11 3.9387755,11.3 3.42857142,11.2 C3.02040815,11 2.7142857,10.5 2.81632652,10 C3.9387755,5.90000002 7.81632654,3 12.2040816,3 L12.5102041,3 C12.8163265,2.40000001 13.4285714,2 14.1428571,2 L14.1428571,2 Z" id="形状"></path>
|
||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>编组 26</title>
|
||||
<g id="空间里层页面优化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
|
||||
<g id="记忆库-个人记忆(RAG)" transform="translate(-268, -357)" stroke="#171719" stroke-width="1.2">
|
||||
<g id="编组-13" transform="translate(252, 64)">
|
||||
<g id="编组-31" transform="translate(12, 292)">
|
||||
<g id="编组-26" transform="translate(4, 1)">
|
||||
<g id="编组-24" transform="translate(2, 2)">
|
||||
<g id="编组-23" transform="translate(7, 0)">
|
||||
<path d="M3.80487741,4.75801529 C4.57304648,4.31981911 5.09090909,3.49311348 5.09090909,2.54545455 C5.09090909,1.13963882 3.95127027,0 2.54545455,0 C1.13963882,0 0,1.13963882 0,2.54545455 L0,11.4545455" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M0,11.4545455 C0,12.8603612 1.13963882,14 2.54545455,14 C3.95127027,14 5.09090909,12.8603612 5.09090909,11.4545455" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M5.43716946,6.89920585 C6.34272849,6.61654964 7,5.77139545 7,4.77272727 C7,3.65162269 6.17168669,2.72398105 5.09366357,2.56840585" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M5.08556316,11.8284839 C6.21217524,11.3387175 7,10.2159074 7,8.90909091 C7,8.05692399 6.66499617,7.2830014 6.11950096,6.7118356" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M6.05374225e-07,3.30502525 C0.598221325,3.16656842 1.19644204,3.53131423 1.79466276,4.39926267" id="路径-73"></path>
|
||||
<path d="M0,6.36955959 C0,7.05675687 0.699825901,9.10572809 3.14599655,8.05286405" id="路径-74"></path>
|
||||
</g>
|
||||
<g id="编组-23" transform="translate(3.5, 7) scale(-1, 1) translate(-3.5, -7)">
|
||||
<path d="M3.80487741,4.75801529 C4.57304648,4.31981911 5.09090909,3.49311348 5.09090909,2.54545455 C5.09090909,1.13963882 3.95127027,0 2.54545455,0 C1.13963882,0 0,1.13963882 0,2.54545455 L0,11.4545455" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M0,11.4545455 C0,12.8603612 1.13963882,14 2.54545455,14 C3.95127027,14 5.09090909,12.8603612 5.09090909,11.4545455" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M5.43716946,6.89920585 C6.34272849,6.61654964 7,5.77139545 7,4.77272727 C7,3.65162269 6.17168669,2.72398105 5.09366357,2.56840585" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M5.08556316,11.8284839 C6.21217524,11.3387175 7,10.2159074 7,8.90909091 C7,8.05692399 6.66499617,7.2830014 6.11950096,6.7118356" id="路径" stroke-linejoin="round"></path>
|
||||
<path d="M6.05374225e-07,3.30502525 C0.598221325,3.16656842 1.19644204,3.53131423 1.79466276,4.39926267" id="路径-73"></path>
|
||||
<path d="M0,6.36955959 C0,7.05675687 0.699825901,9.10572809 3.14599655,8.05286405" id="路径-74"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.4 KiB |
BIN
web/src/assets/images/userMemory/user.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
49
web/src/components/BtnTabs/index.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-19 14:05:09
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 14:05:09
|
||||
*/
|
||||
import { type FC } from 'react'
|
||||
import { Flex } from 'antd';
|
||||
import clsx from 'clsx'
|
||||
|
||||
/** A single tab item with a display label and unique key */
|
||||
interface Tab {
|
||||
label: string
|
||||
key: string
|
||||
}
|
||||
|
||||
/** Props for the BtnTabs component */
|
||||
interface BtnTabsProps {
|
||||
/** List of tab items to render */
|
||||
items: Tab[]
|
||||
/** Key of the currently active tab */
|
||||
activeKey: string
|
||||
/** Callback fired when a tab is clicked */
|
||||
onChange: (key: string) => void;
|
||||
/** Optional extra class name for the container */
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/** Button-style tab switcher — renders tabs as pill-shaped buttons with active highlight */
|
||||
const BtnTabs: FC<BtnTabsProps> = ({ items, activeKey, onChange, className }) => {
|
||||
return (
|
||||
<Flex align="center" gap={8} className={className || ''}>
|
||||
{items.map((tab) => (
|
||||
<div
|
||||
key={tab.key}
|
||||
onClick={() => onChange(tab.key)}
|
||||
className={clsx('rb:px-2 rb:py-1 rb:rounded-[13px] rb:text-[12px] rb:leading-4.5 rb:cursor-pointer', {
|
||||
'rb:bg-[#F6F6F6]': activeKey !== tab.key,
|
||||
'rb:bg-[#171719] rb:text-white': activeKey === tab.key,
|
||||
})}
|
||||
>
|
||||
{tab.label}
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export default BtnTabs
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-03-07 16:49:59
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-07 17:14:57
|
||||
* @Last Modified time: 2026-03-18 10:12:23
|
||||
*/
|
||||
import { useEffect, useState, type FC } from 'react';
|
||||
import { Select, Flex, Space } from 'antd';
|
||||
@@ -19,11 +19,13 @@ interface ModelSelectProps extends SelectProps {
|
||||
/** Extra query params passed to getModelList */
|
||||
params?: Query;
|
||||
placeholder?: string;
|
||||
fontClassName?: string;
|
||||
}
|
||||
|
||||
const ModelSelect: FC<ModelSelectProps> = ({
|
||||
params,
|
||||
placeholder,
|
||||
fontClassName,
|
||||
...props
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -48,7 +50,7 @@ const ModelSelect: FC<ModelSelectProps> = ({
|
||||
return (
|
||||
<Flex align="center" gap={8}>
|
||||
{logo && <img src={logo} className="rb:size-5 rb:rounded-md" alt="" />}
|
||||
<div className="rb:flex-1 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap">{item.name}</div>
|
||||
<div className={`rb:flex-1 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap ${fontClassName}`}>{item.name}</div>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1548,7 +1548,7 @@ export const en = {
|
||||
Meaning: 'Meaning',
|
||||
|
||||
exampleMemoryExtractionResults: 'Example Memory Extraction Results',
|
||||
exampleMemoryExtractionResultsSubTitle: '(from a technology conference)',
|
||||
exampleMemoryExtractionResultsSubTitle: 'from a technology conference',
|
||||
|
||||
extractTheNumberOfEntities: 'Extract the number of entities',
|
||||
extractTheNumberOfEntitiesDesc: 'Merge after deduplication: {{num}} (exact: {{exact}}, fuzzy: {{fuzzy}}, LLM: {{llm}})',
|
||||
@@ -1670,7 +1670,12 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
disagreementCase: 'Disagreement Case',
|
||||
Pruned: 'Pruned',
|
||||
pruning: 'Pruning',
|
||||
pruning_desc: 'Text pruning {{count}} fragments'
|
||||
pruning_desc: 'Text pruning {{count}} fragments',
|
||||
|
||||
processData: 'Process Data',
|
||||
finalResult: 'Final Result',
|
||||
chunking: 'Chunking',
|
||||
dataStatistics: 'Data Statistics',
|
||||
},
|
||||
memoryConversation: {
|
||||
searchPlaceholder: 'Enter user ID...',
|
||||
|
||||
@@ -1546,7 +1546,7 @@ export const zh = {
|
||||
Meaning: '含义',
|
||||
|
||||
exampleMemoryExtractionResults: '示例记忆提取结果',
|
||||
exampleMemoryExtractionResultsSubTitle: '(来自技术会议)',
|
||||
exampleMemoryExtractionResultsSubTitle: '来自技术会议',
|
||||
|
||||
extractTheNumberOfEntities: '提取实体数量',
|
||||
extractTheNumberOfEntitiesDesc: '去重后合并:{{num}}(精确:{{exact}},模糊:{{fuzzy}},LLM:{{llm}})',
|
||||
@@ -1666,7 +1666,12 @@ export const zh = {
|
||||
disagreementCase: '不一致案例',
|
||||
Pruned: '已剪枝',
|
||||
pruning: '剪枝',
|
||||
pruning_desc: '文本剪枝{{count}}个片段'
|
||||
pruning_desc: '文本剪枝{{count}}个片段',
|
||||
|
||||
processData: '处理数据',
|
||||
finalResult: '最终结果',
|
||||
chunking: '分块',
|
||||
dataStatistics: '数据统计',
|
||||
},
|
||||
memoryConversation: {
|
||||
chatEmpty:'有什么我可以帮您的吗?',
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:00:20
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-04 10:03:35
|
||||
* @Last Modified time: 2026-03-16 15:43:42
|
||||
*/
|
||||
/**
|
||||
* Line Chart Component
|
||||
@@ -84,7 +84,7 @@ const SeriesConfig = {
|
||||
/**
|
||||
* Chart color palette
|
||||
*/
|
||||
const Colors = ['#155EEF', '#4DA8FF', '#FFB048']
|
||||
const Colors = ['#155EEF', '#4DA8FF', '#369F21']
|
||||
|
||||
|
||||
/**
|
||||
@@ -228,8 +228,8 @@ const LineChart: FC<LineCardProps> = ({ config }) => {
|
||||
grid: {
|
||||
left: 4,
|
||||
right: '2%',
|
||||
bottom: 60,
|
||||
top: 32,
|
||||
bottom: 48,
|
||||
top: 8,
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
@@ -243,7 +243,7 @@ const LineChart: FC<LineCardProps> = ({ config }) => {
|
||||
show: true,
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#5B6167'
|
||||
@@ -268,7 +268,7 @@ const LineChart: FC<LineCardProps> = ({ config }) => {
|
||||
...initialData || []
|
||||
]
|
||||
}}
|
||||
style={{ height: '450px', width: '100%' }}
|
||||
style={{ height: '400px', width: '100%' }}
|
||||
opts={{ renderer: 'canvas' }}
|
||||
notMerge={true}
|
||||
lazyUpdate={true}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:00:12
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:00:12
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 15:41:54
|
||||
*/
|
||||
/**
|
||||
* Forgetting Engine Configuration Page
|
||||
@@ -11,16 +11,17 @@
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Form, Slider, Button, Space, message } from 'antd';
|
||||
import { Row, Col, Form, Button, Space, message, Flex, Tooltip } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import RbCard from '@/components/RbCard/Card';
|
||||
import strategyImpactSimulator from '@/assets/images/memory/strategyImpactSimulator.svg'
|
||||
import LineChart from './components/LineChart'
|
||||
import { getMemoryForgetConfig, updateMemoryForgetConfig } from '@/api/memory'
|
||||
import type { ConfigForm } from './types'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
import RbSlider from '@/components/RbSlider';
|
||||
import DescWrapper from '@/components/FormItem/DescWrapper'
|
||||
|
||||
/**
|
||||
* Configuration field definitions
|
||||
@@ -154,16 +155,18 @@ const ForgettingEngine: React.FC = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={9}>
|
||||
<RbCard
|
||||
title={
|
||||
<div className="rb:flex rb:items-center">
|
||||
<img src={strategyImpactSimulator} className="rb:w-5 rb:h-5 rb:mr-2" />
|
||||
{t('forgettingEngine.forgettingEngineConfigParams')}
|
||||
</div>
|
||||
}
|
||||
className='rb:h-full!'
|
||||
<Row gutter={12}>
|
||||
<Col span={12}>
|
||||
<RbCard
|
||||
title={t('forgettingEngine.forgettingEngineConfigParams')}
|
||||
extra={<Space>
|
||||
<Button block onClick={handleReset}>{t('common.reset')}</Button>
|
||||
<Button type="primary" loading={loading} block onClick={handleSave}>{t('common.save')}</Button>
|
||||
</Space>}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
|
||||
className="rb:h-[calc(100vh-76px)]!"
|
||||
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-3! rb:pt-0!"
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
@@ -174,7 +177,7 @@ const ForgettingEngine: React.FC = () => {
|
||||
lambda_mem: 0.03,
|
||||
}}
|
||||
>
|
||||
<Space size={24} direction="vertical" style={{ width: '100%' }}>
|
||||
<Flex vertical gap={12}>
|
||||
{configList.map(config => {
|
||||
if (config.type === 'button') {
|
||||
return (
|
||||
@@ -182,51 +185,53 @@ const ForgettingEngine: React.FC = () => {
|
||||
title={t(`forgettingEngine.${config.key}`)}
|
||||
name={config.name}
|
||||
desc={config.type && <span>{t(`forgettingEngine.type`)}: {config.type}</span>}
|
||||
className="rb:mb-2"
|
||||
className="rb:bg-[#F6F6F6] rb:rounded-xl rb:p-3!"
|
||||
/>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div key={config.key}>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
|
||||
<div key={config.key} className="rb:bg-[#F6F6F6] rb:rounded-xl rb:p-3">
|
||||
<Flex align="center" gap={4} className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
|
||||
{t(`forgettingEngine.${config.key}`)}
|
||||
</div>
|
||||
{!config.hiddenDesc && <div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ">
|
||||
{t(`forgettingEngine.${config.key}Desc`)}
|
||||
</div>}
|
||||
{!config.hiddenDesc && <Tooltip title={t(`forgettingEngine.${config.key}Desc`)}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>}
|
||||
</Flex>
|
||||
|
||||
<Form.Item
|
||||
name={config.name}
|
||||
extra={<DescWrapper
|
||||
desc={<>
|
||||
<span className="rb:text-[12px]">{t(`forgettingEngine.range`)}: {config.range?.join('-')}</span> | <span>{t(`forgettingEngine.type`)}: {config.type}</span>
|
||||
</>}
|
||||
/>}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
{config.type === 'decimal'
|
||||
? <Slider tooltip={{ open: false }} max={config.range?.[1] || 1} min={config.range?.[0] || 0} step={config.step ?? 0.01} style={{ margin: '0' }} />
|
||||
? <RbSlider
|
||||
max={config.range?.[1] || 1}
|
||||
min={config.range?.[0] || 0}
|
||||
step={config.step ?? 0.01}
|
||||
isInput={true}
|
||||
prefix={<span className="rb:text-[#5B6167]">{t('emotionEngine.currentValue')}:</span>}
|
||||
inputClassName="rb:w-[155px]!"
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</Form.Item>
|
||||
<div className="rb:flex rb:text-[12px] rb:items-center rb:justify-between rb:text-[#5B6167] rb:leading-5 rb:-mt-6.5">
|
||||
<Space size={4}>
|
||||
{config.range && <span>{t(`forgettingEngine.range`)}: {config.range?.join('-')}</span>}
|
||||
{config.type && <span>{t(`forgettingEngine.type`)}: {config.type}</span>}
|
||||
</Space>
|
||||
<>{t('forgettingEngine.CurrentValue')}: {values?.[config.name] || 0}</>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Button block onClick={handleReset}>{t('common.reset')}</Button>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Button type="primary" loading={loading} block onClick={handleSave}>{t('common.save')}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Space>
|
||||
</Flex>
|
||||
</Form>
|
||||
</RbCard>
|
||||
</Col>
|
||||
<Col span={15}>
|
||||
<Col span={12}>
|
||||
<RbCard
|
||||
title={t('forgettingEngine.forgettingCurve')}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
|
||||
bodyClassName="rb:p-3! rb:pt-0!"
|
||||
>
|
||||
<LineChart
|
||||
config={values}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:51
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:30:51
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 14:06:38
|
||||
*/
|
||||
/**
|
||||
* Card Component
|
||||
@@ -10,11 +10,10 @@
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import clsx from 'clsx';
|
||||
import { Flex, Space, Tooltip } from 'antd';
|
||||
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import down from '@/assets/images/userMemory/down.svg'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
@@ -29,6 +28,7 @@ interface CardProps {
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
bodyClassName?: string;
|
||||
extra?: ReactNode;
|
||||
}
|
||||
|
||||
const Card: FC<CardProps> = ({
|
||||
@@ -41,27 +41,33 @@ const Card: FC<CardProps> = ({
|
||||
className,
|
||||
headerClassName,
|
||||
bodyClassName,
|
||||
extra,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<RbCard
|
||||
title={title}
|
||||
subTitle={subTitle}
|
||||
headerType="borderless"
|
||||
extra={type && handleExpand && (
|
||||
<div
|
||||
className="rb:flex rb:items-center rb:text-[14px] rb:text-[#5B6167] rb:cursor-pointer rb:font-regular rb:leading-5"
|
||||
onClick={() => handleExpand(type)}
|
||||
>
|
||||
{expanded ? t('common.foldUp') : t('common.expanded')}
|
||||
<img src={down} className={clsx("rb:w-4 rb:h-4 rb:ml-1", {
|
||||
title={() => <Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
className="rb:font-[MiSans-Bold] rb:font-bold rb:cursor-pointer"
|
||||
onClick={type && handleExpand ? () => handleExpand(type) : undefined}
|
||||
>
|
||||
<Space size={4}>
|
||||
{title}
|
||||
{subTitle && <Tooltip title={subTitle}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>}
|
||||
</Space>
|
||||
{handleExpand && <div
|
||||
className={clsx("rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_up.svg')]", {
|
||||
'rb:rotate-180': !expanded,
|
||||
})} />
|
||||
</div>
|
||||
)}
|
||||
})}
|
||||
></div>}
|
||||
</Flex>}
|
||||
headerType="borderless"
|
||||
className={className}
|
||||
headerClassName={headerClassName}
|
||||
bodyClassName={bodyClassName}
|
||||
headerClassName={`rb:h-[50px]! rb:pb-[12px]! rb:pt-[16px]! rb:leading-[22px]! rb:font-[MiSans-Bold] rb:font-bold rb:text-[16px] ${headerClassName}`}
|
||||
bodyClassName={`rb:px-3! rb:py-0! ${expanded ? 'rb:pb-3!' : 'rb:pb-0!'} ${bodyClassName}`}
|
||||
extra={extra}
|
||||
>
|
||||
{(expanded || !(type && handleExpand)) && children}
|
||||
</RbCard>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:11
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-02 11:41:12
|
||||
* @Last Modified time: 2026-03-19 14:22:20
|
||||
*/
|
||||
/**
|
||||
* Result Component
|
||||
@@ -13,13 +13,13 @@
|
||||
import { type FC, useState } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Space, Button, Progress, Form, Input } from 'antd'
|
||||
import { ExclamationCircleFilled, CheckCircleFilled, ClockCircleOutlined, LoadingOutlined } from '@ant-design/icons'
|
||||
import { Space, Button, Progress, Form, Input, Flex } from 'antd'
|
||||
import { ExclamationCircleFilled, LoadingOutlined } from '@ant-design/icons'
|
||||
import clsx from 'clsx'
|
||||
import ResultCard from './ResultCard'
|
||||
import type { AnyObject } from 'antd/es/_util/type';
|
||||
|
||||
import Card from './Card'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import RbAlert from '@/components/RbAlert'
|
||||
import type { TestResult, OntologyCoverage } from '../types'
|
||||
import { pilotRunMemoryExtractionConfig } from '@/api/memory'
|
||||
@@ -27,6 +27,8 @@ import { type SSEMessage } from '@/utils/stream'
|
||||
import Tag, { type TagProps } from '@/components/Tag'
|
||||
import Markdown from '@/components/Markdown'
|
||||
import { groupDataByType } from '../constant'
|
||||
import Empty from '@/components/Empty'
|
||||
import NoDataIcon from '@/assets/images/empty/noData.png'
|
||||
|
||||
/** Result metric mapping */
|
||||
const resultObj = {
|
||||
@@ -56,7 +58,7 @@ interface ModuleItem {
|
||||
const tagColors: {
|
||||
[key: string]: TagProps['color']
|
||||
} = {
|
||||
pending: 'default',
|
||||
pending: 'warning',
|
||||
processing: 'processing',
|
||||
completed: 'success',
|
||||
failed: 'error'
|
||||
@@ -67,29 +69,55 @@ const initObj = {
|
||||
status: 'pending',
|
||||
result: null
|
||||
}
|
||||
const initialExpanded = {
|
||||
text_preprocessing: false,
|
||||
knowledge_extraction: false,
|
||||
creating_nodes_edges: false,
|
||||
deduplication: false,
|
||||
dataStatistics: false,
|
||||
entityDeduplicationImpact: false,
|
||||
disambiguation: false,
|
||||
coreEntities: false,
|
||||
triplet_samples: false,
|
||||
ontologyCoverage: false,
|
||||
}
|
||||
|
||||
const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
const { t } = useTranslation();
|
||||
const { id } = useParams()
|
||||
const [runLoading, setRunLoading] = useState(false)
|
||||
const [activeTab, setActiveTab] = useState('processData')
|
||||
const [testResult, setTestResult] = useState<TestResult>({} as TestResult)
|
||||
|
||||
const [coreEntitiesTab, setCoreEntitiesTab] = useState<string | null>(null)
|
||||
const [textPreprocessing, setTextPreprocessing] = useState<ModuleItem>(initObj as ModuleItem)
|
||||
const [textPreprocessingTab, setTextPreprocessingTab] = useState('chunking')
|
||||
const [knowledgeExtraction, setKnowledgeExtraction] = useState<ModuleItem>(initObj as ModuleItem)
|
||||
const [creatingNodesEdges, setCreatingNodesEdges] = useState<ModuleItem>(initObj as ModuleItem)
|
||||
const [deduplication, setDeduplication] = useState<ModuleItem>(initObj as ModuleItem)
|
||||
const [ontologyCoverage, setOntologyCoverage] = useState<OntologyCoverage>({} as OntologyCoverage)
|
||||
|
||||
const [expandedCards, setExpandedCards] = useState<Record<string, boolean>>(initialExpanded)
|
||||
const toggleCard = (key: string) => {
|
||||
console.log('toggleCard', key)
|
||||
setExpandedCards(prev => ({ ...prev, [key]: !prev[key] }))
|
||||
}
|
||||
console.log('expandedCards', expandedCards)
|
||||
|
||||
const [runForm] = Form.useForm()
|
||||
const customText = Form.useWatch(['custom_text'], runForm)
|
||||
|
||||
/** Run pilot test */
|
||||
const handleRun = () => {
|
||||
if(!id) return
|
||||
setActiveTab('processData')
|
||||
setCoreEntitiesTab(null)
|
||||
setTextPreprocessing({...initObj} as ModuleItem)
|
||||
setTextPreprocessingTab('chunking')
|
||||
setKnowledgeExtraction({...initObj} as ModuleItem)
|
||||
setCreatingNodesEdges({...initObj} as ModuleItem)
|
||||
setDeduplication({...initObj} as ModuleItem)
|
||||
setTestResult({} as TestResult)
|
||||
setExpandedCards(initialExpanded)
|
||||
const handleStreamMessage = (list: SSEMessage[]) => {
|
||||
|
||||
list.forEach((data: AnyObject) => {
|
||||
@@ -100,6 +128,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
status: 'processing',
|
||||
start_at: data.data.time
|
||||
}))
|
||||
toggleCard('text_preprocessing')
|
||||
break
|
||||
case 'text_preprocessing_result': // Text preprocessing in progress
|
||||
setTextPreprocessing(prev => ({
|
||||
@@ -121,6 +150,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
status: 'processing',
|
||||
start_at: data.data.time
|
||||
}))
|
||||
toggleCard('knowledge_extraction')
|
||||
break
|
||||
case 'knowledge_extraction_result': // Knowledge extraction in progress
|
||||
setKnowledgeExtraction(prev => ({
|
||||
@@ -142,6 +172,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
status: 'processing',
|
||||
start_at: data.data.time
|
||||
}))
|
||||
toggleCard('creating_nodes_edges')
|
||||
break
|
||||
case 'creating_nodes_edges_result': // Creating nodes and edges in progress
|
||||
setCreatingNodesEdges(prev => ({
|
||||
@@ -163,6 +194,7 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
status: 'processing',
|
||||
start_at: data.data.time
|
||||
}))
|
||||
toggleCard('deduplication')
|
||||
break
|
||||
case 'dedup_disambiguation_result': // Deduplication and disambiguation in progress
|
||||
setDeduplication(prev => ({
|
||||
@@ -183,6 +215,15 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
case 'result': // Result
|
||||
setTestResult(data.data?.extracted_result)
|
||||
setOntologyCoverage(data.data?.ontology_coverage)
|
||||
setExpandedCards(prev => ({
|
||||
...prev,
|
||||
dataStatistics: true,
|
||||
entityDeduplicationImpact: true,
|
||||
disambiguation: true,
|
||||
coreEntities: true,
|
||||
triplet_samples: true,
|
||||
ontologyCoverage: true,
|
||||
}))
|
||||
break
|
||||
}
|
||||
})
|
||||
@@ -203,9 +244,10 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
/** Format status tag */
|
||||
const formatTag = (status: string) => {
|
||||
return (
|
||||
<Tag color={tagColors[status]}>
|
||||
{status === 'pending' && <ClockCircleOutlined className="rb:mr-1" />}
|
||||
<Tag color={tagColors[status]} className="rb:flex! rb:items-center rb:gap-1 rb:bg-white! rb:border-white!">
|
||||
{status === 'pending' && <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/memory/clock_orange.svg')]"></div>}
|
||||
{status === 'processing' && <LoadingOutlined spin className="rb:mr-1" />}
|
||||
{status === 'completed' && <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/check_green.svg')]"></div>}
|
||||
{t(`memoryExtractionEngine.status.${status}`)}
|
||||
</Tag>
|
||||
)
|
||||
@@ -213,294 +255,411 @@ const Result: FC<ResultProps> = ({ loading, handleSave }) => {
|
||||
/** Format processing time */
|
||||
const formatTime = (data: ModuleItem, color?: string) => {
|
||||
if (typeof data.end_at === 'number' && typeof data.start_at === 'number') {
|
||||
return <div className={`rb:mt-3 rb:text-[${color ?? '#155EEF'}]`}>{t('memoryExtractionEngine.time')}{data.end_at - data.start_at}ms</div>
|
||||
return <div className={`rb:text-[${color ?? '#155EEF'}] rb:mb-0.5`}>{t('memoryExtractionEngine.time')}{data.end_at - data.start_at}ms</div>
|
||||
}
|
||||
return null
|
||||
}
|
||||
/** Convert first character to lowercase */
|
||||
const lowercaseFirst = (str: string) => str.charAt(0).toLowerCase() + str.slice(1)
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={t('memoryExtractionEngine.exampleMemoryExtractionResults')}
|
||||
subTitle={t('memoryExtractionEngine.exampleMemoryExtractionResultsSubTitle')}
|
||||
className="rb:min-h-[calc(100vh-330px)]!"
|
||||
headerClassName="rb:pb-0! rb:pt-4!"
|
||||
bodyClassName="rb:min-h-[calc(100vh-388px)] rb:p-[16px_20px]!"
|
||||
bodyClassName="rb:h-[calc(100vh-163px)]! rb:overflow-y-auto rb:p-[16px_20px]!"
|
||||
extra={<Space size={8}>
|
||||
<Button
|
||||
icon={<div className="rb:size-3.5 rb:bg-cover rb:bg-[url('@/assets/images/common/save.svg')]"></div>}
|
||||
loading={loading}
|
||||
onClick={handleSave}
|
||||
>{t('common.save')}</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<div className="rb:size-3.5 rb:bg-cover rb:bg-[url('@/assets/images/memory/debug.svg')]"></div>}
|
||||
loading={runLoading}
|
||||
onClick={handleRun}
|
||||
>{t('memoryExtractionEngine.debug')}</Button>
|
||||
</Space>}
|
||||
>
|
||||
<Form form={runForm} layout="vertical">
|
||||
{/* <RbAlert color="orange" icon={<ExclamationCircleFilled />} className="rb:mb-3!">
|
||||
{t('memoryExtractionEngine.warning')}
|
||||
</RbAlert> */}
|
||||
<Form form={runForm} layout="vertical" className="rb:bg-[#F6F6F6]! rb:rounded-xl rb:py-2! rb:mb-4!">
|
||||
<Flex align="center" justify="space-between" className="rb:px-3! rb:mb-2!">
|
||||
<div className="rb:text-[#212332] rb:font-medium rb:leading-5">{t('memoryExtractionEngine.custom_text')}</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4.5">{customText?.length || 0}</div>
|
||||
</Flex>
|
||||
<Form.Item
|
||||
name="custom_text"
|
||||
label={t('memoryExtractionEngine.custom_text')}
|
||||
noStyle
|
||||
>
|
||||
<Input.TextArea placeholder={t('common.pleaseEnter')} />
|
||||
<Input.TextArea placeholder={t('common.pleaseEnter')} variant="borderless" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<div className="rb:min-h-[calc(100vh-480px)] rb:overflow-y-auto">
|
||||
{runLoading
|
||||
? <>
|
||||
<RbAlert color="blue" icon={<ExclamationCircleFilled />} className="rb:mb-3.5">
|
||||
|
||||
{runLoading
|
||||
? <>
|
||||
<RbAlert color="blue">
|
||||
<div className="rb:w-full">
|
||||
{t('memoryExtractionEngine.processing')}
|
||||
</RbAlert>
|
||||
{/* Overall Progress */}
|
||||
<div className="rb:mb-2">
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:text-[12px] rb:leading-4 rb:font-regular">
|
||||
{t('memoryExtractionEngine.overallProgress')}
|
||||
<span className="rb:text-[#155eef]">{`${completedNum}/4`}</span>
|
||||
</div>
|
||||
<Progress percent={completedNum * 100/4} showInfo={false} />
|
||||
|
||||
{/* Overall Progress */}
|
||||
<Flex gap={13} align="center">
|
||||
<Progress percent={completedNum * 100 / 4} showInfo={false} className="rb:flex-1!" />
|
||||
<div className="rb:text-[12px] rb:leading-4 rb:font-regular">
|
||||
{t('memoryExtractionEngine.overallProgress')}{`${(completedNum*100/4).toFixed(0)}%`}
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
</>
|
||||
: !testResult || Object.keys(testResult).length === 0
|
||||
? <RbAlert color="orange" icon={<ExclamationCircleFilled />} className="rb:mb-3.5">
|
||||
{t('memoryExtractionEngine.warning')}
|
||||
</RbAlert>
|
||||
: <RbAlert color="green" icon={<ExclamationCircleFilled />} className="rb:mb-3.5">
|
||||
{t('memoryExtractionEngine.success')}
|
||||
</RbAlert>
|
||||
}
|
||||
<Space size={16} direction="vertical" style={{ width: '100%' }}>
|
||||
{/* Text Preprocessing */}
|
||||
<RbCard
|
||||
title={t(`memoryExtractionEngine.text_preprocessing`)}
|
||||
extra={formatTag(textPreprocessing.status)}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#155EEF]!"
|
||||
>
|
||||
{textPreprocessing.data.map((vo, index) => {
|
||||
if (vo.deleted_messages) {
|
||||
return <div key={index} className="rb:mb-3 rb:pb-1 rb:border-b rb:border-b-[#EBEBEB]">
|
||||
<div className="rb:font-medium rb:text-[12px] rb:mb-2">{t('memoryExtractionEngine.Pruned')}</div>
|
||||
{vo.deleted_messages.map((msg: any, idx: number) => (
|
||||
<div key={idx} className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">
|
||||
<Markdown content={'-' + t('memoryExtractionEngine.pruning') + (idx + 1) + ': ' + msg.content} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
return (
|
||||
<div key={index} className="rb:mb-3 rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">
|
||||
<Markdown content={'-' + t('memoryExtractionEngine.fragment') + vo.chunk_index + ': ' + (vo.content.startsWith('\n') ? vo.content : '\n' + vo.content)} />
|
||||
</div>
|
||||
)
|
||||
</RbAlert>
|
||||
</>
|
||||
: !testResult || Object.keys(testResult).length === 0
|
||||
? <RbAlert color="orange" icon={<ExclamationCircleFilled />}>
|
||||
{t('memoryExtractionEngine.warning')}
|
||||
</RbAlert>
|
||||
: <RbAlert color="green" icon={<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/check_green.svg')]"></div>}>
|
||||
{t('memoryExtractionEngine.success')}
|
||||
</RbAlert>
|
||||
}
|
||||
|
||||
<Space size={24} className="rb:mt-4! rb:mb-3!">
|
||||
{['processData', 'finalResult'].map(tab => (
|
||||
<div
|
||||
className={clsx('rb:font-[MiSans-Bold] rb:font-bold rb:leading-5 rb:cursor-pointer', {
|
||||
'rb:text-[#212332]': activeTab === tab,
|
||||
'rb:text-[#A8A9AA]': activeTab !== tab,
|
||||
})}
|
||||
{formatTime(textPreprocessing)}
|
||||
{textPreprocessing.result &&
|
||||
<RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
onClick={() => setActiveTab(tab)}
|
||||
>{t(`memoryExtractionEngine.${tab}`)}</div>
|
||||
))}
|
||||
</Space>
|
||||
|
||||
{activeTab === 'processData' && <Flex vertical gap={12} className="rb:pb-3!">
|
||||
{/* Text Preprocessing */}
|
||||
<ResultCard
|
||||
title={t(`memoryExtractionEngine.text_preprocessing`)}
|
||||
extra={formatTag(textPreprocessing.status)}
|
||||
expanded={expandedCards['text_preprocessing']}
|
||||
handleExpand={() => toggleCard('text_preprocessing')}
|
||||
>
|
||||
{expandedCards['text_preprocessing'] && textPreprocessing.data?.length > 0 &&
|
||||
<Space size={10} className="rb:px-1! rb:mb-3!">
|
||||
{(['chunking', ...(textPreprocessing.data.some(vo => vo.deleted_messages) ? ['pruning'] : [])] as string[]).map(type => (
|
||||
<div
|
||||
key={type}
|
||||
className={clsx("rb:rounded-[13px] rb:py-0.5 rb:px-3 rb:leading-5 rb:cursor-pointer", {
|
||||
'rb:bg-white': textPreprocessingTab !== type,
|
||||
'rb:bg-[#171719] rb:text-white': textPreprocessingTab === type
|
||||
})}
|
||||
onClick={() => setTextPreprocessingTab(type)}
|
||||
>
|
||||
{t(`memoryExtractionEngine.${type}`)}
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
}
|
||||
{expandedCards['text_preprocessing'] && textPreprocessing.result &&
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
<div>
|
||||
<div>{formatTime(textPreprocessing)}</div>
|
||||
{t('memoryExtractionEngine.pruning_desc', { count: textPreprocessing.result.pruning.deleted_count || 0 })},
|
||||
{t('memoryExtractionEngine.text_preprocessing_desc', { count: textPreprocessing.result.total_chunks })},
|
||||
{t('memoryExtractionEngine.chunkerStrategy')}: {t(`memoryExtractionEngine.${lowercaseFirst(textPreprocessing.result.chunker_strategy)}`)}
|
||||
</RbAlert>
|
||||
}
|
||||
</RbCard>
|
||||
{/* Knowledge Extraction */}
|
||||
<RbCard
|
||||
title={t(`memoryExtractionEngine.knowledge_extraction`)}
|
||||
extra={formatTag(knowledgeExtraction.status)}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#155EEF]!"
|
||||
>
|
||||
{knowledgeExtraction.data.map((vo, index) =>
|
||||
<div key={index} className="rb:mb-3 rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">{vo.statement}</div>
|
||||
)}
|
||||
{formatTime(knowledgeExtraction)}
|
||||
{knowledgeExtraction.result && <RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{t('memoryExtractionEngine.knowledge_extraction_desc', {
|
||||
entities: knowledgeExtraction.result.entities_count,
|
||||
statements: knowledgeExtraction.result.statements_count,
|
||||
temporal_ranges_count: knowledgeExtraction.result.temporal_ranges_count,
|
||||
triplets: knowledgeExtraction.result.triplets_count
|
||||
})}
|
||||
</RbAlert>}
|
||||
</RbCard>
|
||||
{/* Creating Entity Relationships */}
|
||||
<RbCard
|
||||
title={t(`memoryExtractionEngine.creating_nodes_edges`)}
|
||||
extra={formatTag(creatingNodesEdges.status)}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#9C6FFF]!"
|
||||
>
|
||||
{creatingNodesEdges.data?.map((vo, index) => (
|
||||
<div key={index} className="rb:mb-3 rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">
|
||||
{vo?.result_type === 'entity_nodes_creation'
|
||||
? <>{vo.type_display_name}: {vo.entity_names.join(', ')}</>
|
||||
: <>{vo?.relationship_text}</>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
{formatTime(creatingNodesEdges, '#9C6FFF')}
|
||||
{creatingNodesEdges.result && <RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{t('memoryExtractionEngine.creating_nodes_edges_desc', {num: creatingNodesEdges.result.entity_entity_edges_count})}
|
||||
</RbAlert>}
|
||||
</RbCard>
|
||||
{/* Deduplication and Disambiguation */}
|
||||
<RbCard
|
||||
title={t(`memoryExtractionEngine.deduplication`)}
|
||||
extra={formatTag(deduplication.status)}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#9C6FFF]!"
|
||||
>
|
||||
{Object.keys(deduplicationData).length > 0 && Object.keys(deduplicationData).map(key => {
|
||||
return deduplicationData[key].map((vo, index) => (
|
||||
<div key={index} className="rb:mb-3 rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">
|
||||
{vo.message}
|
||||
</div>
|
||||
))
|
||||
})}
|
||||
{formatTime(deduplication, '#9C6FFF')}
|
||||
{deduplication.result && <RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{t('memoryExtractionEngine.deduplication_desc', { count: deduplication.result.summary.total_merges })}<br />
|
||||
</RbAlert>}
|
||||
</RbCard>
|
||||
|
||||
{testResult && Object.keys(testResult).length > 0 && resultObj && Object.keys(resultObj).length > 0 &&
|
||||
<RbCard>
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-[40px_57px]">
|
||||
{Object.keys(resultObj).map((key, index) => {
|
||||
const keys = (resultObj as Record<string, string>)[key].split('.')
|
||||
return (
|
||||
<div key={index}>
|
||||
<div className="rb:text-[24px] rb:leading-7.5 rb:font-extrabold">{(testResult?.[keys[0] as keyof TestResult] as any)?.[keys[1]]}</div>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:font-regular">{t(`memoryExtractionEngine.${key}`)}</div>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#369F21] rb:leading-3.5 rb:font-regular">
|
||||
{key === 'extractTheNumberOfEntities' && testResult.dedup
|
||||
? t(`memoryExtractionEngine.${key}Desc`, {
|
||||
num: testResult.dedup.total_merged_count,
|
||||
exact: testResult.dedup.breakdown.exact,
|
||||
fuzzy: testResult.dedup.breakdown.fuzzy,
|
||||
llm: testResult.dedup.breakdown.llm,
|
||||
})
|
||||
: key === 'numberOfEntityDisambiguation' && testResult.disambiguation
|
||||
? t(`memoryExtractionEngine.${key}Desc`, { num: testResult.disambiguation.effects?.length, block_count: testResult.disambiguation.block_count })
|
||||
: key === 'numberOfRelationalTriples' && testResult.triplets
|
||||
? t(`memoryExtractionEngine.${key}Desc`, { num: testResult.triplets.count })
|
||||
:t(`memoryExtractionEngine.${key}Desc`)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)})}
|
||||
</div>
|
||||
</RbCard>
|
||||
</RbAlert>
|
||||
}
|
||||
|
||||
{testResult?.dedup?.impact && testResult.dedup.impact?.length > 0 &&
|
||||
<RbCard
|
||||
title={t('memoryExtractionEngine.entityDeduplicationImpact')}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#155EEF]!"
|
||||
>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:font-medium rb:leading-4">{t('memoryExtractionEngine.identifyDuplicates')}</div>
|
||||
{testResult.dedup.impact.map((item, index) => (
|
||||
<div key={index} className="rb:pl-2 rb:mt-2 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">
|
||||
-{t('memoryExtractionEngine.identifyDuplicatesDesc', { ...item })}
|
||||
{expandedCards['text_preprocessing'] && textPreprocessing.data.map((vo, index) => {
|
||||
if (vo.deleted_messages && textPreprocessingTab === 'pruning') {
|
||||
return <div key={index} className="rb:mb-3 rb:pb-1 rb:border-b rb:border-b-[#EBEBEB]">
|
||||
<div className="rb:font-medium rb:text-[12px] rb:mb-2">{t('memoryExtractionEngine.Pruned')}</div>
|
||||
{vo.deleted_messages.map((msg: any, idx: number) => (
|
||||
<div key={idx} className="rb:leading-5">
|
||||
<div className="rb:font-medium">-{t('memoryExtractionEngine.pruning')}{idx}:</div>
|
||||
<Markdown content={msg.content} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
if (textPreprocessingTab === 'chunking') {
|
||||
return (
|
||||
<div key={index} className="rb:leading-5">
|
||||
<div className="rb:font-medium">-{t('memoryExtractionEngine.fragment')}{vo.chunk_index}:</div>
|
||||
<Markdown content={vo.content.startsWith('\n') ? vo.content : '\n' + vo.content} className="rb:text-[#212332]" />
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</ResultCard>
|
||||
{/* Knowledge Extraction */}
|
||||
<ResultCard
|
||||
title={t(`memoryExtractionEngine.knowledge_extraction`)}
|
||||
extra={formatTag(knowledgeExtraction.status)}
|
||||
expanded={expandedCards['knowledge_extraction']}
|
||||
handleExpand={() => toggleCard('knowledge_extraction')}
|
||||
>
|
||||
{knowledgeExtraction.result &&
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
<div>
|
||||
<div>{formatTime(knowledgeExtraction)}</div>
|
||||
{t('memoryExtractionEngine.knowledge_extraction_desc', {
|
||||
entities: knowledgeExtraction.result.entities_count,
|
||||
statements: knowledgeExtraction.result.statements_count,
|
||||
temporal_ranges_count: knowledgeExtraction.result.temporal_ranges_count,
|
||||
triplets: knowledgeExtraction.result.triplets_count
|
||||
})}
|
||||
</div>
|
||||
</RbAlert>
|
||||
}
|
||||
{knowledgeExtraction.data?.length > 0 &&
|
||||
<ul className="rb:list-disc rb:ml-4 rb:mb-3">
|
||||
{knowledgeExtraction.data.map((vo, index) =>
|
||||
<li key={index} className="rb:leading-6">{vo.statement}</li>
|
||||
)}
|
||||
</ul>
|
||||
}
|
||||
</ResultCard>
|
||||
{/* Creating Entity Relationships */}
|
||||
<ResultCard
|
||||
title={t(`memoryExtractionEngine.creating_nodes_edges`)}
|
||||
extra={formatTag(creatingNodesEdges.status)}
|
||||
expanded={expandedCards['creating_nodes_edges']}
|
||||
handleExpand={() => toggleCard('creating_nodes_edges')}
|
||||
>
|
||||
{creatingNodesEdges.result &&
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
<div>
|
||||
<div>{formatTime(creatingNodesEdges)}</div>
|
||||
{t('memoryExtractionEngine.creating_nodes_edges_desc', { num: creatingNodesEdges.result.entity_entity_edges_count })}
|
||||
</div>
|
||||
</RbAlert>
|
||||
}
|
||||
{creatingNodesEdges.data?.length > 0 &&
|
||||
<ul className="rb:list-disc rb:ml-4 rb:mb-3">
|
||||
{creatingNodesEdges.data.map((vo, index) =>
|
||||
<li key={index} className="rb:leading-6">
|
||||
{vo?.result_type === 'entity_nodes_creation'
|
||||
? <>{vo.type_display_name}: {vo.entity_names.join(', ')}</>
|
||||
: <>{vo?.relationship_text}</>
|
||||
}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
}
|
||||
</ResultCard>
|
||||
{/* Deduplication and Disambiguation */}
|
||||
<ResultCard
|
||||
title={t(`memoryExtractionEngine.deduplication`)}
|
||||
extra={formatTag(deduplication.status)}
|
||||
expanded={expandedCards['deduplication']}
|
||||
handleExpand={() => toggleCard('deduplication')}
|
||||
>
|
||||
{deduplication.result &&
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
<div>
|
||||
<div>{formatTime(deduplication)}</div>
|
||||
{t('memoryExtractionEngine.deduplication_desc', { count: deduplication.result.summary.total_merges })}
|
||||
</div>
|
||||
</RbAlert>
|
||||
}
|
||||
{Object.keys(deduplicationData).length > 0 &&
|
||||
<ul className="rb:list-disc rb:ml-4 rb:mb-3">
|
||||
{Object.keys(deduplicationData).map(key => {
|
||||
return deduplicationData[key].map((vo, index) => (
|
||||
<li key={index} className="rb:leading-6">
|
||||
{vo.message}
|
||||
</li>
|
||||
))
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
</ResultCard>
|
||||
</Flex>}
|
||||
|
||||
<RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{activeTab === 'finalResult' && <Flex vertical gap={12} className="rb:pb-3!">
|
||||
{!testResult || Object.keys(testResult).length === 0
|
||||
? <Empty url={NoDataIcon} />
|
||||
: null
|
||||
}
|
||||
|
||||
{testResult && Object.keys(testResult).length > 0 && resultObj && Object.keys(resultObj).length > 0 &&
|
||||
<ResultCard
|
||||
title={t(`memoryExtractionEngine.dataStatistics`)}
|
||||
expanded={expandedCards['dataStatistics']}
|
||||
handleExpand={() => toggleCard('dataStatistics')}
|
||||
>
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-2.5 rb:mb-3">
|
||||
{Object.keys(resultObj).map((key, index) => {
|
||||
const keys = (resultObj as Record<string, string>)[key].split('.')
|
||||
return (
|
||||
<div key={index} className="rb:bg-white rb:rounded-lg rb:py-2 rb:px-3">
|
||||
<div className="rb:text-[24px] rb:leading-8 rb:font-bold rb:font-[MiSans-Bold] rb:mb-1">{(testResult?.[keys[0] as keyof TestResult] as any)?.[keys[1]]}</div>
|
||||
<div className="rb:text-[12px] rb:leading-4 rb:mb-0.5">{t(`memoryExtractionEngine.${key}`)}</div>
|
||||
<div className="rb:text-[12px] rb:text-[#369F21] rb:leading-4">
|
||||
{key === 'extractTheNumberOfEntities' && testResult.dedup
|
||||
? t(`memoryExtractionEngine.${key}Desc`, {
|
||||
num: testResult.dedup.total_merged_count,
|
||||
exact: testResult.dedup.breakdown.exact,
|
||||
fuzzy: testResult.dedup.breakdown.fuzzy,
|
||||
llm: testResult.dedup.breakdown.llm,
|
||||
})
|
||||
: key === 'numberOfEntityDisambiguation' && testResult.disambiguation
|
||||
? t(`memoryExtractionEngine.${key}Desc`, { num: testResult.disambiguation.effects?.length, block_count: testResult.disambiguation.block_count })
|
||||
: key === 'numberOfRelationalTriples' && testResult.triplets
|
||||
? t(`memoryExtractionEngine.${key}Desc`, { num: testResult.triplets.count })
|
||||
: t(`memoryExtractionEngine.${key}Desc`)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
|
||||
{testResult?.dedup?.impact && testResult.dedup.impact?.length > 0 &&
|
||||
<ResultCard
|
||||
title={t('memoryExtractionEngine.entityDeduplicationImpact')}
|
||||
expanded={expandedCards['entityDeduplicationImpact']}
|
||||
handleExpand={() => toggleCard('entityDeduplicationImpact')}
|
||||
>
|
||||
<div className="rb:bg-white rb:rounded-xl rb:p-3 rb:mb-3">
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
{t('memoryExtractionEngine.entityDeduplicationImpactDesc', { count: testResult.dedup.impact.length })}
|
||||
</RbAlert>
|
||||
</RbCard>
|
||||
}
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-2">{t('memoryExtractionEngine.identifyDuplicates')}:</div>
|
||||
|
||||
{testResult?.disambiguation && testResult.disambiguation?.effects?.length > 0 &&
|
||||
<RbCard
|
||||
title={t('memoryExtractionEngine.theEffectOfEntityDisambiguationLLMDriven')}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#155EEF]!"
|
||||
>
|
||||
<ul className="rb:list-disc rb:ml-4">
|
||||
{testResult.dedup.impact.map((item, index) => (
|
||||
<li key={index} className="rb:leading-6">
|
||||
{t('memoryExtractionEngine.identifyDuplicatesDesc', { ...item })}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
|
||||
{testResult?.disambiguation && testResult.disambiguation?.effects?.length > 0 &&
|
||||
<ResultCard
|
||||
title={t('memoryExtractionEngine.theEffectOfEntityDisambiguationLLMDriven')}
|
||||
expanded={expandedCards['disambiguation']}
|
||||
handleExpand={() => toggleCard('disambiguation')}
|
||||
>
|
||||
<div className="rb:bg-white rb:rounded-xl rb:p-3 rb:mb-3">
|
||||
<RbAlert color="blue" className="rb:mb-2!">
|
||||
{t('memoryExtractionEngine.entityDeduplicationImpactDesc', { count: testResult.dedup.impact.length })}
|
||||
</RbAlert>
|
||||
{testResult.disambiguation.effects.map((item, index) => (
|
||||
<div key={index} className={clsx("rb:text-[12px] rb:text-[#5B6167] rb:leading-4", {
|
||||
'rb:mt-4': index > 0,
|
||||
'rb:mt-5': index > 0,
|
||||
})}>
|
||||
<div className="rb:font-medium rb:mb-2">{t('memoryExtractionEngine.disagreementCase')} {index +1}:</div>
|
||||
-{item.left.name}({item.left.type}) vs {item.right.name}({item.right.type}) → <span className="rb:text-[#369F21]">{item.result}</span>
|
||||
<div className="rb:font-medium rb:leading-5 rb:mb-1">{t('memoryExtractionEngine.disagreementCase')} {index + 1}:</div>
|
||||
|
||||
<ul className="rb:list-disc rb:ml-4">
|
||||
<li key={index} className="rb:leading-6">
|
||||
{item.left.name}({item.left.type}) vs {item.right.name}({item.right.type}) → {item.result}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
|
||||
<RbAlert color="blue" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{t('memoryExtractionEngine.entityDeduplicationImpactDesc', { count: testResult.dedup.impact.length })}
|
||||
</RbAlert>
|
||||
</RbCard>
|
||||
}
|
||||
{testResult?.core_entities && testResult?.core_entities.length > 0 &&
|
||||
<ResultCard
|
||||
title={t('memoryExtractionEngine.coreEntitiesAfterDedup')}
|
||||
expanded={expandedCards['coreEntities']}
|
||||
handleExpand={() => toggleCard('coreEntities')}
|
||||
>
|
||||
<Flex gap={10} wrap className="rb:px-1! rb:mb-3! rb:gap-y-2!">
|
||||
{testResult.core_entities.map((item, index) => (
|
||||
<div
|
||||
key={item.type}
|
||||
className={clsx("rb:rounded-[13px] rb:py-0.5 rb:px-3 rb:leading-5 rb:cursor-pointer", {
|
||||
'rb:bg-white': !((coreEntitiesTab && item.type === coreEntitiesTab) || (!coreEntitiesTab && index === 0)),
|
||||
'rb:bg-[#171719] rb:text-white': (coreEntitiesTab && item.type === coreEntitiesTab) || (!coreEntitiesTab && index === 0)
|
||||
})}
|
||||
onClick={() => setCoreEntitiesTab(item.type)}
|
||||
>
|
||||
{item.type}({item.count})
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
<div className="rb:bg-white rb:rounded-lg rb:py-2.5 rb:px-3 rb:mb-3">
|
||||
{testResult.core_entities.filter((item, index) => (coreEntitiesTab && item.type === coreEntitiesTab) || (!coreEntitiesTab && index === 0)).map((item, idx) => (
|
||||
<div key={idx} className="rb:leading-5">
|
||||
<div className="rb:text-[#155EEF] rb:font-medium rb:mb-2">{item.type}({item.count})</div>
|
||||
|
||||
{testResult?.core_entities && testResult?.core_entities.length > 0 &&
|
||||
<RbCard
|
||||
title={t('memoryExtractionEngine.coreEntitiesAfterDedup')}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#369F21]!"
|
||||
>
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-6">
|
||||
{testResult.core_entities.map((item, idx) => (
|
||||
<div key={idx} className="rb:text-[12px]">
|
||||
<div className="rb:text-[#369F21] rb:font-medium">{item.type}({item.count})</div>
|
||||
<ul className="rb:list-disc rb:ml-4">
|
||||
{item.entities.map((entity, index) => (
|
||||
<li key={index} className="rb:leading-6">
|
||||
{entity}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
|
||||
<div>
|
||||
{item.entities.map((entity, index) => (
|
||||
<div key={index} className="rb:text-[#5B6167] rb:font-regular rb:leading-4">
|
||||
-{entity}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RbCard>
|
||||
}
|
||||
|
||||
{testResult?.triplet_samples && testResult?.triplet_samples.length > 0 &&
|
||||
<RbCard
|
||||
title={t('memoryExtractionEngine.extractRelationalTriples')}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#9C6FFF]!"
|
||||
>
|
||||
<Space size={8} direction="vertical" className="rb:w-full">
|
||||
{testResult.triplet_samples.map((item, index) => (
|
||||
<div key={index} className="rb:text-[12px]">
|
||||
-({item.subject}, <span className="rb:text-[#9C6FFF] rb:font-medium">{item.predicate}</span>, {item.object})
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
<RbAlert color="purple" icon={<CheckCircleFilled />} className="rb:mt-3">
|
||||
{testResult?.triplet_samples && testResult?.triplet_samples.length > 0 &&
|
||||
<ResultCard
|
||||
title={t('memoryExtractionEngine.extractRelationalTriples')}
|
||||
expanded={expandedCards['triplet_samples']}
|
||||
handleExpand={() => toggleCard('triplet_samples')}
|
||||
>
|
||||
<div className="rb:bg-white rb:rounded-xl rb:p-3 rb:mb-3">
|
||||
<RbAlert color="blue"className="rb:mb-2!">
|
||||
{t('memoryExtractionEngine.extractRelationalTriplesDesc', { count: testResult.triplet_samples.length })}
|
||||
</RbAlert>
|
||||
</RbCard>
|
||||
}
|
||||
{ontologyCoverage && Object.keys(ontologyCoverage).length > 0 &&
|
||||
<RbCard
|
||||
title={<>{t('memoryExtractionEngine.ontologyCoverage')}({ontologyCoverage.total_entities})</>}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#369F21]!"
|
||||
>
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-3">
|
||||
<ul className="rb:list-disc rb:ml-4">
|
||||
{testResult.triplet_samples.map((item, index) => (
|
||||
<li key={index} className="rb:leading-6">
|
||||
({item.subject}, <span className="rb:text-[#155EEF] rb:font-medium">{item.predicate}</span>, {item.object})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
{ontologyCoverage && Object.keys(ontologyCoverage).length > 0 &&
|
||||
<ResultCard
|
||||
title={<>{t('memoryExtractionEngine.ontologyCoverage')}({ontologyCoverage.total_entities})</>}
|
||||
expanded={expandedCards['ontologyCoverage']}
|
||||
handleExpand={() => toggleCard('ontologyCoverage')}
|
||||
>
|
||||
<div className="rb:bg-white rb:rounded-xl rb:p-3 rb:mb-3 rb:leading-5">
|
||||
<div className="rb:grid rb:grid-cols-1 rb:gap-3">
|
||||
{(['scene_type_distribution', 'general_type_distribution', 'unmatched'] as const).map((key, idx) => {
|
||||
if (!ontologyCoverage[key]) return null
|
||||
return (
|
||||
<div key={idx} className="rb:text-[12px]">
|
||||
<div className="rb:text-[#369F21] rb:font-medium">{t(`memoryExtractionEngine.${key}`)}({ontologyCoverage[key].type_count})</div>
|
||||
<div>{t('memoryExtractionEngine.entity_total', { num: ontologyCoverage[key].entity_total })}</div>
|
||||
<div>
|
||||
<div key={idx}>
|
||||
<div className="rb:text-[#155EEF] rb:font-medium rb:mb-1">{t(`memoryExtractionEngine.${key}`)}({ontologyCoverage[key].type_count})</div>
|
||||
<div className="rb:text-[#212332] rb:mb-1">{t('memoryExtractionEngine.entity_total', { num: ontologyCoverage[key].entity_total })}</div>
|
||||
|
||||
<ul className="rb:list-disc rb:ml-4">
|
||||
{ontologyCoverage[key].types.map((type, index) => {
|
||||
if (!type.type || type.type === '') return null
|
||||
return (
|
||||
<div key={index} className="rb:text-[#5B6167] rb:font-regular rb:leading-4">
|
||||
-{type.type}({type.count})
|
||||
</div>
|
||||
<li key={index} className="rb:leading-6 rb:text-[#5B6167]">
|
||||
{type.type}({type.count})
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</RbCard>
|
||||
}
|
||||
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<div className="rb:grid rb:grid-cols-2 rb:gap-4 rb:mt-5">
|
||||
<Button block loading={loading} onClick={handleSave}>{t('common.save')}</Button>
|
||||
<Button block type="primary" loading={runLoading} onClick={handleRun}>{t('memoryExtractionEngine.debug')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ResultCard>
|
||||
}
|
||||
</Flex>}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:51
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 14:23:58
|
||||
*/
|
||||
/**
|
||||
* ResultCard Component
|
||||
* Collapsible card wrapper for configuration sections
|
||||
*/
|
||||
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
import clsx from 'clsx';
|
||||
import { Flex, Space, Tooltip } from 'antd';
|
||||
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
|
||||
/**
|
||||
* Component props
|
||||
*/
|
||||
interface ResultCardProps {
|
||||
title: string | ReactNode;
|
||||
subTitle?: string | ReactNode;
|
||||
children: ReactNode;
|
||||
expanded?: boolean;
|
||||
handleExpand?: () => void;
|
||||
className?: string;
|
||||
headerClassName?: string;
|
||||
bodyClassName?: string;
|
||||
extra?: ReactNode;
|
||||
}
|
||||
|
||||
const ResultCard: FC<ResultCardProps> = ({
|
||||
title,
|
||||
subTitle,
|
||||
children,
|
||||
expanded,
|
||||
handleExpand,
|
||||
extra,
|
||||
className,
|
||||
headerClassName,
|
||||
bodyClassName,
|
||||
}) => {
|
||||
return (
|
||||
<RbCard
|
||||
title={() => <Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
className="rb:font-[MiSans-Bold] rb:font-bold rb:cursor-pointer"
|
||||
onClick={handleExpand}
|
||||
>
|
||||
<Space size={4}>
|
||||
{title}
|
||||
{subTitle && <Tooltip title={subTitle}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>}
|
||||
</Space>
|
||||
<Space size={4}>
|
||||
{extra}
|
||||
{handleExpand && <div
|
||||
className={clsx("rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_up.svg')] rb:transition-transform", {
|
||||
'rb:rotate-180': !expanded,
|
||||
'rb:rotate-0': expanded,
|
||||
})}
|
||||
></div>}
|
||||
</Space>
|
||||
</Flex>}
|
||||
headerType="borderless"
|
||||
headerClassName={headerClassName ?? "rb:min-h-[40px]! rb:text-[#212332]! rb:text-[14px]!"}
|
||||
bodyClassName={bodyClassName ?? "rb:py-0! rb:px-3!"}
|
||||
className={className ?? "rb:bg-[#F6F6F6]!"}
|
||||
>
|
||||
{(expanded && handleExpand) && children}
|
||||
</RbCard>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResultCard
|
||||
@@ -2,7 +2,7 @@
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:30:02
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-06 13:50:05
|
||||
* @Last Modified time: 2026-03-18 17:55:32
|
||||
*/
|
||||
/**
|
||||
* Memory Extraction Engine Configuration Page
|
||||
@@ -13,18 +13,19 @@
|
||||
import { type FC, useState, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { Row, Col, Space, Select, InputNumber, Slider, App, Form, Input } from 'antd'
|
||||
import { Row, Col, Space, Select, InputNumber, App, Form, Input, Flex, Tooltip } from 'antd'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import Card from './components/Card'
|
||||
import type { ConfigForm, Variable } from './types'
|
||||
import { getMemoryExtractionConfig, updateMemoryExtractionConfig } from '@/api/memory'
|
||||
import Markdown from '@/components/Markdown'
|
||||
import { getModelListUrl } from '@/api/models';
|
||||
import { configList } from './constant'
|
||||
import Result from './components/Result'
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
import CustomSelect from '@/components/CustomSelect'
|
||||
import ModelSelect from '@/components/ModelSelect'
|
||||
import RbSlider from '@/components/RbSlider';
|
||||
import DescWrapper from '@/components/FormItem/DescWrapper'
|
||||
|
||||
/** Available configuration section keys */
|
||||
const keys = [
|
||||
@@ -35,7 +36,7 @@ const keys = [
|
||||
/**
|
||||
* Configuration description component
|
||||
*/
|
||||
const ConfigDesc: FC<{ config: Variable, className?: string; onlyMeaning?: boolean; }> = ({ config, className, onlyMeaning = false}) => {
|
||||
const Desc: FC<{ config: Variable, className?: string; onlyMeaning?: boolean; }> = ({ config, className, onlyMeaning = false}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className={className}>
|
||||
@@ -44,7 +45,6 @@ const ConfigDesc: FC<{ config: Variable, className?: string; onlyMeaning?: boole
|
||||
{config.control && <span className="rb:font-regular">{t('memoryExtractionEngine.control')}: {t(`memoryExtractionEngine.${config.control}`)}</span>}
|
||||
{config.type && <span className="rb:font-regular">{t('memoryExtractionEngine.type')}: {config.type}</span>}
|
||||
</Space>}
|
||||
{config.meaning && <div className={clsx("rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 ")}>{t('memoryExtractionEngine.Meaning')}: {t(`memoryExtractionEngine.${config.meaning}`)}</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -122,163 +122,139 @@ const MemoryExtractionEngine: FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rb:text-[24px] rb:font-semibold rb:leading-8 rb:mb-2">{t('memoryExtractionEngine.title')}</div>
|
||||
<div className="rb:text-[#5B6167] rb:leading-5 rb:mb-6">{t('memoryExtractionEngine.subTitle')}</div>
|
||||
<Flex align="center" gap={4} className="rb:font-[MiSans-Bold] rb:text-[16px] rb:font-bold rb:leading-5.5 rb:mb-4!">
|
||||
{t('memoryExtractionEngine.title')}
|
||||
<Tooltip title={t('memoryExtractionEngine.subTitle')}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
|
||||
<Row gutter={[16, 16]}>
|
||||
<Row gutter={12}>
|
||||
<Col span={12}>
|
||||
<Form form={modelForm}>
|
||||
<Form.Item
|
||||
label={t('memoryExtractionEngine.model')}
|
||||
name="llm_id"
|
||||
>
|
||||
<CustomSelect
|
||||
url={getModelListUrl}
|
||||
params={{ type: 'llm,chat', pagesize: 100, is_active: true }}
|
||||
valueKey="id"
|
||||
labelKey="name"
|
||||
hasAll={false}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
<Card
|
||||
type="example"
|
||||
title={t('memoryExtractionEngine.example')}
|
||||
expanded={expandedKeys.includes('example')}
|
||||
handleExpand={handleExpand}
|
||||
>
|
||||
{expandedKeys.includes('example') &&
|
||||
<div className="rb:text-[14px] rb:text-[#5B6167] rb:font-regular rb:leading-5">
|
||||
<Markdown content={t('memoryExtractionEngine.exampleText')} />
|
||||
</div>
|
||||
}
|
||||
</Card>
|
||||
<Row gutter={[16, 16]} className="rb:mt-4">
|
||||
<Col span={14}>
|
||||
<Form
|
||||
form={form}
|
||||
>
|
||||
<Space direction="vertical" size={16} style={{ width: '100%' }}>
|
||||
{configList.map((item, index) => (
|
||||
<Card
|
||||
type={item.type}
|
||||
title={t(`memoryExtractionEngine.${item.type}`)}
|
||||
key={index}
|
||||
expanded={expandedKeys.includes(item.type)}
|
||||
handleExpand={handleExpand}
|
||||
>
|
||||
<Space size={20} direction="vertical" style={{width: '100%'}}>
|
||||
{item.data.map(vo => (
|
||||
<div
|
||||
key={vo.title}
|
||||
className={clsx(
|
||||
`rb:p-[16px_24px] rb:rounded-lg`,
|
||||
'rb:border rb:border-[#DFE4ED]',
|
||||
{
|
||||
'rb:shadow-[inset_4px_0px_0px_0px_#155EEF]': index % 2 === 0,
|
||||
'rb:shadow-[inset_4px_0px_0px_0px_#369F21]': index % 2 !== 0,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5">{t(`memoryExtractionEngine.${vo.title}`)}</div>
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`memoryExtractionEngine.${vo.title}SubTitle`)}</div>
|
||||
<Flex vertical gap={12} className="rb:h-[calc(100vh-114px)]! rb:overflow-y-auto">
|
||||
<Form form={modelForm}>
|
||||
<Form.Item
|
||||
name="llm_id"
|
||||
noStyle
|
||||
>
|
||||
<ModelSelect
|
||||
params={{ type: 'llm,chat' }}
|
||||
className="rb:w-full! rb:h-10! rb:bg-white rb:rounded-xl"
|
||||
variant="borderless"
|
||||
placeholder={t('memoryExtractionEngine.model')}
|
||||
allowClear={false}
|
||||
fontClassName="rb:font-medium!"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
{vo.list.map(config => (
|
||||
<div key={config.label}>
|
||||
{config.control === 'button' &&
|
||||
<SwitchFormItem
|
||||
title={<>-{t(`memoryExtractionEngine.${config.label}`)}</>}
|
||||
name={config.variableName}
|
||||
desc={<ConfigDesc config={config} className="rb:ml-2" />}
|
||||
className="rb:mt-6"
|
||||
/>
|
||||
}
|
||||
{config.control === 'select' &&
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
-{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
<div className="rb:pl-2">
|
||||
<div className="rb:bg-white rb:rounded-xl rb:py-2.5 rb:px-4">
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
className="rb:font-[MiSans-Bold] rb:font-bold rb:cursor-pointer"
|
||||
onClick={() => handleExpand('example')}
|
||||
>
|
||||
{t('memoryExtractionEngine.example')}
|
||||
<div className={clsx("rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_up.svg')]", {
|
||||
'rb:rotate-180': !expandedKeys.includes('example'),
|
||||
'rb:rotate-0': expandedKeys.includes('example'),
|
||||
})}></div>
|
||||
</Flex>
|
||||
|
||||
{expandedKeys.includes('example') &&
|
||||
<div className="rb:text-[14px] rb:text-[#5B6167] rb:font-regular rb:leading-5 rb:mt-2.5 rb:mb-1.5">
|
||||
<Markdown content={t('memoryExtractionEngine.exampleText')} />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Form form={form}>
|
||||
<Flex vertical gap={16}>
|
||||
{configList.map((item, index) => (
|
||||
<Card
|
||||
type={item.type}
|
||||
title={t(`memoryExtractionEngine.${item.type}`)}
|
||||
key={index}
|
||||
expanded={expandedKeys.includes(item.type)}
|
||||
handleExpand={handleExpand}
|
||||
>
|
||||
<Flex gap={16} vertical>
|
||||
{item.data.map(vo => (
|
||||
<Flex
|
||||
key={vo.title}
|
||||
vertical
|
||||
gap={10}
|
||||
className="rb:bg-[#F6F6F6] rb:rounded-xl rb:p-3! rb:pt-2.5!"
|
||||
>
|
||||
<Space size={4} className="rb:text-[#212332] rb:font-medium rb:leading-5">
|
||||
{t(`memoryExtractionEngine.${vo.title}`)}
|
||||
<Tooltip title={t(`memoryExtractionEngine.${vo.title}SubTitle`)}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
|
||||
{vo.list.map(config => (
|
||||
<div key={config.label} className="rb:bg-white rb:rounded-xl rb:p-3 rb:pr-2.5">
|
||||
{config.control === 'button'
|
||||
? <SwitchFormItem
|
||||
title={t(`memoryExtractionEngine.${config.label}`)}
|
||||
name={config.variableName}
|
||||
desc={<DescWrapper desc={<Desc config={config} />} />}
|
||||
className="rb:mt-6"
|
||||
/>
|
||||
: <>
|
||||
{config.meaning
|
||||
? <Space size={4} className="rb:text-[#212332] rb:font-medium rb:leading-5">
|
||||
{t(`memoryExtractionEngine.${config.label}`)}
|
||||
<Tooltip title={<>{t('memoryExtractionEngine.Meaning')}: {t(`memoryExtractionEngine.${config.meaning}`)}</>}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
: <div className="rb:text-[#212332] rb:font-medium rb:leading-5">
|
||||
{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
}
|
||||
{config.control !== 'text' && <DescWrapper desc={<Desc config={config} />} />}
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
className="rb:mb-0! rb:mt-2!"
|
||||
>
|
||||
<Select
|
||||
disabled={config.variableName === 'iteration_period' && iterationPeriodDisabled}
|
||||
options={config.options ? config.options.map(item => ({ ...item, label: t(`memoryExtractionEngine.${item.label}`) })) : []}
|
||||
/>
|
||||
{config.control === 'select'
|
||||
? <Select
|
||||
disabled={config.variableName === 'iteration_period' && iterationPeriodDisabled}
|
||||
options={config.options ? config.options.map(item => ({ ...item, label: t(`memoryExtractionEngine.${item.label}`) })) : []}
|
||||
/>
|
||||
: config.control === 'slider'
|
||||
? <RbSlider
|
||||
min={config.min || 0}
|
||||
max={config.max || 1}
|
||||
step={config.step || 0.01}
|
||||
isInput={true}
|
||||
prefix={<span className="rb:text-[#5B6167]">{t('emotionEngine.currentValue')}:</span>}
|
||||
inputClassName="rb:w-[155px]!"
|
||||
/>
|
||||
: config.control === 'inputNumber'
|
||||
? <InputNumber min={config.min || 0} style={{ width: '100%' }} placeholder={t('common.pleaseEnter')} />
|
||||
: config.control === 'text'
|
||||
? <Input placeholder={t('common.pleaseEnter')} disabled />
|
||||
: null
|
||||
}
|
||||
</Form.Item>
|
||||
<ConfigDesc config={config} className="rb:-mt-4!" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{config.control === 'slider' &&
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
-{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
<div className="rb:pl-2">
|
||||
<ConfigDesc config={config} className="rb:mb-2.5" />
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
>
|
||||
<Slider
|
||||
style={{ margin: '0' }}
|
||||
min={config.min || 0}
|
||||
max={config.max || 1}
|
||||
step={config.step || 0.01}
|
||||
/>
|
||||
</Form.Item>
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:text-[#5B6167] rb:leading-5 rb:-mt-6.5">
|
||||
{config.min || 0}
|
||||
<span>{t('memoryExtractionEngine.CurrentValue')}: {values?.[config.variableName as keyof ConfigForm]}</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{config.control === 'inputNumber' &&
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
-{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
<div className="rb:pl-2">
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
>
|
||||
<InputNumber min={config.min || 0} style={{ width: '100%' }} placeholder={t('common.pleaseEnter')} />
|
||||
</Form.Item>
|
||||
<ConfigDesc config={config} className="rb:-mt-4!" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
{config.control === 'text' &&
|
||||
<>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mt-6 rb:mb-2">
|
||||
-{t(`memoryExtractionEngine.${config.label}`)}
|
||||
</div>
|
||||
<div className="rb:pl-2">
|
||||
<Form.Item
|
||||
name={config.variableName}
|
||||
>
|
||||
<Input placeholder={t('common.pleaseEnter')} disabled />
|
||||
</Form.Item>
|
||||
<ConfigDesc config={config} onlyMeaning={true} className="rb:-mt-4!" />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
</Card>
|
||||
))}
|
||||
</Space>
|
||||
</Form>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Card>
|
||||
))}
|
||||
</Flex>
|
||||
</Form>
|
||||
</Flex>
|
||||
</Col>
|
||||
<Col span={10}>
|
||||
<Col span={12}>
|
||||
<Result
|
||||
loading={loading}
|
||||
handleSave={handleSave}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:46:47
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:46:47
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 15:54:45
|
||||
*/
|
||||
/**
|
||||
* Self Reflection Engine Configuration Page
|
||||
@@ -11,12 +11,11 @@
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Row, Col, Form, App, Button, Space, Select } from 'antd';
|
||||
import { Row, Col, Form, App, Button, Space, Select, Flex } from 'antd';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import RbCard from '@/components/RbCard/Card';
|
||||
import strategyImpactSimulator from '@/assets/images/memory/strategyImpactSimulator.svg'
|
||||
import { getMemoryReflectionConfig, updateMemoryReflectionConfig, pilotRunMemoryReflectionConfig } from '@/api/memory'
|
||||
import type { ConfigForm, Result, ReflexionData, MemoryVerify, QualityAssessment } from './types'
|
||||
import CustomSelect from '@/components/CustomSelect';
|
||||
@@ -24,6 +23,8 @@ import { getModelListUrl } from '@/api/models'
|
||||
import Tag from '@/components/Tag'
|
||||
import { useI18n } from '@/store/locale';
|
||||
import SwitchFormItem from '@/components/FormItem/SwitchFormItem'
|
||||
import LabelWrapper from '@/components/FormItem/LabelWrapper'
|
||||
import DescWrapper from '@/components/FormItem/DescWrapper'
|
||||
|
||||
/** Configuration list */
|
||||
const configList = [
|
||||
@@ -172,13 +173,16 @@ const SelfReflectionEngine: React.FC = () => {
|
||||
return (
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={12}>
|
||||
<RbCard
|
||||
title={
|
||||
<div className="rb:flex rb:items-center">
|
||||
<img src={strategyImpactSimulator} className="rb:w-5 rb:h-5 rb:mr-2" />
|
||||
{t('reflectionEngine.reflectionEngineConfig')}
|
||||
</div>
|
||||
}
|
||||
<RbCard
|
||||
title={t('reflectionEngine.reflectionEngineConfig')}
|
||||
extra={<Space>
|
||||
<Button block onClick={handleReset}>{t('common.reset')}</Button>
|
||||
<Button type="primary" loading={loading} block onClick={handleSave}>{t('common.save')}</Button>
|
||||
</Space>}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:min-h-[54px]! rb:font-[MiSans-Bold] rb:font-bold"
|
||||
className="rb:h-[calc(100vh-76px)]!"
|
||||
bodyClassName="rb:h-[calc(100%-54px)] rb:overflow-y-auto! rb:p-4! rb:pt-0!"
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
@@ -189,74 +193,68 @@ const SelfReflectionEngine: React.FC = () => {
|
||||
lambda_mem: 0.03,
|
||||
}}
|
||||
>
|
||||
{configList.map(config => {
|
||||
if (config.type === 'customSelect') {
|
||||
return (
|
||||
<div key={config.key}>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
|
||||
{t(`reflectionEngine.${config.key}`)}
|
||||
<Flex vertical gap={24}>
|
||||
{configList.map(config => {
|
||||
if (config.type === 'customSelect') {
|
||||
return (
|
||||
<div key={config.key}>
|
||||
<LabelWrapper title={t(`reflectionEngine.${config.key}`)} className="rb:mb-3">
|
||||
<DescWrapper desc={t(`reflectionEngine.${config.key}_desc`)} className="rb:mt-1" />
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<CustomSelect
|
||||
url={config.url as string}
|
||||
params={config.params}
|
||||
valueKey='id'
|
||||
labelKey='name'
|
||||
hasAll={false}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
extra={t(`reflectionEngine.${config.key}_desc`)}
|
||||
>
|
||||
<CustomSelect
|
||||
url={config.url as string}
|
||||
params={config.params}
|
||||
valueKey='id'
|
||||
labelKey='name'
|
||||
hasAll={false}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (config.type === 'select') {
|
||||
return (
|
||||
<div key={config.key}>
|
||||
<div className="rb:text-[14px] rb:font-medium rb:leading-5 rb:mb-2">
|
||||
{t(`reflectionEngine.${config.key}`)}
|
||||
)
|
||||
}
|
||||
if (config.type === 'select') {
|
||||
return (
|
||||
<div key={config.key}>
|
||||
<LabelWrapper title={t(`reflectionEngine.${config.key}`)} className="rb:mb-3">
|
||||
<DescWrapper desc={t(`reflectionEngine.${config.key}_desc`)} className="rb:mt-1" />
|
||||
</LabelWrapper>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
className="rb:mb-0!"
|
||||
>
|
||||
<Select
|
||||
options={config.options?.map(vo => ({
|
||||
...vo,
|
||||
label: t(`reflectionEngine.${vo.label}`),
|
||||
}))}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<Form.Item
|
||||
name={config.key}
|
||||
extra={t(`reflectionEngine.${config.key}_desc`)}
|
||||
>
|
||||
<Select
|
||||
options={config.options?.map(vo => ({
|
||||
...vo,
|
||||
label: t(`reflectionEngine.${vo.label}`),
|
||||
}))}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SwitchFormItem
|
||||
title={t(`reflectionEngine.${config.key}`)}
|
||||
name={config.key}
|
||||
desc={<>
|
||||
{(config as any).hasSubTitle && <div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_subTitle`)}</div>}
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_desc`)}</div>
|
||||
</>}
|
||||
className="rb:mb-6"
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
<Row gutter={16} className="rb:mt-3">
|
||||
<Col span={12}>
|
||||
<Button block onClick={handleReset}>{t('common.reset')}</Button>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Button type="primary" loading={loading} block onClick={handleSave}>{t('common.save')}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
return (
|
||||
<SwitchFormItem
|
||||
title={t(`reflectionEngine.${config.key}`)}
|
||||
name={config.key}
|
||||
desc={<>
|
||||
{(config as any).hasSubTitle && <div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_subTitle`)}</div>}
|
||||
<div className="rb:mt-1 rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4">{t(`reflectionEngine.${config.key}_desc`)}</div>
|
||||
</>}
|
||||
className="rb:mb-6"
|
||||
disabled={!values?.reflection_enabled && config.key !== 'reflection_enabled'}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
</Form>
|
||||
</RbCard>
|
||||
</Col>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:53:44
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 15:01:27
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-16 15:23:18
|
||||
*/
|
||||
/**
|
||||
* User Memory Page
|
||||
@@ -104,7 +104,7 @@ export default function UserMemory() {
|
||||
title={<Flex gap={4}>
|
||||
<div className="rb:size-6 rb:text-center rb:font-semibold rb:leading-6 rb:rounded-md rb:text-white rb:bg-[#155EEF]">{name[0]}</div>
|
||||
|
||||
<Tooltip title={name || '-'}><div className={`rb:w-full rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap`}>{name || '-'}</div></Tooltip>
|
||||
<Tooltip title={name || '-'}><div className={`rb:flex-1 rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap`}>{name || '-'}</div></Tooltip>
|
||||
</Flex>}
|
||||
headerType="border"
|
||||
headerClassName="rb:h-[48px]! rb:mx-4!"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 17:57:11
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 17:57:11
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 11:38:17
|
||||
*/
|
||||
/**
|
||||
* RAG User Memory Detail View
|
||||
@@ -12,83 +12,55 @@
|
||||
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import clsx from 'clsx'
|
||||
import { Row, Col, Skeleton } from 'antd'
|
||||
import { Row, Col, Skeleton, Flex } from 'antd'
|
||||
import { useParams } from 'react-router-dom'
|
||||
|
||||
import aboutUs from '@/assets/images/userMemory/aboutUs.svg'
|
||||
import down from '@/assets/images/userMemory/down.svg'
|
||||
import interestDistribution from '@/assets/images/userMemory/interestDistribution.svg'
|
||||
import memoryInsight from '@/assets/images/userMemory/memoryInsight.svg'
|
||||
import RbCard from '@/components/RbCard/Card'
|
||||
import type { Data } from './types'
|
||||
import {
|
||||
getChunkSummaryTag,
|
||||
getUserProfile,
|
||||
getTotalRagMemoryCountByUser,
|
||||
getChunkInsight,
|
||||
} from '@/api/memory'
|
||||
import Empty from '@/components/Empty'
|
||||
import ConversationMemory from './components/ConversationMemory'
|
||||
|
||||
/** Tag color palette */
|
||||
const tagColors = ['21, 94, 239', '156, 111, 255', '255, 93, 52', '54, 159, 33']
|
||||
|
||||
/**
|
||||
* Title component props
|
||||
*/
|
||||
interface TitleProps {
|
||||
type: string;
|
||||
title: string
|
||||
icon: string
|
||||
t: (key: string) => string;
|
||||
expanded: boolean;
|
||||
onClick: (type: string) => void;
|
||||
}
|
||||
/** Collapsible section title */
|
||||
const Title: FC<TitleProps> = ({ type, title, icon, t, expanded, onClick }) => (
|
||||
<div className="rb:flex rb:items-center rb:justify-between rb:py-4.25 rb:border-b rb:border-[#DFE4ED] rb:text-[16px] rb:font-semibold rb:leading-5.5">
|
||||
<span className="rb:flex rb:items-center">
|
||||
<img src={icon} className="rb:w-5 rb:h-5 rb:mr-2" />
|
||||
{title}
|
||||
</span>
|
||||
|
||||
<span className="rb:flex rb:items-center rb:cursor-pointer rb:text-[#5B6167] rb:text-[14px] rb:font-regular rb:leading-5" onClick={() => onClick(type)}>
|
||||
{t(`userMemory.${expanded ? 'foldUp' : 'expanded'}`)}
|
||||
<img src={down} className={clsx("rb:w-4 rb:h-4 rb:ml-1", {
|
||||
'rb:rotate-180': !expanded,
|
||||
})} />
|
||||
</span>
|
||||
</div>
|
||||
const Title: FC<TitleProps> = ({ title, icon }) => (
|
||||
<Flex align="center" gap={4} className="rb:font-medium rb:leading-5 rb:mb-2.25!">
|
||||
<img src={icon} className="rb:size-4.5 rb:ml-0.5" />
|
||||
{title}
|
||||
</Flex>
|
||||
)
|
||||
|
||||
const Rag: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const [data, setData] = useState<Data | null>(null)
|
||||
const [expanded, setExpanded] = useState<string[]>(['aboutUs', 'memoryInsight',])
|
||||
const [summary, setSummary] = useState<string | null>('')
|
||||
const [loading, setLoading] = useState<Record<string, boolean>>({
|
||||
detail: true,
|
||||
summary: true,
|
||||
insight: true,
|
||||
})
|
||||
const [memory, setMemory] = useState<number | null>(null)
|
||||
const [insight, setInsight] = useState<string | null>('')
|
||||
const [tags, setTags] = useState<{ tag: string; frequency: number }[]>([])
|
||||
const [personas, setPersonas] = useState<string[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return
|
||||
getMemory()
|
||||
getSummary()
|
||||
getDetail()
|
||||
getInsightReport()
|
||||
}, [id])
|
||||
|
||||
/** Toggle section expansion */
|
||||
const handleTitleClick = (key: string) => {
|
||||
setExpanded(expanded.includes(key) ? expanded.filter((item) => item !== key) : [...expanded, key])
|
||||
}
|
||||
/** Fetch user memory detail */
|
||||
const getDetail = () => {
|
||||
if (!id) return
|
||||
@@ -100,13 +72,6 @@ const Rag: FC = () => {
|
||||
setLoading(prev => ({ ...prev, detail: false }))
|
||||
})
|
||||
}
|
||||
/** Fetch memory count */
|
||||
const getMemory = () => {
|
||||
if (!id) return
|
||||
getTotalRagMemoryCountByUser(id).then((res) => {
|
||||
setMemory(res as number || 0)
|
||||
})
|
||||
}
|
||||
/** Fetch user summary */
|
||||
const getSummary = () => {
|
||||
if (!id) return
|
||||
@@ -114,8 +79,6 @@ const Rag: FC = () => {
|
||||
getChunkSummaryTag(id).then((res) => {
|
||||
const response = res as { summary?: string; tags?: { tag: string; frequency: number }[]; personas?: string[] }
|
||||
setSummary(response.summary || null)
|
||||
setTags(response.tags || [])
|
||||
setPersonas(response.personas || [])
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(prev => ({ ...prev, summary: false }))
|
||||
@@ -134,82 +97,51 @@ const Rag: FC = () => {
|
||||
}
|
||||
const name = loading.detail ? '' : data?.name && data?.name !== '' ? data.name : id
|
||||
return (
|
||||
<Row gutter={[16, 16]} className="rb:pb-6">
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={8}>
|
||||
<RbCard>
|
||||
<div className="rb:flex rb:items-center">
|
||||
<div className="rb:flex-[0_0_auto] rb:w-20 rb:h-20 rb:text-center rb:font-semibold rb:text-[28px] rb:leading-20 rb:rounded-lg rb:text-[#FBFDFF] rb:bg-[#155EEF]">{name?.[0]}</div>
|
||||
<div className="rb:text-[24px] rb:font-semibold rb:leading-8 rb:ml-4">
|
||||
{name}<br/>
|
||||
<div className="rb:text-[12px] rb:text-[#5B6167] rb:font-regular rb:leading-4 rb:mt-2">{personas?.join(' | ')}</div>
|
||||
<RbCard
|
||||
bodyClassName="rb:p-3! rb:pt-4! rb:h-[calc(100vh-76px)]"
|
||||
>
|
||||
<Flex align="center" gap={12} className="rb:mb-6!">
|
||||
<div className="rb:size-12 rb:text-center rb:font-semibold rb:text-[28px] rb:leading-12 rb:rounded-xl rb:text-white rb:bg-[#155EEF]">{name?.[0]}</div>
|
||||
<div className="rb:text-[16px] rb:font-semibold rb:leading-6 rb:line-clamp-2 rb:flex-1">
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rb:flex rb:gap-2 rb:mb-2 rb:flex-wrap rb:mt-6.25">
|
||||
{tags?.map((tag, tagIndex) => (
|
||||
<span key={tag.tag} className="rb:rounded-[11px] rb:p-[0_8px] rb:leading-5.5 rb:border"
|
||||
style={{
|
||||
backgroundColor: `rgba(${tagColors[tagIndex % tagColors.length]}, 0.08)`,
|
||||
borderColor: `rgba(${tagColors[tagIndex % tagColors.length]}, 0.3)`,
|
||||
color: `rgba(${tagColors[tagIndex % tagColors.length]}, 1)`,
|
||||
}}
|
||||
>
|
||||
{tag.tag}({tag.frequency})
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Total Memory */}
|
||||
<div className="rb:font-regular rb:text-[12px] rb:text-[#5B6167] rb:leading-4 rb:mb-6.25">
|
||||
{t('userMemory.totalNumOfMemories')}
|
||||
<div className="rb:font-extrabold rb:text-[24px] rb:text-[#212332] rb:leading-7.5 rb:mt-2">{memory || 0}</div>
|
||||
</div>
|
||||
</Flex>
|
||||
|
||||
{/* About Me */}
|
||||
<>
|
||||
<Title
|
||||
type="aboutUs"
|
||||
title={t('userMemory.aboutMe')}
|
||||
icon={aboutUs}
|
||||
t={t}
|
||||
expanded={expanded.includes('aboutUs')}
|
||||
onClick={handleTitleClick}
|
||||
/>
|
||||
{expanded.includes('aboutUs') && (
|
||||
<>
|
||||
{loading.summary
|
||||
? <Skeleton className="rb:mt-4" />
|
||||
: summary
|
||||
? <div className="rb:font-regular rb:leading-5.5 rb:pt-4">
|
||||
{summary || '-'}
|
||||
</div>
|
||||
: <Empty size={88} className="rb:mt-12 rb:mb-20.25" />
|
||||
}
|
||||
</>
|
||||
)}
|
||||
<div className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-2.5 rb:px-3 rb:mb-4">
|
||||
{loading.summary
|
||||
? <Skeleton />
|
||||
: summary
|
||||
? <div className="rb:leading-5 rb:text-[#5B6167]">
|
||||
{summary || '-'}
|
||||
</div>
|
||||
: <Empty size={88} />
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
{/* Memory Insights */}
|
||||
<>
|
||||
<Title
|
||||
type="memoryInsight"
|
||||
title={t('userMemory.memoryInsight')}
|
||||
icon={interestDistribution}
|
||||
t={t}
|
||||
expanded={expanded.includes('memoryInsight')}
|
||||
onClick={handleTitleClick}
|
||||
icon={memoryInsight}
|
||||
/>
|
||||
{expanded.includes('memoryInsight') && (
|
||||
<>
|
||||
{loading.insight
|
||||
? <Skeleton className="rb:mt-4" />
|
||||
: insight
|
||||
? <div className="rb:font-regular rb:leading-5.5 rb:pt-4">
|
||||
{insight || '-'}
|
||||
</div>
|
||||
: <Empty size={88} className="rb:mt-12 rb:mb-20.25" />
|
||||
}
|
||||
</>
|
||||
)}
|
||||
<div className="rb:bg-[#F6F6F6] rb:rounded-lg rb:py-2.5 rb:px-3">
|
||||
{loading.insight
|
||||
? <Skeleton />
|
||||
: insight
|
||||
? <div className="rb:leading-5 rb:text-[#5B6167]">
|
||||
{insight || '-'}
|
||||
</div>
|
||||
: <Empty size={88} />
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
</RbCard>
|
||||
</Col>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 18:33:44
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 18:33:44
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 11:55:42
|
||||
*/
|
||||
import { type FC, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import { Flex } from 'antd';
|
||||
|
||||
import Empty from '@/components/Empty'
|
||||
import Loading from '@/components/Empty/Loading'
|
||||
@@ -63,12 +64,12 @@ const EmotionLine: FC<EmotionLineProps> = ({ chartData, loading }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{t('userMemory.emotionLine')}</div>
|
||||
<Flex vertical gap={16} className="rb-border rb:rounded-xl rb:p-4! rb:h-78">
|
||||
<div className="rb:text-[#212332] rb:font-medium rb:leading-5">{t('userMemory.emotionLine')}</div>
|
||||
{loading
|
||||
? <Loading size={249} />
|
||||
: !chartData || chartData.length === 0
|
||||
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
|
||||
? <Empty size={120} className="rb:flex-1" />
|
||||
: <ReactEcharts
|
||||
ref={chartRef}
|
||||
option={{
|
||||
@@ -175,12 +176,12 @@ const EmotionLine: FC<EmotionLineProps> = ({ chartData, loading }) => {
|
||||
},
|
||||
series: getSeries()
|
||||
}}
|
||||
style={{ height: '265px', width: '100%', minWidth: '100%' }}
|
||||
style={{ height: '242px', width: '100%', minWidth: '100%' }}
|
||||
notMerge={true}
|
||||
lazyUpdate={true}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ const Habits = forwardRef<{ handleRefresh: () => void; }>((_props, ref) => {
|
||||
title={() => (<Space size={4}>
|
||||
{t('implicitDetail.habits')}
|
||||
<Tooltip title={t('implicitDetail.habitsSubTitle')}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/userMemory/question.svg')]"></div>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/userMemory/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Space>)}
|
||||
headerType="borderless"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* @Author: ZhaoYing
|
||||
* @Date: 2026-02-03 18:32:57
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-02-03 18:32:57
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-19 11:56:49
|
||||
*/
|
||||
import { type FC, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactEcharts from 'echarts-for-react'
|
||||
import { Flex } from 'antd'
|
||||
|
||||
import Empty from '@/components/Empty'
|
||||
import Loading from '@/components/Empty/Loading'
|
||||
@@ -41,12 +42,12 @@ const InteractionBar: FC<InteractionBarProps> = ({ chartData, loading }) => {
|
||||
}, [chartData, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{t('userMemory.interaction')}</div>
|
||||
<Flex vertical gap={16} className="rb-border rb:rounded-xl rb:p-4! rb:h-78">
|
||||
<div className="rb:text-[#212332] rb:font-medium rb:leading-5">{t('userMemory.emotionLine')}</div>
|
||||
{loading
|
||||
? <Loading size={249} />
|
||||
: !chartData || chartData.length === 0
|
||||
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
|
||||
? <Empty size={120} className="rb:flex-1" />
|
||||
: <ReactEcharts
|
||||
option={{
|
||||
color: Colors,
|
||||
@@ -128,10 +129,10 @@ const InteractionBar: FC<InteractionBarProps> = ({ chartData, loading }) => {
|
||||
},
|
||||
series
|
||||
}}
|
||||
style={{ height: '265px', width: '100%' }}
|
||||
style={{ height: '242px', width: '100%', minWidth: '100%' }}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, forwardRef, useImperativeHandle, useMemo, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { Row, Col, Tabs, Space, Skeleton } from 'antd'
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom'
|
||||
import { Row, Col, Flex, Space, Skeleton, Button } from 'antd'
|
||||
|
||||
import { getRelationshipEvolution, getTimelineMemories } from '@/api/memory'
|
||||
import type { Node, GraphDetailRef } from '../types'
|
||||
@@ -11,7 +11,8 @@ import { formatDateTime } from '@/utils/format'
|
||||
import Tag from '@/components/Tag'
|
||||
import InteractionBar from '../components/InteractionBar'
|
||||
import Empty from '@/components/Empty'
|
||||
import PageHeader from '../components/PageHeader'
|
||||
import PageHeader from '@/components/Layout/PageHeader'
|
||||
import BtnTabs from '@/components/BtnTabs'
|
||||
|
||||
export interface Emotion {
|
||||
emotion_intensity: number;
|
||||
@@ -36,6 +37,7 @@ interface Timeline {
|
||||
|
||||
const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const [searchParams] = useSearchParams()
|
||||
const [vo, setVo] = useState<Node | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -97,54 +99,75 @@ const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
|
||||
return (
|
||||
<>
|
||||
<PageHeader
|
||||
name={vo?.name}
|
||||
source="node"
|
||||
title={vo?.name}
|
||||
extra={
|
||||
<Space size={12}>
|
||||
<Button
|
||||
className="rb:px-2! rb:gap-0.5!"
|
||||
icon={<div className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/common/return.svg')]"></div>}
|
||||
onClick={() => navigate(-1)}
|
||||
>
|
||||
{t('common.return')}
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
<div className="rb:h-full rb:max-w-266 rb:mx-auto">
|
||||
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3">{t('userMemory.relationshipEvolution')}</div>
|
||||
<RbCard>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Row gutter={12} className="rb:p-3! rb:pr-0! rb:h-[calc(100vh-64px)] rb:w-full! rb:flex-nowrap! rb:overflow-hidden!">
|
||||
<Col flex="480px">
|
||||
<RbCard
|
||||
title={t('userMemory.relationshipEvolution')}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:min-h-[56px]! rb:font-[MiSans-Bold] rb:font-bold"
|
||||
bodyClassName="rb:p-3! rb:pt-0! rb:h-[calc(100%-56px)] rb:overflow-y-auto!"
|
||||
className="rb:h-[calc(100vh-88px)]!"
|
||||
>
|
||||
<Flex vertical gap={16}>
|
||||
<EmotionLine chartData={emotionData} loading={loading} />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<InteractionBar chartData={interactionData} loading={loading} />
|
||||
</Col>
|
||||
</Row>
|
||||
</RbCard>
|
||||
|
||||
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3 rb:mt-6">{t('userMemory.timelineMemories')}</div>
|
||||
<RbCard>
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
items={['timelines_memory', 'Statement', 'MemorySummary'].map(key => ({
|
||||
label: t(`userMemory.${key}`),
|
||||
key
|
||||
}))}
|
||||
onChange={(key: string) => setActiveTab(key)}
|
||||
/>
|
||||
{timelineLoading
|
||||
? <Skeleton active />
|
||||
: !activeContent || activeContent.length === 0
|
||||
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
|
||||
: <Space size={16} direction="vertical" className="rb:w-full">
|
||||
{activeContent.map((vo, index) => (
|
||||
<RbCard
|
||||
key={index}
|
||||
headerType="borderL"
|
||||
headerClassName="rb:before:bg-[#155EEF]!"
|
||||
title={vo.text}
|
||||
>
|
||||
<div className="rb:text-[#A8A9AA] rb:text-[12px] rb:leading-4">{formatDateTime(vo.created_at)}</div>
|
||||
<Tag className="rb:mt-2">{vo.type}</Tag>
|
||||
</RbCard>
|
||||
))}
|
||||
</Space>
|
||||
}
|
||||
|
||||
|
||||
</RbCard>
|
||||
</div>
|
||||
</Flex>
|
||||
</RbCard>
|
||||
</Col>
|
||||
<Col className="rb:w-[calc(100%-480px)]!">
|
||||
<RbCard
|
||||
title={t('userMemory.timelineMemories')}
|
||||
headerType="borderless"
|
||||
headerClassName="rb:min-h-[53px]! rb:font-[MiSans-Bold] rb:font-bold"
|
||||
bodyClassName="rb:p-3! rb:pt-0!"
|
||||
className="rb:w-full!"
|
||||
>
|
||||
<BtnTabs
|
||||
className="rb:mb-4!"
|
||||
activeKey={activeTab}
|
||||
items={['timelines_memory', 'Statement', 'MemorySummary'].map(key => ({
|
||||
label: t(`userMemory.${key}`),
|
||||
key
|
||||
}))}
|
||||
onChange={(key: string) => setActiveTab(key)}
|
||||
/>
|
||||
<div className="rb:h-[calc(100vh-193px)] rb:overflow-y-auto">
|
||||
{timelineLoading
|
||||
? <Skeleton active />
|
||||
: !activeContent || activeContent.length === 0
|
||||
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
|
||||
: <Flex gap={12} vertical>
|
||||
{activeContent.map((vo, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="rb-border rb:rounded-xl rb:p-3"
|
||||
>
|
||||
<Flex align="center" justify="space-between">
|
||||
<div className="rb:text-[#5B6167] rb:text-[12px] rb:leading-4.5">{formatDateTime(vo.created_at)}</div>
|
||||
<Tag>{vo.type}</Tag>
|
||||
</Flex>
|
||||
<div className="rb:mt-3 rb:leading-5 rb:break-all">{vo.text}</div>
|
||||
</div>
|
||||
))}
|
||||
</Flex>
|
||||
}
|
||||
</div>
|
||||
</RbCard>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -110,7 +110,7 @@ const ShortTermDetail: FC = () => {
|
||||
title={() => (<Space size={4}>
|
||||
{t('shortTermDetail.shortTermTitle')}
|
||||
<Tooltip title={t('shortTermDetail.shortTermSubTitle')}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/userMemory/question.svg')]"></div>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/userMemory/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Space>)}
|
||||
headerType="borderless"
|
||||
@@ -194,7 +194,7 @@ const ShortTermDetail: FC = () => {
|
||||
title={() => (<Space size={4}>
|
||||
{t('shortTermDetail.longTermTitle')}
|
||||
<Tooltip title={t('shortTermDetail.longTermTitleSubTitle')}>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/userMemory/question.svg')]"></div>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/userMemory/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
</Space>)}
|
||||
headerType="borderless"
|
||||
|
||||
@@ -178,7 +178,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
|
||||
'rb:border-[#d1d5db] rb:bg-[#FCFCFD] rb:text-[#374151]': !data.isSelected
|
||||
})}
|
||||
>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/workflow/node_plus.png')]"></div>
|
||||
<div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/workflow/node_plus.png')]"></div>
|
||||
{data.label}
|
||||
</Flex>
|
||||
</Popover>
|
||||
|
||||
@@ -27,7 +27,7 @@ const NodeTools: FC<{ node: Node }> = ({
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'delete', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/common/delete_dark.svg')]"></div>, label: <Flex>{t('common.delete')}</Flex>},
|
||||
{ key: 'delete', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/delete_dark.svg')]"></div>, label: <Flex>{t('common.delete')}</Flex>},
|
||||
// { key: 'copy', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/copy_dark.svg')]"></div>, label: t('common.copy') }
|
||||
],
|
||||
onClick: handleClick
|
||||
|
||||
@@ -248,7 +248,7 @@ const CaseList: FC<CaseListProps> = ({
|
||||
<Form.Item name={[caseField.name, 'logical_operator']} noStyle >
|
||||
<Space size={2} className="rb:cursor-pointer rb:text-[#155EEF] rb:leading-4.5 rb:font-medium rb-border rb:py-px! rb:px-1! rb:rounded-sm" onClick={() => handleChangeLogicalOperator(caseIndex)}>
|
||||
{logicalOperator}
|
||||
<div className="rb:size-3 rb:bg-cover rb:bg-[url('src/assets/images/workflow/refresh_active.svg')]"></div>
|
||||
<div className="rb:size-3 rb:bg-cover rb:bg-[url('@/assets/images/workflow/refresh_active.svg')]"></div>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
@@ -114,7 +114,7 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
<Form.Item name={[parentName, 'logical_operator']} noStyle >
|
||||
<Space size={2} className="rb:cursor-pointer rb:text-[#155EEF] rb:leading-4.5 rb:font-medium rb-border rb:py-px! rb:px-1! rb:rounded-sm" onClick={handleChangeLogicalOperator}>
|
||||
{logicalOperator}
|
||||
<div className="rb:size-3 rb:bg-cover rb:bg-[url('src/assets/images/workflow/refresh_active.svg')]"></div>
|
||||
<div className="rb:size-3 rb:bg-cover rb:bg-[url('@/assets/images/workflow/refresh_active.svg')]"></div>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</div>
|
||||
|
||||
@@ -434,7 +434,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'delete', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('src/assets/images/common/delete_dark.svg')]"></div>, label: <Flex>{t('common.delete')}</Flex> },
|
||||
{ key: 'delete', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/delete_dark.svg')]"></div>, label: <Flex>{t('common.delete')}</Flex> },
|
||||
// { key: 'copy', icon: <div className="rb:size-4 rb:bg-cover rb:bg-[url('@/assets/images/common/copy_dark.svg')]"></div>, label: t('common.copy') }
|
||||
],
|
||||
onClick: handleClick
|
||||
@@ -834,7 +834,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
<Flex align="center" className="rb:font-medium rb:cursor-pointer" onClick={handleToggle}>
|
||||
{t('workflow.config.output')}
|
||||
<div
|
||||
className={clsx("rb:size-3 rb:bg-cover rb:bg-[url('src/assets/images/common/caret_right_outlined.svg')]", {
|
||||
className={clsx("rb:size-3 rb:bg-cover rb:bg-[url('@/assets/images/common/caret_right_outlined.svg')]", {
|
||||
'rb:rotate-90': !outputCollapsed
|
||||
})}
|
||||
></div>
|
||||
|
||||