@lobehub/chat 0.151.10 → 0.151.11

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.151.11](https://github.com/lobehub/lobe-chat/compare/v0.151.10...v0.151.11)
6
+
7
+ <sup>Released on **2024-04-30**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix telemetry preference modal and default agent config error.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix telemetry preference modal and default agent config error, closes [#2312](https://github.com/lobehub/lobe-chat/issues/2312) ([8900445](https://github.com/lobehub/lobe-chat/commit/8900445))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 0.151.10](https://github.com/lobehub/lobe-chat/compare/v0.151.9...v0.151.10)
6
31
 
7
32
  <sup>Released on **2024-04-30**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.151.10",
3
+ "version": "0.151.11",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -12,6 +12,7 @@ import { PRIVACY_URL } from '@/const/url';
12
12
  import { useServerConfigStore } from '@/store/serverConfig';
13
13
  import { serverConfigSelectors } from '@/store/serverConfig/selectors';
14
14
  import { useUserStore } from '@/store/user';
15
+ import { preferenceSelectors } from '@/store/user/selectors';
15
16
 
16
17
  const useStyles = createStyles(({ css, token, isDarkMode }) => ({
17
18
  container: css`
@@ -57,13 +58,14 @@ const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => {
57
58
 
58
59
  const { t } = useTranslation('common');
59
60
  const shouldCheck = useServerConfigStore(serverConfigSelectors.enabledTelemetryChat);
61
+ const isPreferenceInit = useUserStore(preferenceSelectors.isPreferenceInit);
60
62
 
61
63
  const [useCheckTrace, updatePreference] = useUserStore((s) => [
62
64
  s.useCheckTrace,
63
65
  s.updatePreference,
64
66
  ]);
65
67
 
66
- const { data: showModal, mutate } = useCheckTrace(shouldCheck);
68
+ const { data: showModal, mutate } = useCheckTrace(shouldCheck && isPreferenceInit);
67
69
 
68
70
  const updateTelemetry = (telemetry: boolean) => {
69
71
  updatePreference({ telemetry });
@@ -3,13 +3,11 @@ import { memo } from 'react';
3
3
 
4
4
  import { INBOX_SESSION_ID } from '@/const/session';
5
5
  import AgentSetting from '@/features/AgentSetting';
6
- import { useAgentStore } from '@/store/agent';
7
- import { agentSelectors } from '@/store/agent/selectors';
8
6
  import { useUserStore } from '@/store/user';
9
7
  import { settingsSelectors } from '@/store/user/selectors';
10
8
 
11
9
  const Agent = memo(() => {
12
- const config = useAgentStore(agentSelectors.defaultAgentConfig, isEqual);
10
+ const config = useUserStore(settingsSelectors.defaultAgentConfig, isEqual);
13
11
  const meta = useUserStore(settingsSelectors.defaultAgentMeta, isEqual);
14
12
  const [updateAgent] = useUserStore((s) => [s.updateDefaultAgent]);
15
13
 
@@ -12,7 +12,7 @@ const Temperature = memo(() => {
12
12
 
13
13
  const [temperature, updateAgentConfig] = useAgentStore((s) => {
14
14
  const config = agentSelectors.currentAgentConfig(s);
15
- return [config.params.temperature, s.updateAgentConfig];
15
+ return [config.params?.temperature, s.updateAgentConfig];
16
16
  });
17
17
 
18
18
  return (
@@ -243,6 +243,32 @@ describe('SessionService', () => {
243
243
  });
244
244
  });
245
245
 
246
+ describe('hasSessions', () => {
247
+ it('should return false if no sessions exist', async () => {
248
+ // Setup
249
+ (SessionModel.count as Mock).mockResolvedValue(0);
250
+
251
+ // Execute
252
+ const result = await sessionService.hasSessions();
253
+
254
+ // Assert
255
+ expect(SessionModel.count).toHaveBeenCalled();
256
+ expect(result).toBe(false);
257
+ });
258
+
259
+ it('should return true if sessions exist', async () => {
260
+ // Setup
261
+ (SessionModel.count as Mock).mockResolvedValue(1);
262
+
263
+ // Execute
264
+ const result = await sessionService.hasSessions();
265
+
266
+ // Assert
267
+ expect(SessionModel.count).toHaveBeenCalled();
268
+ expect(result).toBe(true);
269
+ });
270
+ });
271
+
246
272
  describe('searchSessions', () => {
247
273
  it('should return sessions that match the keyword', async () => {
248
274
  // Setup
@@ -80,7 +80,7 @@ export class ClientService implements ISessionService {
80
80
  return SessionModel.count();
81
81
  }
82
82
  async hasSessions() {
83
- return (await this.countSessions()) === 0;
83
+ return (await this.countSessions()) !== 0;
84
84
  }
85
85
 
86
86
  async searchSessions(keyword: string) {
@@ -4,6 +4,7 @@ import { withSWR } from '~test-utils';
4
4
 
5
5
  import { globalService } from '@/services/global';
6
6
  import { useGlobalStore } from '@/store/global/index';
7
+ import { initialState } from '@/store/global/initialState';
7
8
 
8
9
  vi.mock('zustand/traditional');
9
10
 
@@ -141,4 +142,41 @@ describe('createPreferenceSlice', () => {
141
142
  expect(useGlobalStore.getState().latestVersion).toBe(latestVersion);
142
143
  });
143
144
  });
145
+
146
+ describe('useInitGlobalPreference', () => {
147
+ it('should init global preference if there is empty object', async () => {
148
+ vi.spyOn(
149
+ useGlobalStore.getState().preferenceStorage,
150
+ 'getFromLocalStorage',
151
+ ).mockReturnValueOnce({} as any);
152
+
153
+ const { result } = renderHook(() => useGlobalStore().useInitGlobalPreference(), {
154
+ wrapper: withSWR,
155
+ });
156
+
157
+ await waitFor(() => {
158
+ expect(result.current.data).toEqual({});
159
+ });
160
+
161
+ expect(useGlobalStore.getState().preference).toEqual(initialState.preference);
162
+ });
163
+
164
+ it('should update with data', async () => {
165
+ const { result } = renderHook(() => useGlobalStore());
166
+ vi.spyOn(
167
+ useGlobalStore.getState().preferenceStorage,
168
+ 'getFromLocalStorage',
169
+ ).mockReturnValueOnce({ inputHeight: 300 } as any);
170
+
171
+ const { result: hooks } = renderHook(() => result.current.useInitGlobalPreference(), {
172
+ wrapper: withSWR,
173
+ });
174
+
175
+ await waitFor(() => {
176
+ expect(hooks.current.data).toEqual({ inputHeight: 300 });
177
+ });
178
+
179
+ expect(result.current.preference.inputHeight).toEqual(300);
180
+ });
181
+ });
144
182
  });
@@ -1,3 +1,4 @@
1
+ import isEqual from 'fast-deep-equal';
1
2
  import { produce } from 'immer';
2
3
  import { gt } from 'semver';
3
4
  import useSWR, { SWRResponse } from 'swr';
@@ -94,9 +95,11 @@ export const globalActionSlice: StateCreator<
94
95
  () => get().preferenceStorage.getFromLocalStorage(),
95
96
  {
96
97
  onSuccess: (preference) => {
97
- if (preference) {
98
- set({ preference }, false, n('initPreference'));
99
- }
98
+ const nextPreference = merge(get().preference, preference);
99
+
100
+ if (isEqual(get().preference, nextPreference)) return;
101
+
102
+ set({ preference: nextPreference }, false, n('initPreference'));
100
103
  },
101
104
  },
102
105
  ),
@@ -1,6 +1,8 @@
1
- import { act, renderHook } from '@testing-library/react';
1
+ import { act, renderHook, waitFor } from '@testing-library/react';
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { withSWR } from '~test-utils';
3
4
 
5
+ import { globalService } from '@/services/global';
4
6
  import { useUserStore } from '@/store/user';
5
7
 
6
8
  import { type Guide } from './initialState';
@@ -38,4 +40,23 @@ describe('createPreferenceSlice', () => {
38
40
  expect(result.current.preference.hideSyncAlert).toEqual(true);
39
41
  });
40
42
  });
43
+
44
+ describe('useInitPreference', () => {
45
+ it('should return false when userId is empty', async () => {
46
+ const { result } = renderHook(() => useUserStore());
47
+
48
+ vi.spyOn(result.current.preferenceStorage, 'getFromLocalStorage').mockResolvedValueOnce(
49
+ {} as any,
50
+ );
51
+
52
+ const { result: prefernce } = renderHook(() => result.current.useInitPreference(), {
53
+ wrapper: withSWR,
54
+ });
55
+
56
+ await waitFor(() => {
57
+ expect(prefernce.current.data).toEqual({});
58
+ expect(result.current.isPreferenceInit).toBeTruthy();
59
+ });
60
+ });
61
+ });
41
62
  });
@@ -41,9 +41,7 @@ export const createPreferenceSlice: StateCreator<
41
41
  () => get().preferenceStorage.getFromLocalStorage(),
42
42
  {
43
43
  onSuccess: (preference) => {
44
- if (preference) {
45
- set({ preference }, false, n('initPreference'));
46
- }
44
+ set({ isPreferenceInit: true, preference }, false, n('initPreference'));
47
45
  },
48
46
  },
49
47
  ),
@@ -16,6 +16,7 @@ export interface UserPreference {
16
16
  }
17
17
 
18
18
  export interface UserPreferenceState {
19
+ isPreferenceInit: boolean;
19
20
  /**
20
21
  * the user preference, which only store in local storage
21
22
  */
@@ -24,6 +25,7 @@ export interface UserPreferenceState {
24
25
  }
25
26
 
26
27
  export const initialPreferenceState: UserPreferenceState = {
28
+ isPreferenceInit: false,
27
29
  preference: {
28
30
  guide: {},
29
31
  telemetry: null,
@@ -5,9 +5,11 @@ const useCmdEnterToSend = (s: UserStore): boolean => s.preference.useCmdEnterToS
5
5
  const userAllowTrace = (s: UserStore) => s.preference.telemetry;
6
6
 
7
7
  const hideSyncAlert = (s: UserStore) => s.preference.hideSyncAlert;
8
+ const isPreferenceInit = (s: UserStore) => s.isPreferenceInit;
8
9
 
9
10
  export const preferenceSelectors = {
10
11
  hideSyncAlert,
12
+ isPreferenceInit,
11
13
  useCmdEnterToSend,
12
14
  userAllowTrace,
13
15
  };
@@ -2,17 +2,11 @@ import { act, renderHook } from '@testing-library/react';
2
2
  import { describe, expect, it, vi } from 'vitest';
3
3
 
4
4
  import { userService } from '@/services/user';
5
- import { UserStore, useUserStore } from '@/store/user';
6
- import { UserSettingsState, initialSettingsState } from '@/store/user/slices/settings/initialState';
7
- import {
8
- modelConfigSelectors,
9
- modelProviderSelectors,
10
- settingsSelectors,
11
- } from '@/store/user/slices/settings/selectors';
5
+ import { useUserStore } from '@/store/user';
12
6
  import { GeneralModelProviderConfig } from '@/types/settings';
13
- import { merge } from '@/utils/merge';
14
7
 
15
- import { CustomModelCardDispatch, customModelCardsReducer } from '../reducers/customModelCard';
8
+ import { CustomModelCardDispatch } from '../reducers/customModelCard';
9
+ import { modelProviderSelectors, settingsSelectors } from '../selectors';
16
10
 
17
11
  // Mock userService
18
12
  vi.mock('@/services/user', () => ({
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_LANG } from '@/const/locale';
2
2
  import { DEFAULT_AGENT_META } from '@/const/meta';
3
- import { DEFAULT_AGENT, DEFAULT_TTS_CONFIG } from '@/const/settings';
3
+ import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_TTS_CONFIG } from '@/const/settings';
4
4
  import { Locales } from '@/locales/resources';
5
5
  import { GeneralModelProviderConfig, GlobalLLMProviderKey, GlobalSettings } from '@/types/settings';
6
6
  import { isOnServerSide } from '@/utils/env';
@@ -21,6 +21,7 @@ const password = (s: UserStore) => currentSettings(s).password;
21
21
  const currentTTS = (s: UserStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts);
22
22
 
23
23
  const defaultAgent = (s: UserStore) => merge(DEFAULT_AGENT, currentSettings(s).defaultAgent);
24
+ const defaultAgentConfig = (s: UserStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
24
25
 
25
26
  const defaultAgentMeta = (s: UserStore) => merge(DEFAULT_AGENT_META, defaultAgent(s).meta);
26
27
 
@@ -53,6 +54,7 @@ export const settingsSelectors = {
53
54
  currentTTS,
54
55
  dalleConfig,
55
56
  defaultAgent,
57
+ defaultAgentConfig,
56
58
  defaultAgentMeta,
57
59
  exportSettings,
58
60
  isDalleAutoGenerating,