@lobehub/chat 0.42.3 → 0.44.0
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/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +74 -0
- package/package.json +5 -2
- package/src/const/meta.ts +3 -0
- package/src/const/session.ts +1 -0
- package/src/helpers/export.ts +15 -9
- package/src/locales/default/common.ts +8 -1
- package/src/pages/chat/SessionList/Inbox.tsx +23 -0
- package/src/pages/chat/SessionList/List/SessionItem.tsx +15 -4
- package/src/pages/chat/SessionList/List/index.tsx +3 -2
- package/src/pages/chat/SessionList/index.tsx +13 -1
- package/src/pages/chat/[id]/index.page.tsx +7 -9
- package/src/pages/chat/[id]/layout.tsx +7 -23
- package/src/pages/chat/[id]/setting/index.page.tsx +3 -3
- package/src/pages/chat/features/Conversation/ChatList/SkeletonList.tsx +43 -0
- package/src/pages/chat/{[id] → features}/Conversation/ChatList/index.tsx +35 -5
- package/src/pages/chat/{[id] → features}/Conversation/Input/index.tsx +1 -1
- package/src/pages/chat/{[id] → features}/Header/index.tsx +32 -34
- package/src/pages/chat/features/Sidebar/Inner.tsx +42 -0
- package/src/pages/chat/{[id]/Sidebar/Inner.tsx → features/Sidebar/SystemRole.tsx} +7 -37
- package/src/pages/chat/{[id] → features}/Sidebar/index.tsx +8 -4
- package/src/pages/chat/index.page.tsx +39 -1
- package/src/pages/chat/layout.tsx +27 -0
- package/src/pages/settings/Settings/Common.tsx +11 -2
- package/src/pages/welcome/Footer/index.tsx +1 -0
- package/src/services/chatModel.ts +1 -1
- package/src/store/global/hooks/useOnFinishHydrationGlobal.ts +18 -0
- package/src/store/global/hooks/useSwitchSideBarOnInit.ts +4 -8
- package/src/store/global/slices/settings.ts +2 -2
- package/src/store/session/hooks/index.ts +2 -0
- package/src/store/session/hooks/useOnFinishHydrationSession.ts +18 -0
- package/src/store/session/hooks/useSessionHydrated.ts +1 -1
- package/src/store/session/index.ts +1 -3
- package/src/store/session/initialState.ts +6 -3
- package/src/store/session/selectors.ts +3 -3
- package/src/store/session/slices/agentConfig/action.ts +2 -1
- package/src/store/session/slices/agentConfig/selectors.ts +11 -11
- package/src/store/session/slices/chat/actions/message.ts +13 -20
- package/src/store/session/slices/chat/actions/topic.ts +3 -1
- package/src/store/session/slices/chat/selectors/chat.ts +1 -1
- package/src/store/session/slices/chat/selectors/token.ts +1 -1
- package/src/store/session/slices/chat/selectors/topic.ts +4 -2
- package/src/store/session/slices/session/action.ts +29 -15
- package/src/store/session/slices/session/initialState.ts +18 -4
- package/src/store/session/slices/session/selectors/export.ts +13 -8
- package/src/store/session/slices/session/selectors/list.test.ts +303 -0
- package/src/store/session/slices/session/selectors/list.ts +8 -3
- package/src/store/session/store.ts +5 -5
- package/src/types/exportConfig.ts +93 -31
- package/src/utils/config.ts +9 -0
- package/src/pages/chat/SessionList/List/style.ts +0 -36
- package/src/store/session/slices/chat/index.ts +0 -3
- package/src/store/session/slices/session/index.ts +0 -3
- package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ApiKeyForm.tsx +0 -0
- package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ErrorActionContainer.tsx +0 -0
- package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/InvalidAccess.tsx +1 -1
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/OpenAiBizError.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/style.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/MessageExtra.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/OTPInput.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/FunctionCall.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginMessage.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginResultRender.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/style.ts +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/Input/Action.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/Input/ActionRight.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/Input/Token.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Conversation/index.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Header/PluginTag.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Sidebar/Header.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Sidebar/Topic/DefaultContent.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicContent.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicItem.tsx +0 -0
- /package/src/pages/chat/{[id] → features}/Sidebar/Topic/index.tsx +0 -0
|
@@ -2,12 +2,14 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
2
2
|
|
|
3
3
|
import { LOADING_FLAT } from '@/const/message';
|
|
4
4
|
import { promptSummaryTitle } from '@/prompts/chat';
|
|
5
|
-
import { SessionStore
|
|
5
|
+
import { SessionStore } from '@/store/session';
|
|
6
6
|
import { fetchPresetTaskResult } from '@/utils/fetch';
|
|
7
7
|
import { setNamespace } from '@/utils/storeDebug';
|
|
8
8
|
import { nanoid } from '@/utils/uuid';
|
|
9
9
|
|
|
10
|
+
import { sessionSelectors } from '../../session/selectors';
|
|
10
11
|
import { ChatTopicDispatch, topicReducer } from '../reducers/topic';
|
|
12
|
+
import { chatSelectors, topicSelectors } from '../selectors';
|
|
11
13
|
|
|
12
14
|
const t = setNamespace('chat/topic');
|
|
13
15
|
export interface ChatTopicAction {
|
|
@@ -4,7 +4,7 @@ import { ChatMessage } from '@/types/chatMessage';
|
|
|
4
4
|
|
|
5
5
|
import type { SessionStore } from '../../../store';
|
|
6
6
|
import { agentSelectors } from '../../agentConfig';
|
|
7
|
-
import { sessionSelectors } from '../../session';
|
|
7
|
+
import { sessionSelectors } from '../../session/selectors';
|
|
8
8
|
import { getSlicedMessagesWithConfig } from '../utils';
|
|
9
9
|
import { organizeChats } from './utils';
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { encode } from 'gpt-tokenizer';
|
|
2
2
|
|
|
3
|
-
import { agentSelectors } from '@/store/session';
|
|
3
|
+
import { agentSelectors } from '@/store/session/slices/agentConfig';
|
|
4
4
|
|
|
5
5
|
import type { SessionStore } from '../../../store';
|
|
6
6
|
import { currentChatsWithHistoryConfig } from './chat';
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// 展示在聊天框中的消息
|
|
2
|
-
import { SessionStore, sessionSelectors } from '@/store/session';
|
|
3
|
-
import { organizeChats } from '@/store/session/slices/chat/selectors/utils';
|
|
4
2
|
import { ChatTopic } from '@/types/topic';
|
|
5
3
|
|
|
4
|
+
import { SessionStore } from '../../../store';
|
|
5
|
+
import { sessionSelectors } from '../../session/selectors';
|
|
6
|
+
import { organizeChats } from './utils';
|
|
7
|
+
|
|
6
8
|
export const currentTopics = (s: SessionStore): ChatTopic[] => {
|
|
7
9
|
const session = sessionSelectors.currentSession(s);
|
|
8
10
|
if (!session) return [];
|
|
@@ -3,19 +3,21 @@ import { merge } from 'lodash-es';
|
|
|
3
3
|
import Router from 'next/router';
|
|
4
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
6
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
6
7
|
import { useGlobalStore } from '@/store/global';
|
|
7
|
-
import { SessionStore
|
|
8
|
+
import { SessionStore } from '@/store/session';
|
|
8
9
|
import { LobeAgentSession, LobeSessions } from '@/types/session';
|
|
9
10
|
import { setNamespace } from '@/utils/storeDebug';
|
|
10
11
|
import { uuid } from '@/utils/uuid';
|
|
11
12
|
|
|
13
|
+
import { initLobeSession } from './initialState';
|
|
12
14
|
import { SessionDispatch, sessionsReducer } from './reducers/session';
|
|
13
15
|
|
|
14
16
|
const t = setNamespace('session');
|
|
17
|
+
|
|
15
18
|
export interface SessionAction {
|
|
16
19
|
activeSession: (sessionId: string) => void;
|
|
17
20
|
clearSessions: () => void;
|
|
18
|
-
|
|
19
21
|
/**
|
|
20
22
|
* @title 添加会话
|
|
21
23
|
* @param session - 会话信息
|
|
@@ -27,7 +29,6 @@ export interface SessionAction {
|
|
|
27
29
|
* @param payload - 聊天记录
|
|
28
30
|
*/
|
|
29
31
|
dispatchSession: (payload: SessionDispatch) => void;
|
|
30
|
-
|
|
31
32
|
/**
|
|
32
33
|
* 导入会话
|
|
33
34
|
* @param sessions
|
|
@@ -39,6 +40,7 @@ export interface SessionAction {
|
|
|
39
40
|
* @param sessionId
|
|
40
41
|
*/
|
|
41
42
|
pinSession: (sessionId: string, pinned?: boolean) => void;
|
|
43
|
+
|
|
42
44
|
/**
|
|
43
45
|
* 生成压缩后的消息
|
|
44
46
|
* @returns 压缩后的消息
|
|
@@ -50,6 +52,7 @@ export interface SessionAction {
|
|
|
50
52
|
* @returns void
|
|
51
53
|
*/
|
|
52
54
|
removeSession: (sessionId: string) => void;
|
|
55
|
+
switchInbox: () => void;
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
58
|
* @title 切换会话
|
|
@@ -95,11 +98,21 @@ export const createSessionSlice: StateCreator<
|
|
|
95
98
|
|
|
96
99
|
dispatchSession: (payload) => {
|
|
97
100
|
const { type, ...res } = payload;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
|
|
102
|
+
// 如果是 inbox 类型的 session
|
|
103
|
+
if ('id' in res && res.id === INBOX_SESSION_ID) {
|
|
104
|
+
const nextInbox = sessionsReducer({ inbox: get().inbox }, payload) as {
|
|
105
|
+
inbox: LobeAgentSession;
|
|
106
|
+
};
|
|
107
|
+
set({ inbox: nextInbox.inbox }, false, t(`dispatchInbox/${type}`, res));
|
|
108
|
+
} else {
|
|
109
|
+
// 常规类型的session
|
|
110
|
+
set(
|
|
111
|
+
{ sessions: sessionsReducer(get().sessions, payload) },
|
|
112
|
+
false,
|
|
113
|
+
t(`dispatchSessions/${type}`, res),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
103
116
|
},
|
|
104
117
|
|
|
105
118
|
importSessions: (importSessions) => {
|
|
@@ -119,13 +132,7 @@ export const createSessionSlice: StateCreator<
|
|
|
119
132
|
t('importSessions', importSessions),
|
|
120
133
|
);
|
|
121
134
|
},
|
|
122
|
-
|
|
123
|
-
// const session = sessionSelectors.currentSession(get());
|
|
124
|
-
// if (!session) return '';
|
|
125
|
-
//
|
|
126
|
-
// const agent = session.config;
|
|
127
|
-
// return genShareMessagesUrl(session.chats, agent.systemRole);
|
|
128
|
-
// },
|
|
135
|
+
|
|
129
136
|
pinSession: (sessionId, pinned) => {
|
|
130
137
|
const nextValue = typeof pinned === 'boolean' ? pinned : !get().sessions[sessionId].pinned;
|
|
131
138
|
|
|
@@ -140,6 +147,13 @@ export const createSessionSlice: StateCreator<
|
|
|
140
147
|
}
|
|
141
148
|
},
|
|
142
149
|
|
|
150
|
+
switchInbox: () => {
|
|
151
|
+
if (get().activeId === INBOX_SESSION_ID) return;
|
|
152
|
+
get().activeSession(INBOX_SESSION_ID);
|
|
153
|
+
|
|
154
|
+
Router.push('/chat');
|
|
155
|
+
},
|
|
156
|
+
|
|
143
157
|
switchSession: async (sessionId) => {
|
|
144
158
|
if (get().activeId === sessionId) return;
|
|
145
159
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LobeAgentSession, LobeSessionType } from '@/types/session';
|
|
1
|
+
import { merge } from 'lodash-es';
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { DEFAULT_AGENT_META, DEFAULT_INBOX_AVATAR } from '@/const/meta';
|
|
4
|
+
import { LobeAgentConfig, LobeAgentSession, LobeSessionType } from '@/types/session';
|
|
5
|
+
|
|
6
|
+
import { initialLobeAgentConfig } from '../agentConfig/initialState';
|
|
5
7
|
|
|
6
8
|
export interface SessionState {
|
|
7
9
|
/**
|
|
@@ -10,6 +12,8 @@ export interface SessionState {
|
|
|
10
12
|
* @default null
|
|
11
13
|
*/
|
|
12
14
|
activeId: string | null;
|
|
15
|
+
// 默认会话
|
|
16
|
+
inbox: LobeAgentSession;
|
|
13
17
|
searchKeywords: string;
|
|
14
18
|
sessions: Record<string, LobeAgentSession>;
|
|
15
19
|
}
|
|
@@ -24,9 +28,19 @@ export const initLobeSession: LobeAgentSession = {
|
|
|
24
28
|
updateAt: Date.now(),
|
|
25
29
|
};
|
|
26
30
|
|
|
31
|
+
export const initInbox = merge({}, initLobeSession, {
|
|
32
|
+
config: {
|
|
33
|
+
systemRole: '你是一名 AI 助理',
|
|
34
|
+
} as LobeAgentConfig,
|
|
35
|
+
id: 'inbox',
|
|
36
|
+
meta: {
|
|
37
|
+
avatar: DEFAULT_INBOX_AVATAR,
|
|
38
|
+
},
|
|
39
|
+
} as Partial<LobeAgentSession>);
|
|
40
|
+
|
|
27
41
|
export const initialSessionState: SessionState = {
|
|
28
42
|
activeId: null,
|
|
29
|
-
|
|
43
|
+
inbox: initInbox,
|
|
30
44
|
searchKeywords: '',
|
|
31
45
|
sessions: {},
|
|
32
46
|
};
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { transform } from 'lodash-es';
|
|
2
2
|
|
|
3
3
|
import { SessionStore } from '@/store/session';
|
|
4
|
+
import { ConfigStateAgents, ConfigStateSessions } from '@/types/exportConfig';
|
|
4
5
|
import { LobeAgentSession, LobeSessions } from '@/types/session';
|
|
5
6
|
|
|
6
7
|
import { getSessionById } from './list';
|
|
7
8
|
|
|
8
|
-
export const exportSessions = (s: SessionStore) =>
|
|
9
|
+
export const exportSessions = (s: SessionStore): ConfigStateSessions => ({
|
|
10
|
+
inbox: s.inbox,
|
|
11
|
+
sessions: s.sessions,
|
|
12
|
+
});
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export const exportAgents = (s: SessionStore): ConfigStateAgents => {
|
|
15
|
+
return {
|
|
16
|
+
sessions: transform(s.sessions, (result: LobeSessions, value, key) => {
|
|
17
|
+
// 移除 chats 和 topics
|
|
18
|
+
result[key] = { ...value, chats: {}, topics: {} } as LobeAgentSession;
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
16
21
|
};
|
|
17
|
-
|
|
22
|
+
|
|
18
23
|
export const getExportAgent =
|
|
19
24
|
(id: string) =>
|
|
20
25
|
(s: SessionStore): LobeAgentSession => {
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import type { SessionStore } from '@/store/session';
|
|
2
|
+
import { LanguageModel } from '@/types/llm';
|
|
3
|
+
import { LobeAgentSession, LobeSessionType } from '@/types/session';
|
|
4
|
+
|
|
5
|
+
import { initLobeSession } from '../initialState';
|
|
6
|
+
import {
|
|
7
|
+
currentSession,
|
|
8
|
+
currentSessionSafe,
|
|
9
|
+
getSessionById,
|
|
10
|
+
getSessionMetaById,
|
|
11
|
+
hasSessionList,
|
|
12
|
+
sessionList,
|
|
13
|
+
} from './list';
|
|
14
|
+
|
|
15
|
+
describe('currentSession', () => {
|
|
16
|
+
const s = {
|
|
17
|
+
activeId: '1',
|
|
18
|
+
sessions: {
|
|
19
|
+
'1': {
|
|
20
|
+
id: '1',
|
|
21
|
+
config: {
|
|
22
|
+
model: LanguageModel.GPT3_5,
|
|
23
|
+
params: {},
|
|
24
|
+
systemRole: 'system-role',
|
|
25
|
+
},
|
|
26
|
+
type: LobeSessionType.Agent,
|
|
27
|
+
} as LobeAgentSession,
|
|
28
|
+
'2': {
|
|
29
|
+
id: '2',
|
|
30
|
+
config: {
|
|
31
|
+
model: LanguageModel.GPT3_5,
|
|
32
|
+
params: {},
|
|
33
|
+
systemRole: 'system-role',
|
|
34
|
+
},
|
|
35
|
+
type: LobeSessionType.Agent,
|
|
36
|
+
} as LobeAgentSession,
|
|
37
|
+
},
|
|
38
|
+
inbox: {
|
|
39
|
+
id: 'inbox',
|
|
40
|
+
config: {
|
|
41
|
+
model: LanguageModel.GPT3_5,
|
|
42
|
+
params: {},
|
|
43
|
+
systemRole: 'system-role',
|
|
44
|
+
},
|
|
45
|
+
type: LobeSessionType.Agent,
|
|
46
|
+
} as LobeAgentSession,
|
|
47
|
+
} as unknown as SessionStore;
|
|
48
|
+
|
|
49
|
+
it('should return undefined when s.activeId is not defined', () => {
|
|
50
|
+
expect(currentSession({ sessions: {} } as any)).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return s.inbox when s.activeId is equal to INBOX_SESSION_ID', () => {
|
|
54
|
+
expect(currentSession({ ...s, activeId: 'inbox' })).toEqual(s.inbox);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return s.sessions[s.activeId] when s.activeId is not equal to INBOX_SESSION_ID', () => {
|
|
58
|
+
expect(currentSession(s)).toEqual(s.sessions['1']);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('currentSessionSafe', () => {
|
|
63
|
+
const s = {
|
|
64
|
+
activeId: '1',
|
|
65
|
+
sessions: {
|
|
66
|
+
'1': {
|
|
67
|
+
id: '1',
|
|
68
|
+
config: {
|
|
69
|
+
model: LanguageModel.GPT3_5,
|
|
70
|
+
params: {},
|
|
71
|
+
systemRole: 'system-role',
|
|
72
|
+
},
|
|
73
|
+
type: LobeSessionType.Agent,
|
|
74
|
+
} as LobeAgentSession,
|
|
75
|
+
'2': {
|
|
76
|
+
id: '2',
|
|
77
|
+
config: {
|
|
78
|
+
model: LanguageModel.GPT3_5,
|
|
79
|
+
params: {},
|
|
80
|
+
systemRole: 'system-role',
|
|
81
|
+
},
|
|
82
|
+
type: LobeSessionType.Agent,
|
|
83
|
+
} as LobeAgentSession,
|
|
84
|
+
},
|
|
85
|
+
inbox: {
|
|
86
|
+
id: 'inbox',
|
|
87
|
+
config: {
|
|
88
|
+
model: LanguageModel.GPT3_5,
|
|
89
|
+
params: {},
|
|
90
|
+
systemRole: 'system-role',
|
|
91
|
+
},
|
|
92
|
+
type: LobeSessionType.Agent,
|
|
93
|
+
} as LobeAgentSession,
|
|
94
|
+
} as unknown as SessionStore;
|
|
95
|
+
|
|
96
|
+
it('should return initLobeSession when currentSession(s) returns undefined', () => {
|
|
97
|
+
expect(currentSessionSafe({ sessions: {} } as any)).toEqual(initLobeSession);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should return the result of currentSession(s) when it returns a non-undefined value', () => {
|
|
101
|
+
expect(currentSessionSafe(s)).toEqual(s.sessions['1']);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('sessionList', () => {
|
|
106
|
+
const s = {
|
|
107
|
+
sessions: {
|
|
108
|
+
'1': {
|
|
109
|
+
pinned: true,
|
|
110
|
+
updateAt: 100,
|
|
111
|
+
},
|
|
112
|
+
'2': {
|
|
113
|
+
pinned: false,
|
|
114
|
+
updateAt: 200,
|
|
115
|
+
},
|
|
116
|
+
'3': {
|
|
117
|
+
pinned: true,
|
|
118
|
+
updateAt: 300,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
} as unknown as SessionStore;
|
|
122
|
+
|
|
123
|
+
it('should return an array of sessions sorted by pinned and updateAt fields', () => {
|
|
124
|
+
expect(sessionList(s)).toEqual([s.sessions['3'], s.sessions['1'], s.sessions['2']]);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should return an empty array when s.sessions is an empty object', () => {
|
|
128
|
+
const result = sessionList({
|
|
129
|
+
sessions: {},
|
|
130
|
+
} as any);
|
|
131
|
+
expect(result).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should return a sorted session list when s.sessions is not empty object', () => {
|
|
135
|
+
const session1 = {
|
|
136
|
+
id: '1',
|
|
137
|
+
pinned: true,
|
|
138
|
+
updateAt: 1635705600000,
|
|
139
|
+
meta: {},
|
|
140
|
+
};
|
|
141
|
+
const session2 = {
|
|
142
|
+
id: '2',
|
|
143
|
+
pinned: false,
|
|
144
|
+
updateAt: 1635705600000,
|
|
145
|
+
meta: {},
|
|
146
|
+
};
|
|
147
|
+
const session3 = {
|
|
148
|
+
id: '3',
|
|
149
|
+
pinned: true,
|
|
150
|
+
updateAt: 1635705700000,
|
|
151
|
+
meta: {},
|
|
152
|
+
};
|
|
153
|
+
const s = {
|
|
154
|
+
sessions: {
|
|
155
|
+
'1': session1,
|
|
156
|
+
'2': session2,
|
|
157
|
+
'3': session3,
|
|
158
|
+
},
|
|
159
|
+
} as unknown as SessionStore;
|
|
160
|
+
const result = sessionList(s);
|
|
161
|
+
expect(result).toEqual([session3, session1, session2]);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should return a sorted session list when s.searchKeywords is not an empty string', () => {
|
|
165
|
+
const session1 = {
|
|
166
|
+
id: '1',
|
|
167
|
+
pinned: true,
|
|
168
|
+
meta: { title: 'yword' },
|
|
169
|
+
|
|
170
|
+
updateAt: 1635705600000,
|
|
171
|
+
};
|
|
172
|
+
const session2 = {
|
|
173
|
+
id: '2',
|
|
174
|
+
pinned: false,
|
|
175
|
+
meta: { title: 'keyword' },
|
|
176
|
+
updateAt: 1635705600000,
|
|
177
|
+
};
|
|
178
|
+
const session3 = {
|
|
179
|
+
id: '3',
|
|
180
|
+
pinned: true,
|
|
181
|
+
meta: { title: 'kyword' },
|
|
182
|
+
updateAt: 1635705800000,
|
|
183
|
+
};
|
|
184
|
+
const s = {
|
|
185
|
+
sessions: {
|
|
186
|
+
'1': session1,
|
|
187
|
+
'2': session2,
|
|
188
|
+
'3': session3,
|
|
189
|
+
},
|
|
190
|
+
searchKeywords: 'k',
|
|
191
|
+
} as unknown as SessionStore;
|
|
192
|
+
|
|
193
|
+
const result = sessionList(s);
|
|
194
|
+
expect(result).toEqual([session3, session2]);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should return an empty array when s.sessions is an empty object and s.searchKeywords is not an empty string', () => {
|
|
198
|
+
const result = sessionList({
|
|
199
|
+
sessions: {},
|
|
200
|
+
searchKeywords: 'keyword',
|
|
201
|
+
} as unknown as SessionStore);
|
|
202
|
+
expect(result).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
describe('hasSessionList', () => {
|
|
207
|
+
it('should return true when sessionList(s) returns a non-empty array', () => {
|
|
208
|
+
const s = {
|
|
209
|
+
sessions: {
|
|
210
|
+
'1': {},
|
|
211
|
+
},
|
|
212
|
+
} as unknown as SessionStore;
|
|
213
|
+
expect(hasSessionList(s)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should return false when sessionList(s) returns an empty array', () => {
|
|
217
|
+
const s = {
|
|
218
|
+
sessions: {},
|
|
219
|
+
} as unknown as SessionStore;
|
|
220
|
+
expect(hasSessionList(s)).toBe(false);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('getSessionById', () => {
|
|
225
|
+
const s = {
|
|
226
|
+
activeId: '1',
|
|
227
|
+
sessions: {
|
|
228
|
+
'1': {
|
|
229
|
+
id: '1',
|
|
230
|
+
config: {
|
|
231
|
+
model: LanguageModel.GPT3_5,
|
|
232
|
+
params: {},
|
|
233
|
+
systemRole: 'system-role',
|
|
234
|
+
},
|
|
235
|
+
type: LobeSessionType.Agent,
|
|
236
|
+
} as LobeAgentSession,
|
|
237
|
+
'2': {
|
|
238
|
+
id: '2',
|
|
239
|
+
config: {
|
|
240
|
+
model: LanguageModel.GPT3_5,
|
|
241
|
+
params: {},
|
|
242
|
+
systemRole: 'system-role',
|
|
243
|
+
},
|
|
244
|
+
type: LobeSessionType.Agent,
|
|
245
|
+
} as LobeAgentSession,
|
|
246
|
+
},
|
|
247
|
+
inbox: {
|
|
248
|
+
id: 'inbox',
|
|
249
|
+
config: {
|
|
250
|
+
model: LanguageModel.GPT3_5,
|
|
251
|
+
params: {},
|
|
252
|
+
systemRole: 'system-role',
|
|
253
|
+
},
|
|
254
|
+
type: LobeSessionType.Agent,
|
|
255
|
+
} as LobeAgentSession,
|
|
256
|
+
} as unknown as SessionStore;
|
|
257
|
+
|
|
258
|
+
it('should return s.inbox when id is equal to INBOX_SESSION_ID', () => {
|
|
259
|
+
expect(getSessionById('inbox')(s)).toEqual(s.inbox);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('should return the session with the specified id when id is not equal to INBOX_SESSION_ID', () => {
|
|
263
|
+
expect(getSessionById('1')(s)).toEqual(s.sessions['1']);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it('should return initLobeSession when the session with the specified id does not exist', () => {
|
|
267
|
+
expect(getSessionById('3')(s)).toEqual(initLobeSession);
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('getSessionMetaById', () => {
|
|
272
|
+
const s: SessionStore = {
|
|
273
|
+
sessions: {
|
|
274
|
+
'1': {
|
|
275
|
+
meta: {
|
|
276
|
+
title: 'Session 1',
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
'2': {
|
|
280
|
+
meta: {
|
|
281
|
+
title: 'Session 2',
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
inbox: {
|
|
286
|
+
meta: {
|
|
287
|
+
title: 'Inbox',
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
} as unknown as SessionStore;
|
|
291
|
+
|
|
292
|
+
it('should return the meta data of the session with the specified id', () => {
|
|
293
|
+
expect(getSessionMetaById('1')(s)).toEqual({ title: 'Session 1' });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should return the meta data of the inbox session when id is equal to INBOX_SESSION_ID', () => {
|
|
297
|
+
expect(getSessionMetaById('inbox')(s)).toEqual({ title: 'Inbox' });
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should return an empty object when the session with the specified id does not exist', () => {
|
|
301
|
+
expect(getSessionMetaById('3')(s)).toEqual({});
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
2
2
|
import { MetaData } from '@/types/meta';
|
|
3
3
|
import { LobeAgentSession } from '@/types/session';
|
|
4
4
|
import { filterWithKeywords } from '@/utils/filter';
|
|
5
5
|
|
|
6
|
+
import { SessionStore } from '../../../store';
|
|
6
7
|
import { initLobeSession } from '../initialState';
|
|
7
8
|
|
|
8
9
|
export const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
|
|
9
10
|
if (!s.activeId) return;
|
|
10
11
|
|
|
12
|
+
if (s.activeId === INBOX_SESSION_ID) return s.inbox;
|
|
13
|
+
|
|
11
14
|
return s.sessions[s.activeId];
|
|
12
15
|
};
|
|
13
16
|
|
|
@@ -17,7 +20,7 @@ export const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
|
|
|
17
20
|
|
|
18
21
|
export const sessionList = (s: SessionStore) => {
|
|
19
22
|
const filterChats = filterWithKeywords(s.sessions, s.searchKeywords, (item) => [
|
|
20
|
-
Object.values(item.chats)
|
|
23
|
+
Object.values(item.chats || {})
|
|
21
24
|
.map((c) => c.content)
|
|
22
25
|
.join(''),
|
|
23
26
|
]);
|
|
@@ -44,6 +47,8 @@ export const hasSessionList = (s: SessionStore) => {
|
|
|
44
47
|
export const getSessionById =
|
|
45
48
|
(id: string) =>
|
|
46
49
|
(s: SessionStore): LobeAgentSession => {
|
|
50
|
+
if (id === INBOX_SESSION_ID) return s.inbox;
|
|
51
|
+
|
|
47
52
|
const session = s.sessions[id];
|
|
48
53
|
|
|
49
54
|
if (!session) return initLobeSession;
|
|
@@ -53,7 +58,7 @@ export const getSessionById =
|
|
|
53
58
|
export const getSessionMetaById =
|
|
54
59
|
(id: string) =>
|
|
55
60
|
(s: SessionStore): MetaData => {
|
|
56
|
-
const session = s
|
|
61
|
+
const session = getSessionById(id)(s);
|
|
57
62
|
|
|
58
63
|
if (!session) return {};
|
|
59
64
|
return session.meta;
|
|
@@ -6,9 +6,9 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
6
6
|
import { isDev } from '@/utils/env';
|
|
7
7
|
|
|
8
8
|
import { SessionStoreState, initialState } from './initialState';
|
|
9
|
-
import { AgentAction, createAgentSlice } from './slices/agentConfig';
|
|
10
|
-
import { ChatAction, createChatSlice } from './slices/chat';
|
|
11
|
-
import { SessionAction, createSessionSlice } from './slices/session';
|
|
9
|
+
import { AgentAction, createAgentSlice } from './slices/agentConfig/action';
|
|
10
|
+
import { ChatAction, createChatSlice } from './slices/chat/actions';
|
|
11
|
+
import { SessionAction, createSessionSlice } from './slices/session/action';
|
|
12
12
|
|
|
13
13
|
// =============== 聚合 createStoreFn ============ //
|
|
14
14
|
|
|
@@ -23,7 +23,7 @@ const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (
|
|
|
23
23
|
|
|
24
24
|
// =============== persist 本地缓存中间件配置 ============ //
|
|
25
25
|
|
|
26
|
-
type SessionPersist = Pick<SessionStore, 'sessions'>;
|
|
26
|
+
type SessionPersist = Pick<SessionStore, 'sessions' | 'inbox'>;
|
|
27
27
|
|
|
28
28
|
const LOBE_CHAT = 'LOBE_CHAT';
|
|
29
29
|
|
|
@@ -31,13 +31,13 @@ const persistOptions: PersistOptions<SessionStore, SessionPersist> = {
|
|
|
31
31
|
name: LOBE_CHAT,
|
|
32
32
|
|
|
33
33
|
partialize: (s) => ({
|
|
34
|
+
inbox: s.inbox,
|
|
34
35
|
sessions: s.sessions,
|
|
35
36
|
}),
|
|
36
37
|
|
|
37
38
|
// 手动控制 Hydration ,避免 ssr 报错
|
|
38
39
|
skipHydration: true,
|
|
39
40
|
version: 0,
|
|
40
|
-
// version: Migration.targetVersion,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// =============== 实装 useStore ============ //
|