@lobehub/chat 0.149.4 → 0.149.5

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.
Files changed (80) hide show
  1. package/.github/FUNDING.yml +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/package.json +1 -1
  4. package/src/app/chat/(desktop)/features/ChatHeader/Main.tsx +5 -5
  5. package/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx +3 -3
  6. package/src/app/chat/(desktop)/features/ChatInput/Footer/DragUpload.tsx +9 -9
  7. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +3 -3
  8. package/src/app/chat/(desktop)/features/SideBar/SystemRole/index.tsx +8 -3
  9. package/src/app/chat/(mobile)/mobile/ChatHeader/ChatHeaderTitle.tsx +2 -2
  10. package/src/app/chat/(mobile)/mobile/page.tsx +0 -6
  11. package/src/app/chat/_layout/Desktop/SessionList.tsx +2 -0
  12. package/src/app/chat/features/PageTitle/index.tsx +3 -3
  13. package/src/app/chat/features/PluginTag/PluginStatus.tsx +2 -2
  14. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +4 -2
  15. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +10 -17
  16. package/src/app/chat/features/SessionListContent/index.tsx +2 -0
  17. package/src/app/chat/features/ShareButton/Preview.tsx +15 -11
  18. package/src/app/chat/features/ShareButton/useScreenshot.ts +2 -2
  19. package/src/app/chat/settings/features/EditPage.tsx +10 -7
  20. package/src/app/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +5 -3
  21. package/src/app/metadata.ts +3 -3
  22. package/src/app/settings/(mobile)/features/AvatarBanner.tsx +1 -0
  23. package/src/config/modelProviders/ollama.ts +11 -12
  24. package/src/const/session.ts +1 -0
  25. package/src/database/client/models/session.ts +1 -0
  26. package/src/database/client/models/user.ts +6 -0
  27. package/src/features/ChatInput/ActionBar/FileUpload.tsx +11 -5
  28. package/src/features/ChatInput/ActionBar/History.tsx +3 -3
  29. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +2 -0
  30. package/src/features/ChatInput/ActionBar/Temperature.tsx +3 -3
  31. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +4 -4
  32. package/src/features/ChatInput/ActionBar/Token/index.tsx +3 -3
  33. package/src/features/ChatInput/ActionBar/Tools/ToolItem.tsx +3 -3
  34. package/src/features/ChatInput/ActionBar/Tools/index.tsx +4 -4
  35. package/src/features/ChatInput/STT/browser.tsx +3 -3
  36. package/src/features/ChatInput/STT/openai.tsx +3 -3
  37. package/src/features/ChatInput/useChatInput.ts +3 -3
  38. package/src/features/Conversation/Extras/Assistant.test.tsx +7 -7
  39. package/src/features/Conversation/Extras/Assistant.tsx +3 -3
  40. package/src/features/Conversation/Extras/TTS/index.tsx +3 -3
  41. package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +2 -2
  42. package/src/features/Conversation/components/ChatItem/index.tsx +6 -4
  43. package/src/features/Conversation/hooks/useInitConversation.ts +10 -7
  44. package/src/features/Conversation/index.tsx +6 -3
  45. package/src/features/ModelSwitchPanel/index.tsx +6 -4
  46. package/src/hooks/useTTS.ts +4 -4
  47. package/src/services/chat.ts +3 -3
  48. package/src/services/session/client.ts +19 -0
  49. package/src/services/session/type.ts +2 -0
  50. package/src/store/agent/index.ts +2 -0
  51. package/src/store/agent/initialState.ts +7 -0
  52. package/src/store/agent/selectors.ts +1 -0
  53. package/src/store/{session/slices/agent → agent/slices/chat}/action.test.ts +26 -63
  54. package/src/store/agent/slices/chat/action.ts +107 -0
  55. package/src/store/agent/slices/chat/initialState.ts +14 -0
  56. package/src/store/agent/slices/chat/selectors.test.ts +82 -0
  57. package/src/store/agent/slices/chat/selectors.ts +81 -0
  58. package/src/store/agent/store.ts +27 -0
  59. package/src/store/chat/slices/message/action.test.ts +3 -2
  60. package/src/store/chat/slices/message/action.ts +3 -3
  61. package/src/store/chat/slices/message/selectors.test.ts +9 -2
  62. package/src/store/chat/slices/message/selectors.ts +6 -4
  63. package/src/store/chat/slices/share/action.ts +5 -3
  64. package/src/store/global/slices/preference/selectors.ts +3 -1
  65. package/src/store/session/selectors.ts +1 -2
  66. package/src/store/session/slices/session/action.test.ts +43 -0
  67. package/src/store/session/slices/session/action.ts +28 -18
  68. package/src/store/session/slices/session/helpers.ts +2 -3
  69. package/src/store/session/slices/session/initialState.ts +1 -17
  70. package/src/store/session/slices/session/selectors/index.ts +1 -0
  71. package/src/store/session/slices/session/selectors/list.test.ts +5 -3
  72. package/src/store/session/slices/session/selectors/list.ts +2 -3
  73. package/src/store/session/slices/session/selectors/meta.test.ts +108 -0
  74. package/src/store/session/slices/session/selectors/meta.ts +45 -0
  75. package/src/store/session/store.ts +1 -7
  76. package/src/types/session.ts +1 -0
  77. package/src/store/session/slices/agent/action.ts +0 -84
  78. package/src/store/session/slices/agent/selectors.test.ts +0 -180
  79. package/src/store/session/slices/agent/selectors.ts +0 -129
  80. /package/src/store/{session/slices/agent → agent/slices/chat}/index.ts +0 -0
@@ -7,6 +7,8 @@ import { sessionService } from '@/services/session';
7
7
  import { useSessionStore } from '@/store/session';
8
8
  import { LobeSessionType } from '@/types/session';
9
9
 
10
+ import { sessionSelectors } from './selectors';
11
+
10
12
  // Mock sessionService 和其他依赖项
11
13
  vi.mock('@/services/session', () => ({
12
14
  sessionService: {
@@ -189,4 +191,45 @@ describe('SessionAction', () => {
189
191
  expect(mockRefresh).toHaveBeenCalled();
190
192
  });
191
193
  });
194
+
195
+ describe('updateAgentMeta', () => {
196
+ it('should not update meta if there is no current session', async () => {
197
+ const { result } = renderHook(() => useSessionStore());
198
+ const meta = { title: 'Test Agent' };
199
+ const updateSessionMock = vi.spyOn(sessionService, 'updateSession');
200
+ const refreshSessionsMock = vi.spyOn(result.current, 'refreshSessions');
201
+
202
+ // 模拟没有当前会话
203
+ vi.spyOn(sessionSelectors, 'currentSession').mockReturnValue(null as any);
204
+
205
+ await act(async () => {
206
+ await result.current.updateSessionMeta(meta as any);
207
+ });
208
+
209
+ expect(updateSessionMock).not.toHaveBeenCalled();
210
+ expect(refreshSessionsMock).not.toHaveBeenCalled();
211
+ updateSessionMock.mockRestore();
212
+ refreshSessionsMock.mockRestore();
213
+ });
214
+
215
+ it('should update session meta and refresh sessions', async () => {
216
+ const { result } = renderHook(() => useSessionStore());
217
+ const meta = { title: 'Test Agent' };
218
+ const updateSessionMock = vi.spyOn(sessionService, 'updateSession');
219
+ const refreshSessionsMock = vi.spyOn(result.current, 'refreshSessions');
220
+
221
+ // 模拟有当前会话
222
+ vi.spyOn(sessionSelectors, 'currentSession').mockReturnValue({ id: 'session-id' } as any);
223
+ vi.spyOn(result.current, 'activeId', 'get').mockReturnValue('session-id');
224
+
225
+ await act(async () => {
226
+ await result.current.updateSessionMeta(meta);
227
+ });
228
+
229
+ expect(updateSessionMock).toHaveBeenCalledWith('session-id', { meta });
230
+ expect(refreshSessionsMock).toHaveBeenCalled();
231
+ updateSessionMock.mockRestore();
232
+ refreshSessionsMock.mockRestore();
233
+ });
234
+ });
192
235
  });
@@ -1,15 +1,17 @@
1
1
  import { t } from 'i18next';
2
+ import { isEqual } from 'lodash-es';
2
3
  import useSWR, { SWRResponse, mutate } from 'swr';
3
4
  import { DeepPartial } from 'utility-types';
4
5
  import { StateCreator } from 'zustand/vanilla';
5
6
 
6
7
  import { message } from '@/components/AntdStaticMethods';
7
- import { INBOX_SESSION_ID } from '@/const/session';
8
- import { SWRRefreshParams, useClientDataSWR } from '@/libs/swr';
8
+ import { DEFAULT_AGENT_LOBE_SESSION, INBOX_SESSION_ID } from '@/const/session';
9
+ import { useClientDataSWR } from '@/libs/swr';
9
10
  import { sessionService } from '@/services/session';
10
11
  import { useGlobalStore } from '@/store/global';
11
12
  import { settingsSelectors } from '@/store/global/selectors';
12
13
  import { SessionStore } from '@/store/session';
14
+ import { MetaData } from '@/types/meta';
13
15
  import {
14
16
  ChatSessionList,
15
17
  LobeAgentSession,
@@ -21,10 +23,9 @@ import {
21
23
  import { merge } from '@/utils/merge';
22
24
  import { setNamespace } from '@/utils/storeDebug';
23
25
 
24
- import { agentSelectors } from '../agent/selectors';
25
- import { initLobeSession } from './initialState';
26
26
  import { SessionDispatch, sessionsReducer } from './reducers';
27
27
  import { sessionSelectors } from './selectors';
28
+ import { sessionMetaSelectors } from './selectors/meta';
28
29
 
29
30
  const n = setNamespace('session');
30
31
 
@@ -53,6 +54,7 @@ export interface SessionAction {
53
54
  ) => Promise<string>;
54
55
  duplicateSession: (id: string) => Promise<void>;
55
56
  updateSessionGroupId: (sessionId: string, groupId: string) => Promise<void>;
57
+ updateSessionMeta: (meta: Partial<MetaData>) => void;
56
58
 
57
59
  /**
58
60
  * Pins or unpins a session.
@@ -61,7 +63,7 @@ export interface SessionAction {
61
63
  /**
62
64
  * re-fetch the data
63
65
  */
64
- refreshSessions: (params?: SWRRefreshParams<ChatSessionList>) => Promise<void>;
66
+ refreshSessions: () => Promise<void>;
65
67
  /**
66
68
  * remove session
67
69
  * @param id - sessionId
@@ -106,7 +108,7 @@ export const createSessionSlice: StateCreator<
106
108
 
107
109
  // merge the defaultAgent in settings
108
110
  const defaultAgent = merge(
109
- initLobeSession,
111
+ DEFAULT_AGENT_LOBE_SESSION,
110
112
  settingsSelectors.defaultAgent(useGlobalStore.getState()),
111
113
  );
112
114
 
@@ -125,7 +127,7 @@ export const createSessionSlice: StateCreator<
125
127
  const session = sessionSelectors.getSessionById(id)(get());
126
128
 
127
129
  if (!session) return;
128
- const title = agentSelectors.getTitle(session.meta);
130
+ const title = sessionMetaSelectors.getTitle(session.meta);
129
131
 
130
132
  const newTitle = t('duplicateSession.title', { ns: 'chat', title: title });
131
133
 
@@ -157,10 +159,6 @@ export const createSessionSlice: StateCreator<
157
159
  await get().internal_updateSession(id, { pinned });
158
160
  },
159
161
 
160
- refreshSessions: async () => {
161
- await mutate(FETCH_SESSIONS_KEY);
162
- },
163
-
164
162
  removeSession: async (sessionId) => {
165
163
  await sessionService.removeSession(sessionId);
166
164
  await get().refreshSessions();
@@ -175,16 +173,25 @@ export const createSessionSlice: StateCreator<
175
173
  await get().internal_updateSession(sessionId, { group });
176
174
  },
177
175
 
176
+ updateSessionMeta: async (meta) => {
177
+ const session = sessionSelectors.currentSession(get());
178
+ if (!session) return;
179
+
180
+ const { activeId, refreshSessions } = get();
181
+
182
+ await sessionService.updateSession(activeId, { meta });
183
+ await refreshSessions();
184
+ },
185
+
178
186
  useFetchSessions: () =>
179
187
  useClientDataSWR<ChatSessionList>(FETCH_SESSIONS_KEY, sessionService.getGroupedSessions, {
180
188
  onSuccess: (data) => {
181
- // 由于 https://github.com/lobehub/lobe-chat/pull/541 的关系
182
- // 只有触发了 refreshSessions 才会更新 sessions,进而触发页面 rerender
183
- // 因此这里不能补充 equal 判断,否则会导致页面不更新
184
- // if (get().isSessionsFirstFetchFinished && isEqual(get().sessions, data)) return;
185
-
186
- // TODO:后续的根本解法应该是解除 inbox 和 session 的数据耦合
187
- // 避免互相依赖的情况出现
189
+ if (
190
+ get().isSessionsFirstFetchFinished &&
191
+ isEqual(get().sessions, data.sessions) &&
192
+ isEqual(get().sessionGroups, data.sessionGroups)
193
+ )
194
+ return;
188
195
 
189
196
  get().internal_processSessions(
190
197
  data.sessions,
@@ -239,4 +246,7 @@ export const createSessionSlice: StateCreator<
239
246
  n('processSessions'),
240
247
  );
241
248
  },
249
+ refreshSessions: async () => {
250
+ await mutate(FETCH_SESSIONS_KEY);
251
+ },
242
252
  });
@@ -1,13 +1,12 @@
1
+ import { DEFAULT_AGENT_LOBE_SESSION } from '@/const/session';
1
2
  import { LobeAgentSession, LobeSessions } from '@/types/session';
2
3
 
3
- import { initLobeSession } from './initialState';
4
-
5
4
  export const getSessionPinned = (session: LobeAgentSession) => session.pinned;
6
5
 
7
6
  const getSessionById = (id: string, sessions: LobeSessions): LobeAgentSession => {
8
7
  const session = sessions.find((s) => s.id === id);
9
8
 
10
- if (!session) return initLobeSession;
9
+ if (!session) return DEFAULT_AGENT_LOBE_SESSION;
11
10
 
12
11
  return session;
13
12
  };
@@ -1,20 +1,4 @@
1
- import { DEFAULT_AGENT_META } from '@/const/meta';
2
- import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
3
- import {
4
- CustomSessionGroup,
5
- LobeAgentSession,
6
- LobeSessionGroups,
7
- LobeSessionType,
8
- } from '@/types/session';
9
-
10
- export const initLobeSession: LobeAgentSession = {
11
- config: DEFAULT_AGENT_CONFIG,
12
- createdAt: Date.now(),
13
- id: '',
14
- meta: DEFAULT_AGENT_META,
15
- type: LobeSessionType.Agent,
16
- updatedAt: Date.now(),
17
- };
1
+ import { CustomSessionGroup, LobeAgentSession, LobeSessionGroups } from '@/types/session';
18
2
 
19
3
  export interface SessionState {
20
4
  /**
@@ -1 +1,2 @@
1
1
  export * from './list';
2
+ export * from './meta';
@@ -1,7 +1,7 @@
1
+ import { DEFAULT_AGENT_LOBE_SESSION } from '@/const/session';
1
2
  import type { SessionStore } from '@/store/session';
2
3
  import { LobeAgentSession, LobeSessionType } from '@/types/session';
3
4
 
4
- import { initLobeSession } from '../initialState';
5
5
  import { sessionSelectors } from './list';
6
6
 
7
7
  describe('currentSession', () => {
@@ -64,7 +64,9 @@ describe('currentSessionSafe', () => {
64
64
  } as unknown as SessionStore;
65
65
 
66
66
  it('should return initLobeSession when currentSession(s) returns undefined', () => {
67
- expect(sessionSelectors.currentSessionSafe({ sessions: {} } as any)).toEqual(initLobeSession);
67
+ expect(sessionSelectors.currentSessionSafe({ sessions: {} } as any)).toEqual(
68
+ DEFAULT_AGENT_LOBE_SESSION,
69
+ );
68
70
  });
69
71
 
70
72
  it('should return the result of currentSession(s) when it returns a non-undefined value', () => {
@@ -102,7 +104,7 @@ describe('getSessionById', () => {
102
104
  });
103
105
 
104
106
  it('should return initLobeSession when the session with the specified id does not exist', () => {
105
- expect(sessionSelectors.getSessionById('3')(s)).toEqual(initLobeSession);
107
+ expect(sessionSelectors.getSessionById('3')(s)).toEqual(DEFAULT_AGENT_LOBE_SESSION);
106
108
  });
107
109
  });
108
110
 
@@ -1,10 +1,9 @@
1
- import { INBOX_SESSION_ID } from '@/const/session';
1
+ import { DEFAULT_AGENT_LOBE_SESSION, INBOX_SESSION_ID } from '@/const/session';
2
2
  import { sessionHelpers } from '@/store/session/slices/session/helpers';
3
3
  import { MetaData } from '@/types/meta';
4
4
  import { CustomSessionGroup, LobeAgentSession, LobeSessions } from '@/types/session';
5
5
 
6
6
  import { SessionStore } from '../../../store';
7
- import { initLobeSession } from '../initialState';
8
7
 
9
8
  const defaultSessions = (s: SessionStore): LobeSessions => s.defaultSessions;
10
9
  const pinnedSessions = (s: SessionStore): LobeSessions => s.pinnedSessions;
@@ -33,7 +32,7 @@ const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
33
32
  };
34
33
 
35
34
  const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
36
- return currentSession(s) || initLobeSession;
35
+ return currentSession(s) || DEFAULT_AGENT_LOBE_SESSION;
37
36
  };
38
37
 
39
38
  const hasCustomAgents = (s: SessionStore) => defaultSessions(s).length > 0;
@@ -0,0 +1,108 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { DEFAULT_AVATAR } from '@/const/meta';
4
+ import { DEFAULT_AGENT_CONFIG, DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings';
5
+ import { SessionStore } from '@/store/session';
6
+ import { MetaData } from '@/types/meta';
7
+ import { LobeAgentSession, LobeSessionType } from '@/types/session';
8
+
9
+ import { sessionMetaSelectors } from './meta';
10
+
11
+ vi.mock('i18next', () => ({
12
+ t: vi.fn((key) => key), // Simplified mock return value
13
+ }));
14
+
15
+ const mockSessionStore = {
16
+ activeId: '1',
17
+ sessions: [
18
+ {
19
+ id: '1',
20
+ config: DEFAULT_AGENT_CONFIG,
21
+ meta: {
22
+ title: 'title1',
23
+ description: 'description1',
24
+ },
25
+ type: LobeSessionType.Agent,
26
+ } as LobeAgentSession,
27
+ {
28
+ id: '2',
29
+ meta: {
30
+ title: 'title2',
31
+ description: 'description2',
32
+ },
33
+ config: DEFAULT_AGENT_CONFIG,
34
+ type: LobeSessionType.Agent,
35
+ } as LobeAgentSession,
36
+ ],
37
+ } as unknown as SessionStore;
38
+
39
+ describe('sessionMetaSelectors', () => {
40
+ describe('currentAgentMeta', () => {
41
+ it('should return the merged default and session-specific meta data', () => {
42
+ const meta = sessionMetaSelectors.currentAgentMeta(mockSessionStore);
43
+ expect(meta).toEqual(expect.objectContaining(mockSessionStore.sessions[0].meta));
44
+ });
45
+
46
+ it('should return inbox defaults if it is an inbox session', () => {
47
+ // Assume sessionSelectors.isInboxSession() is mocked to return true for this test
48
+ const meta = sessionMetaSelectors.currentAgentMeta(mockSessionStore);
49
+ expect(meta.avatar).toBe(DEFAULT_AVATAR);
50
+ });
51
+ });
52
+
53
+ describe('currentAgentTitle', () => {
54
+ it('should return the title from the session meta data', () => {
55
+ const title = sessionMetaSelectors.currentAgentTitle(mockSessionStore);
56
+ expect(title).toBe(mockSessionStore.sessions[0].meta.title);
57
+ });
58
+ });
59
+
60
+ describe('currentAgentDescription', () => {
61
+ it('should return the description from the session meta data', () => {
62
+ const description = sessionMetaSelectors.currentAgentDescription(mockSessionStore);
63
+ expect(description).toBe(mockSessionStore.sessions[0].meta.description);
64
+ });
65
+ });
66
+
67
+ describe('getAvatar', () => {
68
+ it('should return the avatar from the meta data', () => {
69
+ const meta: MetaData = { avatar: 'custom-avatar.png' };
70
+ const avatar = sessionMetaSelectors.getAvatar(meta);
71
+ expect(avatar).toBe(meta.avatar);
72
+ });
73
+
74
+ it('should return the default avatar if none is defined in the meta data', () => {
75
+ const meta: MetaData = {};
76
+ const avatar = sessionMetaSelectors.getAvatar(meta);
77
+ expect(avatar).toBe(DEFAULT_AVATAR);
78
+ });
79
+ });
80
+
81
+ describe('getTitle', () => {
82
+ it('should return the title from the meta data', () => {
83
+ const meta: MetaData = { title: 'Custom Title' };
84
+ const title = sessionMetaSelectors.getTitle(meta);
85
+ expect(title).toBe(meta.title);
86
+ });
87
+
88
+ it('should return the default title if none is defined in the meta data', () => {
89
+ const meta: MetaData = {};
90
+ const title = sessionMetaSelectors.getTitle(meta);
91
+ expect(title).toBe('defaultSession'); // Assuming translation returns this key
92
+ });
93
+ });
94
+
95
+ describe('getDescription', () => {
96
+ it('should return the description from the meta data', () => {
97
+ const meta: MetaData = { description: 'Custom Description' };
98
+ const description = sessionMetaSelectors.getDescription(meta);
99
+ expect(description).toBe(meta.description);
100
+ });
101
+
102
+ it('should return the default description if none is defined in the meta data', () => {
103
+ const meta: MetaData = {};
104
+ const description = sessionMetaSelectors.getDescription(meta);
105
+ expect(description).toBe('noDescription'); // Assuming translation returns this key
106
+ });
107
+ });
108
+ });
@@ -0,0 +1,45 @@
1
+ import { t } from 'i18next';
2
+
3
+ import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR, DEFAULT_INBOX_AVATAR } from '@/const/meta';
4
+ import { SessionStore } from '@/store/session';
5
+ import { MetaData } from '@/types/meta';
6
+ import { merge } from '@/utils/merge';
7
+
8
+ import { sessionSelectors } from './list';
9
+
10
+ // ========== Meta ============== //
11
+ const currentAgentMeta = (s: SessionStore): MetaData => {
12
+ const isInbox = sessionSelectors.isInboxSession(s);
13
+
14
+ const defaultMeta = {
15
+ avatar: isInbox ? DEFAULT_INBOX_AVATAR : DEFAULT_AVATAR,
16
+ backgroundColor: DEFAULT_BACKGROUND_COLOR,
17
+ description: isInbox ? t('inbox.desc', { ns: 'chat' }) : t('noDescription'),
18
+ title: isInbox ? t('inbox.title', { ns: 'chat' }) : t('defaultSession'),
19
+ };
20
+
21
+ const session = sessionSelectors.currentSession(s);
22
+
23
+ return merge(defaultMeta, session?.meta);
24
+ };
25
+
26
+ const currentAgentTitle = (s: SessionStore) => currentAgentMeta(s).title;
27
+ const currentAgentDescription = (s: SessionStore) => currentAgentMeta(s).description;
28
+ const currentAgentAvatar = (s: SessionStore) => currentAgentMeta(s).avatar;
29
+ const currentAgentBackgroundColor = (s: SessionStore) => currentAgentMeta(s).backgroundColor;
30
+
31
+ const getAvatar = (s: MetaData) => s.avatar || DEFAULT_AVATAR;
32
+ const getTitle = (s: MetaData) => s.title || t('defaultSession', { ns: 'common' });
33
+ export const getDescription = (s: MetaData) =>
34
+ s.description || t('noDescription', { ns: 'common' });
35
+
36
+ export const sessionMetaSelectors = {
37
+ currentAgentAvatar,
38
+ currentAgentBackgroundColor,
39
+ currentAgentDescription,
40
+ currentAgentMeta,
41
+ currentAgentTitle,
42
+ getAvatar,
43
+ getDescription,
44
+ getTitle,
45
+ };
@@ -6,21 +6,15 @@ 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/agent/action';
10
9
  import { SessionAction, createSessionSlice } from './slices/session/action';
11
10
  import { SessionGroupAction, createSessionGroupSlice } from './slices/sessionGroup/action';
12
11
 
13
12
  // =============== 聚合 createStoreFn ============ //
14
13
 
15
- export interface SessionStore
16
- extends SessionAction,
17
- AgentAction,
18
- SessionGroupAction,
19
- SessionStoreState {}
14
+ export interface SessionStore extends SessionAction, SessionGroupAction, SessionStoreState {}
20
15
 
21
16
  const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (...parameters) => ({
22
17
  ...initialState,
23
- ...createAgentSlice(...parameters),
24
18
  ...createSessionSlice(...parameters),
25
19
  ...createSessionGroupSlice(...parameters),
26
20
  });
@@ -30,6 +30,7 @@ export type SessionGroups = SessionGroupItem[];
30
30
  export interface LobeAgentSession extends BaseDataModel {
31
31
  config: LobeAgentConfig;
32
32
  group?: SessionGroupId;
33
+ model: string;
33
34
  pinned?: boolean;
34
35
  type: LobeSessionType.Agent;
35
36
  }
@@ -1,84 +0,0 @@
1
- import { produce } from 'immer';
2
- import { StateCreator } from 'zustand/vanilla';
3
-
4
- import { sessionService } from '@/services/session';
5
- import { useGlobalStore } from '@/store/global';
6
- import { agentSelectors } from '@/store/session/selectors';
7
- import { LobeAgentConfig } from '@/types/agent';
8
- import { MetaData } from '@/types/meta';
9
-
10
- import { SessionStore } from '../../store';
11
- import { sessionSelectors } from '../session/selectors';
12
-
13
- /**
14
- * 助手接口
15
- */
16
- export interface AgentAction {
17
- removePlugin: (id: string) => void;
18
- togglePlugin: (id: string, open?: boolean) => Promise<void>;
19
- updateAgentConfig: (config: Partial<LobeAgentConfig>) => void;
20
- updateAgentMeta: (meta: Partial<MetaData>) => void;
21
- }
22
-
23
- export const createAgentSlice: StateCreator<
24
- SessionStore,
25
- [['zustand/devtools', never]],
26
- [],
27
- AgentAction
28
- > = (set, get) => ({
29
- removePlugin: async (id) => {
30
- await get().togglePlugin(id, false);
31
- },
32
-
33
- togglePlugin: async (id, open) => {
34
- const originConfig = agentSelectors.currentAgentConfig(get());
35
-
36
- const config = produce(originConfig, (draft) => {
37
- draft.plugins = produce(draft.plugins || [], (plugins) => {
38
- const index = plugins.indexOf(id);
39
- const shouldOpen = open !== undefined ? open : index === -1;
40
-
41
- if (shouldOpen) {
42
- // 如果 open 为 true 或者 id 不存在于 plugins 中,则添加它
43
- if (index === -1) {
44
- plugins.push(id);
45
- }
46
- } else {
47
- // 如果 open 为 false 或者 id 存在于 plugins 中,则移除它
48
- if (index !== -1) {
49
- plugins.splice(index, 1);
50
- }
51
- }
52
- });
53
- });
54
-
55
- get().updateAgentConfig(config);
56
- },
57
- updateAgentConfig: async (config) => {
58
- // if is the inbox session, update the global config
59
- const isInbox = sessionSelectors.isInboxSession(get());
60
- if (isInbox) {
61
- await useGlobalStore.getState().updateDefaultAgent({ config });
62
- } else {
63
- const session = sessionSelectors.currentSession(get());
64
- if (!session) return;
65
-
66
- const { activeId } = get();
67
-
68
- await sessionService.updateSessionConfig(activeId, config);
69
- }
70
-
71
- // trigger store rerender
72
- await get().refreshSessions();
73
- },
74
-
75
- updateAgentMeta: async (meta) => {
76
- const session = sessionSelectors.currentSession(get());
77
- if (!session) return;
78
-
79
- const { activeId, refreshSessions } = get();
80
-
81
- await sessionService.updateSession(activeId, { meta });
82
- await refreshSessions();
83
- },
84
- });