@lobehub/chat 0.160.5 → 0.160.7

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 (60) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/locales/ar/error.json +2 -0
  3. package/locales/bg-BG/error.json +2 -0
  4. package/locales/de-DE/error.json +2 -0
  5. package/locales/en-US/error.json +2 -0
  6. package/locales/es-ES/error.json +2 -0
  7. package/locales/fr-FR/error.json +2 -0
  8. package/locales/it-IT/error.json +2 -0
  9. package/locales/ja-JP/error.json +2 -0
  10. package/locales/ko-KR/error.json +2 -0
  11. package/locales/nl-NL/error.json +2 -0
  12. package/locales/pl-PL/error.json +2 -0
  13. package/locales/pt-BR/error.json +2 -0
  14. package/locales/ru-RU/error.json +2 -0
  15. package/locales/tr-TR/error.json +2 -0
  16. package/locales/vi-VN/error.json +2 -0
  17. package/locales/zh-CN/error.json +2 -0
  18. package/locales/zh-TW/error.json +2 -0
  19. package/package.json +2 -2
  20. package/src/app/(loading)/Redirect.tsx +8 -0
  21. package/src/app/(main)/chat/(workspace)/features/TelemetryNotification.tsx +42 -75
  22. package/src/app/(main)/market/_layout/Desktop/index.tsx +24 -20
  23. package/src/app/(main)/settings/llm/Azure/index.tsx +1 -0
  24. package/src/app/(main)/settings/llm/OpenAI/index.tsx +0 -1
  25. package/src/app/trpc/edge/[trpc]/route.ts +1 -1
  26. package/src/components/FetchErrorNotification/Description.tsx +48 -0
  27. package/src/components/FetchErrorNotification/index.tsx +15 -0
  28. package/src/components/ModelProviderIcon/index.tsx +6 -1
  29. package/src/components/Notification/index.tsx +95 -0
  30. package/src/config/server/provider.ts +1 -0
  31. package/src/database/client/models/__tests__/message.test.ts +2 -2
  32. package/src/database/client/models/message.ts +2 -2
  33. package/src/features/ModelSwitchPanel/index.tsx +6 -3
  34. package/src/features/User/UserAvatar.tsx +4 -3
  35. package/src/locales/default/error.ts +3 -1
  36. package/src/middleware.ts +10 -1
  37. package/src/server/globalConfig/index.ts +2 -0
  38. package/src/server/routers/edge/config/__snapshots__/index.test.ts.snap +1 -0
  39. package/src/services/file/type.ts +2 -3
  40. package/src/services/message/client.test.ts +151 -23
  41. package/src/services/message/client.ts +9 -5
  42. package/src/services/message/type.ts +10 -3
  43. package/src/services/upload.ts +4 -4
  44. package/src/services/user/client.ts +17 -1
  45. package/src/services/user/type.ts +14 -0
  46. package/src/store/agent/slices/chat/selectors.test.ts +2 -2
  47. package/src/store/agent/slices/chat/selectors.ts +1 -1
  48. package/src/store/chat/slices/enchance/action.test.ts +4 -3
  49. package/src/store/chat/slices/enchance/action.ts +3 -2
  50. package/src/store/chat/slices/plugin/action.test.ts +3 -5
  51. package/src/store/chat/slices/plugin/action.ts +1 -1
  52. package/src/store/market/action.ts +7 -0
  53. package/src/store/user/slices/auth/selectors.ts +1 -1
  54. package/src/store/user/slices/preference/action.test.ts +4 -9
  55. package/src/store/user/slices/preference/action.ts +17 -20
  56. package/src/store/user/slices/settings/selectors/modelConfig.test.ts +29 -1
  57. package/src/store/user/slices/settings/selectors/modelConfig.ts +1 -1
  58. package/src/tools/dalle/Render/Item/Error.tsx +1 -1
  59. package/src/types/files.ts +33 -0
  60. /package/src/types/{user.ts → user/index.ts} +0 -0
@@ -105,12 +105,13 @@ export const chatEnhance: StateCreator<
105
105
  },
106
106
 
107
107
  updateMessageTTS: async (id, data) => {
108
- await messageService.updateMessage(id, { tts: data as ChatTTS });
108
+ await messageService.updateMessageTTS(id, data);
109
109
  await get().refreshMessages();
110
110
  },
111
111
 
112
112
  updateMessageTranslate: async (id, data) => {
113
- await messageService.updateMessage(id, { translate: data as ChatTranslate });
113
+ await messageService.updateMessageTranslate(id, data);
114
+
114
115
  await get().refreshMessages();
115
116
  },
116
117
  });
@@ -372,11 +372,9 @@ describe('ChatPluginAction', () => {
372
372
  await result.current.updatePluginState(messageId, pluginStateKey, pluginStateValue);
373
373
  });
374
374
 
375
- expect(messageService.updateMessagePluginState).toHaveBeenCalledWith(
376
- messageId,
377
- pluginStateKey,
378
- pluginStateValue,
379
- );
375
+ expect(messageService.updateMessagePluginState).toHaveBeenCalledWith(messageId, {
376
+ [pluginStateKey]: pluginStateValue,
377
+ });
380
378
  expect(initialState.refreshMessages).toHaveBeenCalled();
381
379
  });
382
380
  });
@@ -298,7 +298,7 @@ export const chatPlugin: StateCreator<
298
298
  updatePluginState: async (id, key, value) => {
299
299
  const { refreshMessages } = get();
300
300
 
301
- await messageService.updateMessagePluginState(id, key, value);
301
+ await messageService.updateMessagePluginState(id, { [key]: value });
302
302
  await refreshMessages();
303
303
  },
304
304
  });
@@ -12,6 +12,7 @@ import type { Store } from './store';
12
12
  export interface StoreAction {
13
13
  activateAgent: (identifier: string) => void;
14
14
  deactivateAgent: () => void;
15
+ getAgentById: (identifiers: string) => Promise<AgentsMarketItem>;
15
16
  setSearchKeywords: (keywords: string) => void;
16
17
  updateAgentMap: (key: string, value: AgentsMarketItem) => void;
17
18
  useFetchAgent: (identifier: string) => SWRResponse<AgentsMarketItem>;
@@ -30,6 +31,12 @@ export const createMarketAction: StateCreator<
30
31
  deactivateAgent: () => {
31
32
  set({ currentIdentifier: '' }, false, 'deactivateAgent');
32
33
  },
34
+
35
+ getAgentById: async (identifier) => {
36
+ const locale = globalHelpers.getCurrentLanguage();
37
+ return marketService.getAgentManifest(identifier, locale as string);
38
+ },
39
+
33
40
  setSearchKeywords: (keywords) => {
34
41
  set({ searchKeywords: keywords });
35
42
  },
@@ -43,5 +43,5 @@ const isLogin = (s: UserStore) => {
43
43
  export const authSelectors = {
44
44
  isLogin,
45
45
  isLoginWithAuth: (s: UserStore) => s.isSignedIn,
46
- isLoginWithClerk: (s: UserStore) => s.isSignedIn && enableClerk,
46
+ isLoginWithClerk: (s: UserStore): boolean => (s.isSignedIn && enableClerk) || false,
47
47
  };
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
  import { withSWR } from '~test-utils';
4
4
 
5
5
  import { DEFAULT_PREFERENCE } from '@/const/user';
6
+ import { userService } from '@/services/user';
6
7
  import { useUserStore } from '@/store/user';
7
8
  import { UserGuide, UserPreference } from '@/types/user';
8
9
 
@@ -44,9 +45,7 @@ describe('createPreferenceSlice', () => {
44
45
  it('should return false when userId is empty', async () => {
45
46
  const { result } = renderHook(() => useUserStore());
46
47
 
47
- vi.spyOn(result.current.preferenceStorage, 'getFromLocalStorage').mockResolvedValueOnce(
48
- {} as any,
49
- );
48
+ vi.spyOn(userService, 'getPreference').mockResolvedValueOnce({} as any);
50
49
 
51
50
  const { result: prefernce } = renderHook(() => result.current.useInitPreference(), {
52
51
  wrapper: withSWR,
@@ -60,9 +59,7 @@ describe('createPreferenceSlice', () => {
60
59
  it('should return default preference when local storage is empty', async () => {
61
60
  const { result } = renderHook(() => useUserStore());
62
61
 
63
- vi.spyOn(result.current.preferenceStorage, 'getFromLocalStorage').mockResolvedValueOnce(
64
- {} as any,
65
- );
62
+ vi.spyOn(userService, 'getPreference').mockResolvedValueOnce({} as any);
66
63
 
67
64
  renderHook(() => result.current.useInitPreference(), {
68
65
  wrapper: withSWR,
@@ -82,9 +79,7 @@ describe('createPreferenceSlice', () => {
82
79
  guide: { topic: false, moveSettingsToAvatar: true },
83
80
  };
84
81
 
85
- vi.spyOn(result.current.preferenceStorage, 'getFromLocalStorage').mockResolvedValueOnce(
86
- savedPreference,
87
- );
82
+ vi.spyOn(userService, 'getPreference').mockResolvedValueOnce(savedPreference);
88
83
 
89
84
  const { result: prefernce } = renderHook(() => result.current.useInitPreference(), {
90
85
  wrapper: withSWR,
@@ -3,6 +3,7 @@ import type { StateCreator } from 'zustand/vanilla';
3
3
 
4
4
  import { DEFAULT_PREFERENCE } from '@/const/user';
5
5
  import { useClientDataSWR } from '@/libs/swr';
6
+ import { userService } from '@/services/user';
6
7
  import type { UserStore } from '@/store/user';
7
8
  import { UserGuide, UserPreference } from '@/types/user';
8
9
  import { merge } from '@/utils/merge';
@@ -11,8 +12,8 @@ import { setNamespace } from '@/utils/storeDebug';
11
12
  const n = setNamespace('preference');
12
13
 
13
14
  export interface PreferenceAction {
14
- updateGuideState: (guide: Partial<UserGuide>) => void;
15
- updatePreference: (preference: Partial<UserPreference>, action?: any) => void;
15
+ updateGuideState: (guide: Partial<UserGuide>) => Promise<void>;
16
+ updatePreference: (preference: Partial<UserPreference>, action?: any) => Promise<void>;
16
17
  useInitPreference: () => SWRResponse;
17
18
  }
18
19
 
@@ -22,33 +23,29 @@ export const createPreferenceSlice: StateCreator<
22
23
  [],
23
24
  PreferenceAction
24
25
  > = (set, get) => ({
25
- updateGuideState: (guide) => {
26
+ updateGuideState: async (guide) => {
26
27
  const { updatePreference } = get();
27
28
  const nextGuide = merge(get().preference.guide, guide);
28
- updatePreference({ guide: nextGuide });
29
+ await updatePreference({ guide: nextGuide });
29
30
  },
30
- updatePreference: (preference, action) => {
31
+ updatePreference: async (preference, action) => {
31
32
  const nextPreference = merge(get().preference, preference);
32
33
 
33
34
  set({ preference: nextPreference }, false, action || n('updatePreference'));
34
35
 
35
- get().preferenceStorage.saveToLocalStorage(nextPreference);
36
+ await userService.updatePreference(nextPreference);
36
37
  },
37
38
 
38
39
  useInitPreference: () =>
39
- useClientDataSWR<UserPreference>(
40
- 'initUserPreference',
41
- () => get().preferenceStorage.getFromLocalStorage(),
42
- {
43
- onSuccess: (preference) => {
44
- const isEmpty = Object.keys(preference).length === 0;
45
-
46
- set(
47
- { isPreferenceInit: true, preference: isEmpty ? DEFAULT_PREFERENCE : preference },
48
- false,
49
- n('initPreference'),
50
- );
51
- },
40
+ useClientDataSWR<UserPreference>('initUserPreference', userService.getPreference, {
41
+ onSuccess: (preference) => {
42
+ const isEmpty = Object.keys(preference).length === 0;
43
+
44
+ set(
45
+ { isPreferenceInit: true, preference: isEmpty ? DEFAULT_PREFERENCE : preference },
46
+ false,
47
+ n('initPreference'),
48
+ );
52
49
  },
53
- ),
50
+ }),
54
51
  });
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import { UserStore } from '@/store/user';
3
4
  import { merge } from '@/utils/merge';
4
5
 
5
- import { UserStore, useUserStore } from '../../../store';
6
6
  import { UserSettingsState, initialSettingsState } from '../initialState';
7
7
  import { modelConfigSelectors } from './modelConfig';
8
8
 
@@ -33,6 +33,34 @@ describe('modelConfigSelectors', () => {
33
33
  });
34
34
  });
35
35
 
36
+ describe('isProviderFetchOnClient', () => {
37
+ it('client fetch should disabled on default', () => {
38
+ const s = merge(initialSettingsState, {
39
+ settings: {
40
+ languageModel: {
41
+ azure: {
42
+ endpoint: 'endpoint',
43
+ apiKey: 'apikey',
44
+ },
45
+ },
46
+ },
47
+ } as UserSettingsState) as unknown as UserStore;
48
+
49
+ expect(modelConfigSelectors.isProviderFetchOnClient('azure')(s)).toBe(false);
50
+ });
51
+
52
+ it('client fetch should enabled if user set it enabled', () => {
53
+ const s = merge(initialSettingsState, {
54
+ settings: {
55
+ languageModel: {
56
+ azure: { fetchOnClient: true },
57
+ },
58
+ },
59
+ } as UserSettingsState) as unknown as UserStore;
60
+ expect(modelConfigSelectors.isProviderFetchOnClient('azure')(s)).toBe(true);
61
+ });
62
+ });
63
+
36
64
  describe('getCustomModelCardById', () => {
37
65
  it('should return the custom model card with the given id and provider', () => {
38
66
  const s = merge(initialSettingsState, {
@@ -13,7 +13,7 @@ const isProviderFetchOnClient = (provider: GlobalLLMProviderKey | string) => (s:
13
13
  const config = getProviderConfigById(provider)(s);
14
14
  if (typeof config?.fetchOnClient !== 'undefined') return config?.fetchOnClient;
15
15
 
16
- return isProviderEndpointNotEmpty(provider)(s);
16
+ return false;
17
17
  };
18
18
 
19
19
  const getCustomModelCard =
@@ -28,7 +28,7 @@ const Error = memo<ErrorProps>(({ messageId, index }) => {
28
28
  <Alert
29
29
  extra={
30
30
  <Highlighter copyButtonSize={'small'} language={'json'}>
31
- {JSON.stringify(error.body, null, 2)}
31
+ {JSON.stringify(error?.body || error, null, 2)}
32
32
  </Highlighter>
33
33
  }
34
34
  extraDefaultExpand
@@ -1,3 +1,5 @@
1
+ import { z } from 'zod';
2
+
1
3
  export interface FilePreview {
2
4
  base64Url?: string;
3
5
  data?: ArrayBuffer;
@@ -6,3 +8,34 @@ export interface FilePreview {
6
8
  saveMode: 'local' | 'url';
7
9
  url: string;
8
10
  }
11
+
12
+ export const UploadFileSchema = z.object({
13
+ data: z.instanceof(ArrayBuffer).optional(),
14
+ /**
15
+ * file type
16
+ * @example 'image/png'
17
+ */
18
+ fileType: z.string(),
19
+ metadata: z.any().optional(),
20
+ /**
21
+ * file name
22
+ * @example 'test.png'
23
+ */
24
+ name: z.string(),
25
+ /**
26
+ * the mode database save the file
27
+ * local mean save the raw file into data
28
+ * url mean upload the file to a cdn and then save the url
29
+ */
30
+ saveMode: z.enum(['local', 'url']),
31
+ /**
32
+ * file size
33
+ */
34
+ size: z.number(),
35
+ /**
36
+ * file url if saveMode is url
37
+ */
38
+ url: z.string().optional(),
39
+ });
40
+
41
+ export type UploadFileParams = z.infer<typeof UploadFileSchema>;
File without changes