@lobehub/lobehub 2.0.0-next.182 → 2.0.0-next.184
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +14 -0
- package/package.json +1 -1
- package/packages/builtin-tool-local-system/package.json +10 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/EditLocalFile/index.tsx +81 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/GlobLocalFiles/index.tsx +73 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/GrepContent/index.tsx +73 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/ReadLocalFile/index.tsx +81 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/RunCommand/index.tsx +80 -0
- package/packages/builtin-tool-local-system/src/client/Inspector/SearchLocalFiles/index.tsx +71 -0
- package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Inspector/index.ts +1 -2
- package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/index.ts +1 -2
- package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/ListFiles/Result.tsx +2 -1
- package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/index.ts +1 -2
- package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Streaming/index.ts +1 -2
- package/packages/builtin-tool-local-system/src/client/index.ts +20 -0
- package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/index.tsx +3 -3
- package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/index.tsx +3 -3
- package/src/app/[variants]/(main)/home/_layout/Body/Agent/index.tsx +3 -3
- package/src/app/[variants]/(main)/home/features/RecentPage/index.tsx +3 -2
- package/src/app/[variants]/(main)/home/features/RecentResource/index.tsx +3 -2
- package/src/app/[variants]/(main)/home/features/RecentTopic/index.tsx +3 -3
- package/src/components/NeuralNetworkLoading/index.tsx +181 -0
- package/src/libs/swr/index.ts +1 -8
- package/src/store/chat/slices/topic/action.test.ts +154 -0
- package/src/store/chat/slices/topic/action.ts +48 -4
- package/src/tools/inspectors.ts +6 -5
- package/src/tools/interventions.ts +5 -4
- package/src/tools/placeholders.ts +9 -7
- package/src/tools/renders.ts +5 -3
- package/src/tools/streamings.ts +6 -5
- package/src/tools/local-system/Inspector/EditLocalFile/index.tsx +0 -55
- package/src/tools/local-system/Inspector/GlobLocalFiles/index.tsx +0 -59
- package/src/tools/local-system/Inspector/GrepContent/index.tsx +0 -59
- package/src/tools/local-system/Inspector/ReadLocalFile/index.tsx +0 -55
- package/src/tools/local-system/Inspector/RunCommand/index.tsx +0 -66
- package/src/tools/local-system/Inspector/SearchLocalFiles/index.tsx +0 -59
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/EditLocalFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/MoveLocalFiles/MoveFileItem.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/MoveLocalFiles/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/RunCommand/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Intervention/WriteFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Placeholder/ListFiles.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Placeholder/SearchFiles.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/EditLocalFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/ListFiles/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/MoveLocalFiles/MoveFileItem.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/MoveLocalFiles/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/ReadLocalFile/ReadFileSkeleton.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/ReadLocalFile/ReadFileView.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/ReadLocalFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/RenameLocalFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/RunCommand/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/SearchFiles/Result.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/SearchFiles/SearchQuery/SearchView.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/SearchFiles/SearchQuery/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/SearchFiles/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Render/WriteFile/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/Streaming/RunCommand/index.tsx +0 -0
- /package/{src/tools/local-system → packages/builtin-tool-local-system/src/client}/components/FileItem.tsx +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { AccordionItem,
|
|
4
|
-
import { Loader2Icon } from 'lucide-react';
|
|
3
|
+
import { AccordionItem, Dropdown, Flexbox, Text } from '@lobehub/ui';
|
|
5
4
|
import React, { Suspense, memo } from 'react';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
6
|
|
|
7
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
8
8
|
import SkeletonList from '@/features/NavPanel/components/SkeletonList';
|
|
9
9
|
import { useFetchTopics } from '@/hooks/useFetchTopics';
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
@@ -45,7 +45,7 @@ const Topic = memo<TopicProps>(({ itemKey }) => {
|
|
|
45
45
|
<Text ellipsis fontSize={12} type={'secondary'} weight={500}>
|
|
46
46
|
{`${t('title')} ${topicCount > 0 ? topicCount : ''}`}
|
|
47
47
|
</Text>
|
|
48
|
-
{isRevalidating && <
|
|
48
|
+
{isRevalidating && <NeuralNetworkLoading size={14} />}
|
|
49
49
|
</Flexbox>
|
|
50
50
|
}
|
|
51
51
|
>
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { AccordionItem,
|
|
4
|
-
import { Loader2Icon } from 'lucide-react';
|
|
3
|
+
import { AccordionItem, Dropdown, Flexbox, Text } from '@lobehub/ui';
|
|
5
4
|
import React, { Suspense, memo, useCallback } from 'react';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
6
|
|
|
7
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
8
8
|
import { useFetchAgentList } from '@/hooks/useFetchAgentList';
|
|
9
9
|
|
|
10
10
|
import SkeletonList from '../../../../../../../features/NavPanel/components/SkeletonList';
|
|
@@ -56,7 +56,7 @@ const Agent = memo<AgentProps>(({ itemKey }) => {
|
|
|
56
56
|
<Text ellipsis fontSize={12} type={'secondary'} weight={500}>
|
|
57
57
|
{t('navPanel.agent')}
|
|
58
58
|
</Text>
|
|
59
|
-
{isRevalidating && <
|
|
59
|
+
{isRevalidating && <NeuralNetworkLoading size={14} />}
|
|
60
60
|
</Flexbox>
|
|
61
61
|
}
|
|
62
62
|
>
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ActionIcon, Dropdown } from '@lobehub/ui';
|
|
4
|
-
import { FileTextIcon,
|
|
4
|
+
import { FileTextIcon, MoreHorizontal } from 'lucide-react';
|
|
5
5
|
import { Suspense, memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { useNavigate } from 'react-router-dom';
|
|
8
8
|
|
|
9
9
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
10
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
10
11
|
import { useInitRecentPage } from '@/hooks/useInitRecentPage';
|
|
11
12
|
import { useHomeStore } from '@/store/home';
|
|
12
13
|
import { homeRecentSelectors } from '@/store/home/selectors';
|
|
@@ -35,7 +36,7 @@ const RecentPage = memo(() => {
|
|
|
35
36
|
<GroupBlock
|
|
36
37
|
action={
|
|
37
38
|
<>
|
|
38
|
-
{isRevalidating && <
|
|
39
|
+
{isRevalidating && <NeuralNetworkLoading size={14} />}
|
|
39
40
|
<Dropdown
|
|
40
41
|
menu={{
|
|
41
42
|
items: [
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ActionIcon, Dropdown } from '@lobehub/ui';
|
|
4
|
-
import { Clock,
|
|
4
|
+
import { Clock, MoreHorizontal } from 'lucide-react';
|
|
5
5
|
import { Suspense, memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { useNavigate } from 'react-router-dom';
|
|
8
8
|
|
|
9
9
|
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
|
|
10
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
10
11
|
import { useInitRecentResource } from '@/hooks/useInitRecentResource';
|
|
11
12
|
import { useHomeStore } from '@/store/home';
|
|
12
13
|
import { homeRecentSelectors } from '@/store/home/selectors';
|
|
@@ -35,7 +36,7 @@ const RecentResource = memo(() => {
|
|
|
35
36
|
<GroupBlock
|
|
36
37
|
action={
|
|
37
38
|
<>
|
|
38
|
-
{isRevalidating && <
|
|
39
|
+
{isRevalidating && <NeuralNetworkLoading size={14} />}
|
|
39
40
|
<Dropdown
|
|
40
41
|
menu={{
|
|
41
42
|
items: [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BotMessageSquareIcon, Loader2Icon } from 'lucide-react';
|
|
1
|
+
import { BotMessageSquareIcon } from 'lucide-react';
|
|
3
2
|
import { Suspense, memo } from 'react';
|
|
4
3
|
import { useTranslation } from 'react-i18next';
|
|
5
4
|
|
|
5
|
+
import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
|
|
6
6
|
import { useInitRecentTopic } from '@/hooks/useInitRecentTopic';
|
|
7
7
|
import { useHomeStore } from '@/store/home';
|
|
8
8
|
import { homeRecentSelectors } from '@/store/home/selectors';
|
|
@@ -26,7 +26,7 @@ const RecentTopic = memo(() => {
|
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
28
|
<GroupBlock
|
|
29
|
-
action={isRevalidating && <
|
|
29
|
+
action={isRevalidating && <NeuralNetworkLoading size={14} />}
|
|
30
30
|
icon={BotMessageSquareIcon}
|
|
31
31
|
title={t('topic.recent')}
|
|
32
32
|
>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { createStaticStyles, keyframes } from 'antd-style';
|
|
4
|
+
import { CSSProperties, memo } from 'react';
|
|
5
|
+
|
|
6
|
+
const pulseAnim = keyframes`
|
|
7
|
+
0%, 100% {
|
|
8
|
+
opacity: 0.3;
|
|
9
|
+
}
|
|
10
|
+
50% {
|
|
11
|
+
opacity: 1;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
const flowAnim = keyframes`
|
|
16
|
+
0% {
|
|
17
|
+
transform: translateX(0);
|
|
18
|
+
opacity: 0.5;
|
|
19
|
+
}
|
|
20
|
+
50% {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
100% {
|
|
24
|
+
transform: translateX(var(--flow-distance));
|
|
25
|
+
opacity: 0.5;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const rotateAnim = keyframes`
|
|
30
|
+
100% {
|
|
31
|
+
transform: rotate(360deg);
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const scaleAnim = keyframes`
|
|
36
|
+
0%, 100% {
|
|
37
|
+
transform: scale(0.8);
|
|
38
|
+
opacity: 0.5;
|
|
39
|
+
}
|
|
40
|
+
50% {
|
|
41
|
+
transform: scale(1);
|
|
42
|
+
opacity: 1;
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const styles = createStaticStyles(({ css, cssVar }) => ({
|
|
47
|
+
center: css`
|
|
48
|
+
fill: ${cssVar.colorTextSecondary};
|
|
49
|
+
animation: ${scaleAnim} 2s infinite;
|
|
50
|
+
`,
|
|
51
|
+
|
|
52
|
+
connection: css`
|
|
53
|
+
opacity: 0.3;
|
|
54
|
+
stroke: ${cssVar.colorTextSecondary};
|
|
55
|
+
stroke-width: 0.5;
|
|
56
|
+
`,
|
|
57
|
+
|
|
58
|
+
container: css`
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
`,
|
|
63
|
+
|
|
64
|
+
node: css`
|
|
65
|
+
fill: ${cssVar.colorTextSecondary};
|
|
66
|
+
animation: ${pulseAnim} 2s infinite;
|
|
67
|
+
`,
|
|
68
|
+
|
|
69
|
+
particle: css`
|
|
70
|
+
fill: ${cssVar.colorTextSecondary};
|
|
71
|
+
animation: ${flowAnim} 2s infinite;
|
|
72
|
+
`,
|
|
73
|
+
|
|
74
|
+
ring: css`
|
|
75
|
+
transform-origin: center;
|
|
76
|
+
|
|
77
|
+
fill: none;
|
|
78
|
+
stroke: ${cssVar.colorFill};
|
|
79
|
+
stroke-dasharray: 0 8;
|
|
80
|
+
stroke-width: 1;
|
|
81
|
+
|
|
82
|
+
animation: ${rotateAnim} 20s infinite linear;
|
|
83
|
+
`,
|
|
84
|
+
|
|
85
|
+
svg: css`
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: 100%;
|
|
88
|
+
`,
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
interface NeuralNetworkLoadingProps {
|
|
92
|
+
size?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const NeuralNetworkLoading = memo<NeuralNetworkLoadingProps>(({ size = 16 }) => {
|
|
96
|
+
const nodeCount = 3;
|
|
97
|
+
const layerCount = 3;
|
|
98
|
+
|
|
99
|
+
// Generate nodes for each layer
|
|
100
|
+
const nodes = [];
|
|
101
|
+
for (let layerIndex = 0; layerIndex < layerCount; layerIndex++) {
|
|
102
|
+
for (let nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
|
|
103
|
+
const x = 25 + layerIndex * 25;
|
|
104
|
+
const y = 25 + nodeIndex * 25;
|
|
105
|
+
const delay = (layerIndex * nodeCount + nodeIndex) * 0.2;
|
|
106
|
+
nodes.push(
|
|
107
|
+
<circle
|
|
108
|
+
className={styles.node}
|
|
109
|
+
cx={x}
|
|
110
|
+
cy={y}
|
|
111
|
+
key={`node-${layerIndex}-${nodeIndex}`}
|
|
112
|
+
r="3"
|
|
113
|
+
style={{ animationDelay: `${delay}s` }}
|
|
114
|
+
/>,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Generate connections between layers
|
|
120
|
+
const connections = [];
|
|
121
|
+
for (let layerIndex = 0; layerIndex < layerCount - 1; layerIndex++) {
|
|
122
|
+
for (let nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
|
|
123
|
+
const x1 = 25 + layerIndex * 25;
|
|
124
|
+
const y1 = 25 + nodeIndex * 25;
|
|
125
|
+
for (let targetIndex = 0; targetIndex < nodeCount; targetIndex++) {
|
|
126
|
+
const x2 = 25 + (layerIndex + 1) * 25;
|
|
127
|
+
const y2 = 25 + targetIndex * 25;
|
|
128
|
+
connections.push(
|
|
129
|
+
<line
|
|
130
|
+
className={styles.connection}
|
|
131
|
+
key={`connection-${layerIndex}-${nodeIndex}-${targetIndex}`}
|
|
132
|
+
x1={x1}
|
|
133
|
+
x2={x2}
|
|
134
|
+
y1={y1}
|
|
135
|
+
y2={y2}
|
|
136
|
+
/>,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Generate particles
|
|
143
|
+
const particles = [0, 1, 2].map((index) => (
|
|
144
|
+
<circle
|
|
145
|
+
className={styles.particle}
|
|
146
|
+
cx={25}
|
|
147
|
+
cy={50}
|
|
148
|
+
key={`particle-${index}`}
|
|
149
|
+
r="1.5"
|
|
150
|
+
style={
|
|
151
|
+
{
|
|
152
|
+
'--flow-distance': '50px',
|
|
153
|
+
'animationDelay': `${index * 0.6}s`,
|
|
154
|
+
} as CSSProperties
|
|
155
|
+
}
|
|
156
|
+
/>
|
|
157
|
+
));
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<div className={styles.container} style={{ height: size, width: size }}>
|
|
161
|
+
<svg className={styles.svg} viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
162
|
+
{/* Connections */}
|
|
163
|
+
{connections}
|
|
164
|
+
|
|
165
|
+
{/* Nodes */}
|
|
166
|
+
{nodes}
|
|
167
|
+
|
|
168
|
+
{/* Particles */}
|
|
169
|
+
{particles}
|
|
170
|
+
|
|
171
|
+
{/* Central processing unit */}
|
|
172
|
+
<rect className={styles.center} height="6" width="6" x="47" y="47" />
|
|
173
|
+
|
|
174
|
+
{/* Rotating outer ring */}
|
|
175
|
+
<circle className={styles.ring} cx="50" cy="50" r="40" />
|
|
176
|
+
</svg>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export default NeuralNetworkLoading;
|
package/src/libs/swr/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import useSWR, { type SWRHook } from 'swr';
|
|
2
2
|
|
|
3
|
-
import { isDesktop } from '@/const/version';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* This type of request method is relatively flexible data, which will be triggered on the first time
|
|
@@ -27,13 +26,7 @@ export const useClientDataSWR: SWRHook = (key, fetch, config) =>
|
|
|
27
26
|
// Cause issue like this: https://github.com/lobehub/lobe-chat/issues/532
|
|
28
27
|
// we need to set it to 0.
|
|
29
28
|
dedupingInterval: 0,
|
|
30
|
-
focusThrottleInterval:
|
|
31
|
-
// FIXME: desktop 云同步模式也是走 edge 请求,也应该增大延迟
|
|
32
|
-
// desktop 1.5s
|
|
33
|
-
isDesktop
|
|
34
|
-
? 1500
|
|
35
|
-
: // web 300s
|
|
36
|
-
5 * 60 * 1000,
|
|
29
|
+
focusThrottleInterval: 5 * 60 * 1000,
|
|
37
30
|
// Custom error retry logic: don't retry on 401 errors
|
|
38
31
|
onErrorRetry: (error: any, key: any, config: any, revalidate: any, { retryCount }: any) => {
|
|
39
32
|
// Check if error is marked as non-retryable (e.g., 401 authentication errors)
|
|
@@ -345,6 +345,160 @@ describe('topic action', () => {
|
|
|
345
345
|
// Verify that the refreshMessages was called to update the messages
|
|
346
346
|
expect(refreshMessagesSpy).toHaveBeenCalled();
|
|
347
347
|
});
|
|
348
|
+
|
|
349
|
+
it('should support boolean as second parameter for backward compatibility', async () => {
|
|
350
|
+
const topicId = 'topic-id';
|
|
351
|
+
const { result } = renderHook(() => useChatStore());
|
|
352
|
+
|
|
353
|
+
const refreshMessagesSpy = vi.spyOn(result.current, 'refreshMessages');
|
|
354
|
+
|
|
355
|
+
// Call with boolean (old API)
|
|
356
|
+
await act(async () => {
|
|
357
|
+
await result.current.switchTopic(topicId, true);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(useChatStore.getState().activeTopicId).toBe(topicId);
|
|
361
|
+
// Should not call refreshMessages when skipRefreshMessage is true
|
|
362
|
+
expect(refreshMessagesSpy).not.toHaveBeenCalled();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should support options object as second parameter', async () => {
|
|
366
|
+
const topicId = 'topic-id';
|
|
367
|
+
const { result } = renderHook(() => useChatStore());
|
|
368
|
+
|
|
369
|
+
const refreshMessagesSpy = vi.spyOn(result.current, 'refreshMessages');
|
|
370
|
+
|
|
371
|
+
// Call with options object (new API)
|
|
372
|
+
await act(async () => {
|
|
373
|
+
await result.current.switchTopic(topicId, { skipRefreshMessage: true });
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
expect(useChatStore.getState().activeTopicId).toBe(topicId);
|
|
377
|
+
expect(refreshMessagesSpy).not.toHaveBeenCalled();
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should clear new key data when switching to new state (main scope)', async () => {
|
|
381
|
+
const { result } = renderHook(() => useChatStore());
|
|
382
|
+
const activeAgentId = 'test-agent-id';
|
|
383
|
+
const newKey = messageMapKey({ agentId: activeAgentId, topicId: null });
|
|
384
|
+
|
|
385
|
+
// Setup initial state with some messages in the new key
|
|
386
|
+
await act(async () => {
|
|
387
|
+
useChatStore.setState({
|
|
388
|
+
activeAgentId,
|
|
389
|
+
activeTopicId: 'existing-topic',
|
|
390
|
+
dbMessagesMap: {
|
|
391
|
+
[newKey]: [{ id: 'msg-1' }, { id: 'msg-2' }] as any,
|
|
392
|
+
},
|
|
393
|
+
messagesMap: {
|
|
394
|
+
[newKey]: [{ id: 'msg-1' }, { id: 'msg-2' }] as any,
|
|
395
|
+
},
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const replaceMessagesSpy = vi.spyOn(result.current, 'replaceMessages');
|
|
400
|
+
|
|
401
|
+
// Switch to new state (id = undefined)
|
|
402
|
+
await act(async () => {
|
|
403
|
+
await result.current.switchTopic(undefined, { skipRefreshMessage: true });
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Verify replaceMessages was called to clear the new key
|
|
407
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith([], {
|
|
408
|
+
context: {
|
|
409
|
+
agentId: activeAgentId,
|
|
410
|
+
groupId: undefined,
|
|
411
|
+
scope: 'main',
|
|
412
|
+
topicId: null,
|
|
413
|
+
},
|
|
414
|
+
action: expect.any(String),
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Verify activeTopicId is now null
|
|
418
|
+
expect(useChatStore.getState().activeTopicId).toBeNull();
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should clear new key data when switching to new state (group scope)', async () => {
|
|
422
|
+
const { result } = renderHook(() => useChatStore());
|
|
423
|
+
const activeAgentId = 'test-agent-id';
|
|
424
|
+
const activeGroupId = 'test-group-id';
|
|
425
|
+
|
|
426
|
+
// Setup initial state with group context
|
|
427
|
+
await act(async () => {
|
|
428
|
+
useChatStore.setState({
|
|
429
|
+
activeAgentId,
|
|
430
|
+
activeGroupId,
|
|
431
|
+
activeTopicId: 'existing-topic',
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const replaceMessagesSpy = vi.spyOn(result.current, 'replaceMessages');
|
|
436
|
+
|
|
437
|
+
// Switch to new state
|
|
438
|
+
await act(async () => {
|
|
439
|
+
await result.current.switchTopic(undefined, { skipRefreshMessage: true });
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Verify replaceMessages was called with group scope
|
|
443
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith([], {
|
|
444
|
+
context: {
|
|
445
|
+
agentId: activeAgentId,
|
|
446
|
+
groupId: activeGroupId,
|
|
447
|
+
scope: 'group',
|
|
448
|
+
topicId: null,
|
|
449
|
+
},
|
|
450
|
+
action: expect.any(String),
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should use explicit scope from options when provided', async () => {
|
|
455
|
+
const { result } = renderHook(() => useChatStore());
|
|
456
|
+
const activeAgentId = 'test-agent-id';
|
|
457
|
+
|
|
458
|
+
await act(async () => {
|
|
459
|
+
useChatStore.setState({
|
|
460
|
+
activeAgentId,
|
|
461
|
+
activeTopicId: 'existing-topic',
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const replaceMessagesSpy = vi.spyOn(result.current, 'replaceMessages');
|
|
466
|
+
|
|
467
|
+
// Switch to new state with explicit scope
|
|
468
|
+
await act(async () => {
|
|
469
|
+
await result.current.switchTopic(undefined, { skipRefreshMessage: true, scope: 'group' });
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Verify replaceMessages was called with explicit scope
|
|
473
|
+
expect(replaceMessagesSpy).toHaveBeenCalledWith([], {
|
|
474
|
+
context: expect.objectContaining({
|
|
475
|
+
scope: 'group',
|
|
476
|
+
}),
|
|
477
|
+
action: expect.any(String),
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('should not clear new key data when switching to an existing topic', async () => {
|
|
482
|
+
const { result } = renderHook(() => useChatStore());
|
|
483
|
+
const activeAgentId = 'test-agent-id';
|
|
484
|
+
|
|
485
|
+
await act(async () => {
|
|
486
|
+
useChatStore.setState({
|
|
487
|
+
activeAgentId,
|
|
488
|
+
activeTopicId: undefined,
|
|
489
|
+
});
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
const replaceMessagesSpy = vi.spyOn(result.current, 'replaceMessages');
|
|
493
|
+
|
|
494
|
+
// Switch to an existing topic (not new state)
|
|
495
|
+
await act(async () => {
|
|
496
|
+
await result.current.switchTopic('existing-topic-id', { skipRefreshMessage: true });
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// replaceMessages should not be called when switching to existing topic
|
|
500
|
+
expect(replaceMessagesSpy).not.toHaveBeenCalled();
|
|
501
|
+
});
|
|
348
502
|
});
|
|
349
503
|
describe('removeSessionTopics', () => {
|
|
350
504
|
it('should remove all topics from the current session and refresh the topic list', async () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Note: To make the code more logic and readable, we just disable the auto sort key eslint rule
|
|
3
3
|
// DON'T REMOVE THE FIRST LINE
|
|
4
4
|
import { chainSummaryTitle } from '@lobechat/prompts';
|
|
5
|
-
import { TraceNameMap, type UIChatMessage } from '@lobechat/types';
|
|
5
|
+
import { type MessageMapScope, TraceNameMap, type UIChatMessage } from '@lobechat/types';
|
|
6
6
|
import isEqual from 'fast-deep-equal';
|
|
7
7
|
import { t } from 'i18next';
|
|
8
8
|
import useSWR, { type SWRResponse } from 'swr';
|
|
@@ -34,6 +34,22 @@ const n = setNamespace('t');
|
|
|
34
34
|
const SWR_USE_FETCH_TOPIC = 'SWR_USE_FETCH_TOPIC';
|
|
35
35
|
const SWR_USE_SEARCH_TOPIC = 'SWR_USE_SEARCH_TOPIC';
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Options for switchTopic action
|
|
39
|
+
*/
|
|
40
|
+
export interface SwitchTopicOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Explicit scope for clearing new key data
|
|
43
|
+
* If not provided, will be inferred from store state (activeGroupId)
|
|
44
|
+
*/
|
|
45
|
+
scope?: MessageMapScope;
|
|
46
|
+
/**
|
|
47
|
+
* Skip refreshing messages after switching topic
|
|
48
|
+
* @default false
|
|
49
|
+
*/
|
|
50
|
+
skipRefreshMessage?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
37
53
|
export interface ChatTopicAction {
|
|
38
54
|
closeAllTopicsDrawer: () => void;
|
|
39
55
|
favoriteTopic: (id: string, favState: boolean) => Promise<void>;
|
|
@@ -53,7 +69,12 @@ export interface ChatTopicAction {
|
|
|
53
69
|
autoRenameTopicTitle: (id: string) => Promise<void>;
|
|
54
70
|
duplicateTopic: (id: string) => Promise<void>;
|
|
55
71
|
summaryTopicTitle: (topicId: string, messages: UIChatMessage[]) => Promise<void>;
|
|
56
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Switch to a topic or create new topic state
|
|
74
|
+
* @param id - Topic ID to switch to, or undefined/null to switch to "new" state
|
|
75
|
+
* @param options - Options object or boolean for backward compatibility (skipRefreshMessage)
|
|
76
|
+
*/
|
|
77
|
+
switchTopic: (id?: string, options?: boolean | SwitchTopicOptions) => Promise<void>;
|
|
57
78
|
updateTopicTitle: (id: string, title: string) => Promise<void>;
|
|
58
79
|
useFetchTopics: (
|
|
59
80
|
enable: boolean,
|
|
@@ -423,14 +444,37 @@ export const chatTopic: StateCreator<
|
|
|
423
444
|
},
|
|
424
445
|
),
|
|
425
446
|
|
|
426
|
-
switchTopic: async (id,
|
|
447
|
+
switchTopic: async (id, options) => {
|
|
448
|
+
// Backward compatibility: support both boolean and options object
|
|
449
|
+
const opts: SwitchTopicOptions =
|
|
450
|
+
typeof options === 'boolean' ? { skipRefreshMessage: options } : (options ?? {});
|
|
451
|
+
|
|
452
|
+
const { activeAgentId, activeGroupId } = get();
|
|
453
|
+
|
|
454
|
+
// When switching to "new" state (id is undefined/null), clear the new key data
|
|
455
|
+
// This prevents stale data from previous conversations showing up
|
|
456
|
+
if (!id && activeAgentId) {
|
|
457
|
+
// Determine scope: use explicit scope from options, or infer from activeGroupId
|
|
458
|
+
const scope = opts.scope ?? (activeGroupId ? 'group' : 'main');
|
|
459
|
+
|
|
460
|
+
get().replaceMessages([], {
|
|
461
|
+
context: {
|
|
462
|
+
agentId: activeAgentId,
|
|
463
|
+
groupId: activeGroupId,
|
|
464
|
+
scope,
|
|
465
|
+
topicId: null,
|
|
466
|
+
},
|
|
467
|
+
action: n('clearNewKeyData'),
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
427
471
|
set(
|
|
428
472
|
{ activeTopicId: !id ? (null as any) : id, activeThreadId: undefined },
|
|
429
473
|
false,
|
|
430
474
|
n('toggleTopic'),
|
|
431
475
|
);
|
|
432
476
|
|
|
433
|
-
if (skipRefreshMessage) return;
|
|
477
|
+
if (opts.skipRefreshMessage) return;
|
|
434
478
|
await get().refreshMessages();
|
|
435
479
|
},
|
|
436
480
|
// delete
|
package/src/tools/inspectors.ts
CHANGED
|
@@ -7,16 +7,17 @@ import {
|
|
|
7
7
|
GroupManagementManifest,
|
|
8
8
|
} from '@lobechat/builtin-tool-group-management/client';
|
|
9
9
|
import { GTDInspectors, GTDManifest } from '@lobechat/builtin-tool-gtd/client';
|
|
10
|
-
import {
|
|
11
|
-
|
|
10
|
+
import {
|
|
11
|
+
LocalSystemInspectors,
|
|
12
|
+
LocalSystemManifest,
|
|
13
|
+
} from '@lobechat/builtin-tool-local-system/client';
|
|
14
|
+
import { PageAgentInspectors, PageAgentManifest } from '@lobechat/builtin-tool-page-agent/client';
|
|
12
15
|
import {
|
|
13
16
|
WebBrowsingInspectors,
|
|
14
17
|
WebBrowsingManifest,
|
|
15
18
|
} from '@lobechat/builtin-tool-web-browsing/client';
|
|
16
19
|
import { type BuiltinInspector } from '@lobechat/types';
|
|
17
20
|
|
|
18
|
-
import { LocalSystemInspectors } from './local-system/Inspector';
|
|
19
|
-
|
|
20
21
|
/**
|
|
21
22
|
* Builtin tools inspector registry
|
|
22
23
|
* Organized by toolset (identifier) -> API name
|
|
@@ -32,7 +33,7 @@ const BuiltinToolInspectors: Record<string, Record<string, BuiltinInspector>> =
|
|
|
32
33
|
>,
|
|
33
34
|
[GTDManifest.identifier]: GTDInspectors as Record<string, BuiltinInspector>,
|
|
34
35
|
[LocalSystemManifest.identifier]: LocalSystemInspectors as Record<string, BuiltinInspector>,
|
|
35
|
-
[
|
|
36
|
+
[PageAgentManifest.identifier]: PageAgentInspectors as Record<string, BuiltinInspector>,
|
|
36
37
|
[WebBrowsingManifest.identifier]: WebBrowsingInspectors as Record<string, BuiltinInspector>,
|
|
37
38
|
};
|
|
38
39
|
|
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
GroupManagementManifest,
|
|
10
10
|
} from '@lobechat/builtin-tool-group-management/client';
|
|
11
11
|
import { GTDInterventions, GTDManifest } from '@lobechat/builtin-tool-gtd/client';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
LocalSystemIdentifier,
|
|
14
|
+
LocalSystemInterventions,
|
|
15
|
+
} from '@lobechat/builtin-tool-local-system/client';
|
|
13
16
|
import { NotebookManifest } from '@lobechat/builtin-tool-notebook';
|
|
14
17
|
import { NotebookInterventions } from '@lobechat/builtin-tool-notebook/client';
|
|
15
18
|
import { type BuiltinIntervention } from '@lobechat/types';
|
|
16
19
|
|
|
17
|
-
import { LocalSystemInterventions } from './local-system/Intervention';
|
|
18
|
-
|
|
19
20
|
/**
|
|
20
21
|
* Builtin tools interventions registry
|
|
21
22
|
* Organized by toolset (identifier) -> API name
|
|
@@ -26,7 +27,7 @@ export const BuiltinToolInterventions: Record<string, Record<string, any>> = {
|
|
|
26
27
|
[CloudSandboxManifest.identifier]: CloudSandboxInterventions,
|
|
27
28
|
[GroupManagementManifest.identifier]: GroupManagementInterventions,
|
|
28
29
|
[GTDManifest.identifier]: GTDInterventions,
|
|
29
|
-
[
|
|
30
|
+
[LocalSystemIdentifier]: LocalSystemInterventions,
|
|
30
31
|
[NotebookManifest.identifier]: NotebookInterventions,
|
|
31
32
|
};
|
|
32
33
|
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
LocalSystemApiName,
|
|
3
|
+
LocalSystemIdentifier,
|
|
4
|
+
LocalSystemListFilesPlaceholder,
|
|
5
|
+
LocalSystemSearchFilesPlaceholder,
|
|
6
|
+
} from '@lobechat/builtin-tool-local-system/client';
|
|
2
7
|
import {
|
|
3
8
|
WebBrowsingManifest,
|
|
4
9
|
WebBrowsingPlaceholders,
|
|
5
10
|
} from '@lobechat/builtin-tool-web-browsing/client';
|
|
6
11
|
import { type BuiltinPlaceholder } from '@lobechat/types';
|
|
7
12
|
|
|
8
|
-
import { ListFiles as LocalSystemListFiles } from './local-system/Placeholder/ListFiles';
|
|
9
|
-
import LocalSystemSearchFiles from './local-system/Placeholder/SearchFiles';
|
|
10
|
-
|
|
11
13
|
/**
|
|
12
14
|
* Builtin tools placeholders registry
|
|
13
15
|
* Organized by toolset (identifier) -> API name
|
|
14
16
|
*/
|
|
15
17
|
export const BuiltinToolPlaceholders: Record<string, Record<string, any>> = {
|
|
16
|
-
[
|
|
17
|
-
[LocalSystemApiName.searchLocalFiles]:
|
|
18
|
-
[LocalSystemApiName.listLocalFiles]:
|
|
18
|
+
[LocalSystemIdentifier]: {
|
|
19
|
+
[LocalSystemApiName.searchLocalFiles]: LocalSystemSearchFilesPlaceholder,
|
|
20
|
+
[LocalSystemApiName.listLocalFiles]: LocalSystemListFilesPlaceholder,
|
|
19
21
|
},
|
|
20
22
|
[WebBrowsingManifest.identifier]: WebBrowsingPlaceholders as Record<string, any>,
|
|
21
23
|
};
|
package/src/tools/renders.ts
CHANGED
|
@@ -10,7 +10,10 @@ import { GroupManagementRenders } from '@lobechat/builtin-tool-group-management/
|
|
|
10
10
|
// gtd
|
|
11
11
|
import { GTDManifest, GTDRenders } from '@lobechat/builtin-tool-gtd/client';
|
|
12
12
|
// local-system
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
LocalSystemIdentifier,
|
|
15
|
+
LocalSystemRenders,
|
|
16
|
+
} from '@lobechat/builtin-tool-local-system/client';
|
|
14
17
|
import { NotebookManifest, NotebookRenders } from '@lobechat/builtin-tool-notebook/client';
|
|
15
18
|
// web-browsing
|
|
16
19
|
import {
|
|
@@ -22,7 +25,6 @@ import { type BuiltinRender } from '@lobechat/types';
|
|
|
22
25
|
// knowledge-base
|
|
23
26
|
import { KnowledgeBaseManifest } from './knowledge-base';
|
|
24
27
|
import { KnowledgeBaseRenders } from './knowledge-base/Render';
|
|
25
|
-
import { LocalSystemRenders } from './local-system/Render';
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* Builtin tools renders registry
|
|
@@ -35,7 +37,7 @@ const BuiltinToolsRenders: Record<string, Record<string, BuiltinRender>> = {
|
|
|
35
37
|
[GTDManifest.identifier]: GTDRenders as Record<string, BuiltinRender>,
|
|
36
38
|
[NotebookManifest.identifier]: NotebookRenders as Record<string, BuiltinRender>,
|
|
37
39
|
[KnowledgeBaseManifest.identifier]: KnowledgeBaseRenders as Record<string, BuiltinRender>,
|
|
38
|
-
[
|
|
40
|
+
[LocalSystemIdentifier]: LocalSystemRenders as Record<string, BuiltinRender>,
|
|
39
41
|
[WebBrowsingManifest.identifier]: WebBrowsingRenders as Record<string, BuiltinRender>,
|
|
40
42
|
};
|
|
41
43
|
|