@lobehub/chat 0.43.0 → 0.44.1
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 +66 -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/layout/index.tsx +5 -5
- package/src/locales/default/common.ts +8 -1
- package/src/pages/chat/SessionList/Inbox.tsx +25 -0
- package/src/pages/chat/SessionList/List/Item/Actions.tsx +112 -0
- package/src/pages/chat/SessionList/List/Item/index.tsx +115 -0
- package/src/pages/chat/SessionList/List/{SkeletonItem.tsx → SkeletonList.tsx} +2 -2
- package/src/pages/chat/SessionList/List/index.tsx +7 -7
- 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/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/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 +24 -16
- 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/SessionItem.tsx +0 -178
- 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
|
@@ -7,7 +7,7 @@ import { SessionStore } from '@/store/session';
|
|
|
7
7
|
import { LanguageModel } from '@/types/llm';
|
|
8
8
|
import { MetaData } from '@/types/meta';
|
|
9
9
|
|
|
10
|
-
import { sessionSelectors } from '../session';
|
|
10
|
+
import { sessionSelectors } from '../session/selectors';
|
|
11
11
|
import { initialLobeAgentConfig } from './initialState';
|
|
12
12
|
|
|
13
13
|
const currentAgentMeta = (s: SessionStore): MetaData => {
|
|
@@ -25,8 +25,17 @@ const currentAutocomplete = (s: SessionStore): Autocomplete => ({
|
|
|
25
25
|
|
|
26
26
|
const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s)?.title || t('defaultSession');
|
|
27
27
|
|
|
28
|
+
const currentAgentConfig = (s: SessionStore) => {
|
|
29
|
+
const session = sessionSelectors.currentSession(s);
|
|
30
|
+
return merge({}, initialLobeAgentConfig, session?.config);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const currentAgentSystemRole = (s: SessionStore) => {
|
|
34
|
+
return currentAgentConfig(s).systemRole;
|
|
35
|
+
};
|
|
36
|
+
|
|
28
37
|
const currentAgentDescription = (s: SessionStore) =>
|
|
29
|
-
currentAgentMeta(s)?.description || t('noDescription');
|
|
38
|
+
currentAgentMeta(s)?.description || currentAgentSystemRole(s) || t('noDescription');
|
|
30
39
|
|
|
31
40
|
const currentAgentBackgroundColor = (s: SessionStore) => {
|
|
32
41
|
const session = sessionSelectors.currentSession(s);
|
|
@@ -41,15 +50,6 @@ const currentAgentAvatar = (s: SessionStore) => {
|
|
|
41
50
|
return session.meta.avatar || DEFAULT_AVATAR;
|
|
42
51
|
};
|
|
43
52
|
|
|
44
|
-
const currentAgentConfig = (s: SessionStore) => {
|
|
45
|
-
const session = sessionSelectors.currentSession(s);
|
|
46
|
-
return merge({}, initialLobeAgentConfig, session?.config);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const currentAgentSystemRole = (s: SessionStore) => {
|
|
50
|
-
return currentAgentConfig(s).systemRole;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
53
|
const currentAgentModel = (s: SessionStore): LanguageModel => {
|
|
54
54
|
const config = currentAgentConfig(s);
|
|
55
55
|
|
|
@@ -4,15 +4,18 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
4
4
|
import { LOADING_FLAT } from '@/const/message';
|
|
5
5
|
import { fetchChatModel } from '@/services/chatModel';
|
|
6
6
|
import { fetchPlugin } from '@/services/plugin';
|
|
7
|
-
import { SessionStore
|
|
8
|
-
import { getSlicedMessagesWithConfig } from '@/store/session/slices/chat/utils';
|
|
7
|
+
import { SessionStore } from '@/store/session';
|
|
9
8
|
import { ChatMessage, OpenAIFunctionCall } from '@/types/chatMessage';
|
|
10
9
|
import { fetchSSE } from '@/utils/fetch';
|
|
11
10
|
import { isFunctionMessage } from '@/utils/message';
|
|
12
11
|
import { setNamespace } from '@/utils/storeDebug';
|
|
13
12
|
import { nanoid } from '@/utils/uuid';
|
|
14
13
|
|
|
14
|
+
import { agentSelectors } from '../../agentConfig/selectors';
|
|
15
|
+
import { sessionSelectors } from '../../session/selectors';
|
|
15
16
|
import { MessageDispatch, messagesReducer } from '../reducers/message';
|
|
17
|
+
import { chatSelectors } from '../selectors';
|
|
18
|
+
import { getSlicedMessagesWithConfig } from '../utils';
|
|
16
19
|
|
|
17
20
|
const t = setNamespace('chat/message');
|
|
18
21
|
|
|
@@ -24,11 +27,6 @@ export interface ChatMessageAction {
|
|
|
24
27
|
* 清除消息
|
|
25
28
|
*/
|
|
26
29
|
clearMessage: () => void;
|
|
27
|
-
/**
|
|
28
|
-
* 创建或发送消息
|
|
29
|
-
* @param text - 消息文本
|
|
30
|
-
*/
|
|
31
|
-
createOrSendMsg: (text: string) => Promise<void>;
|
|
32
30
|
/**
|
|
33
31
|
* 删除消息
|
|
34
32
|
* @param id - 消息 ID
|
|
@@ -86,19 +84,6 @@ export const chatMessage: StateCreator<
|
|
|
86
84
|
}
|
|
87
85
|
},
|
|
88
86
|
|
|
89
|
-
createOrSendMsg: async (message) => {
|
|
90
|
-
if (!message) return;
|
|
91
|
-
|
|
92
|
-
const { sendMessage, createSession } = get();
|
|
93
|
-
const session = sessionSelectors.currentSession(get());
|
|
94
|
-
|
|
95
|
-
if (!session) {
|
|
96
|
-
await createSession();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
sendMessage(message);
|
|
100
|
-
},
|
|
101
|
-
|
|
102
87
|
deleteMessage: (id) => {
|
|
103
88
|
get().dispatchMessage({ id, type: 'deleteMessage' });
|
|
104
89
|
},
|
|
@@ -325,4 +310,12 @@ export const chatMessage: StateCreator<
|
|
|
325
310
|
|
|
326
311
|
await realFetchAIResponse(chats, message.id);
|
|
327
312
|
},
|
|
313
|
+
|
|
314
|
+
// genShareUrl: () => {
|
|
315
|
+
// const session = sessionSelectors.currentSession(get());
|
|
316
|
+
// if (!session) return '';
|
|
317
|
+
//
|
|
318
|
+
// const agent = session.config;
|
|
319
|
+
// return genShareMessagesUrl(session.chats, agent.systemRole);
|
|
320
|
+
// },
|
|
328
321
|
});
|
|
@@ -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,7 @@ export interface SessionAction {
|
|
|
27
29
|
* @param payload - 聊天记录
|
|
28
30
|
*/
|
|
29
31
|
dispatchSession: (payload: SessionDispatch) => void;
|
|
30
|
-
|
|
32
|
+
importInbox: (inbox: LobeAgentSession) => void;
|
|
31
33
|
/**
|
|
32
34
|
* 导入会话
|
|
33
35
|
* @param sessions
|
|
@@ -39,6 +41,7 @@ export interface SessionAction {
|
|
|
39
41
|
* @param sessionId
|
|
40
42
|
*/
|
|
41
43
|
pinSession: (sessionId: string, pinned?: boolean) => void;
|
|
44
|
+
|
|
42
45
|
/**
|
|
43
46
|
* 生成压缩后的消息
|
|
44
47
|
* @returns 压缩后的消息
|
|
@@ -95,13 +98,24 @@ export const createSessionSlice: StateCreator<
|
|
|
95
98
|
|
|
96
99
|
dispatchSession: (payload) => {
|
|
97
100
|
const { type, ...res } = payload;
|
|
98
|
-
set(
|
|
99
|
-
{ sessions: sessionsReducer(get().sessions, payload) },
|
|
100
|
-
false,
|
|
101
|
-
t(`dispatchChat/${type}`, res),
|
|
102
|
-
);
|
|
103
|
-
},
|
|
104
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
|
+
}
|
|
116
|
+
},
|
|
117
|
+
// TODO:暂时先不实现导入 inbox 的功能
|
|
118
|
+
importInbox: () => {},
|
|
105
119
|
importSessions: (importSessions) => {
|
|
106
120
|
const { sessions } = get();
|
|
107
121
|
set(
|
|
@@ -119,13 +133,7 @@ export const createSessionSlice: StateCreator<
|
|
|
119
133
|
t('importSessions', importSessions),
|
|
120
134
|
);
|
|
121
135
|
},
|
|
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
|
-
// },
|
|
136
|
+
|
|
129
137
|
pinSession: (sessionId, pinned) => {
|
|
130
138
|
const nextValue = typeof pinned === 'boolean' ? pinned : !get().sessions[sessionId].pinned;
|
|
131
139
|
|
|
@@ -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;
|