@lobehub/chat 1.136.13 → 1.137.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.
Files changed (66) hide show
  1. package/.cursor/rules/add-setting-env.mdc +175 -0
  2. package/.cursor/rules/db-migrations.mdc +25 -0
  3. package/.env.example +7 -0
  4. package/CHANGELOG.md +25 -0
  5. package/changelog/v1.json +9 -0
  6. package/docs/development/database-schema.dbml +1 -0
  7. package/docs/self-hosting/advanced/feature-flags.mdx +25 -15
  8. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +25 -15
  9. package/docs/self-hosting/environment-variables/basic.mdx +12 -0
  10. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +12 -0
  11. package/locales/ar/setting.json +8 -0
  12. package/locales/bg-BG/setting.json +8 -0
  13. package/locales/de-DE/setting.json +8 -0
  14. package/locales/en-US/setting.json +8 -0
  15. package/locales/es-ES/setting.json +8 -0
  16. package/locales/fa-IR/setting.json +8 -0
  17. package/locales/fr-FR/setting.json +8 -0
  18. package/locales/it-IT/setting.json +8 -0
  19. package/locales/ja-JP/setting.json +8 -0
  20. package/locales/ko-KR/setting.json +8 -0
  21. package/locales/nl-NL/setting.json +8 -0
  22. package/locales/pl-PL/setting.json +8 -0
  23. package/locales/pt-BR/setting.json +8 -0
  24. package/locales/ru-RU/setting.json +8 -0
  25. package/locales/tr-TR/setting.json +8 -0
  26. package/locales/vi-VN/setting.json +8 -0
  27. package/locales/zh-CN/setting.json +8 -0
  28. package/locales/zh-TW/setting.json +8 -0
  29. package/package.json +1 -1
  30. package/packages/const/src/settings/image.ts +8 -0
  31. package/packages/const/src/settings/index.ts +3 -0
  32. package/packages/context-engine/src/__tests__/pipeline.test.ts +485 -0
  33. package/packages/context-engine/src/base/__tests__/BaseProcessor.test.ts +381 -0
  34. package/packages/context-engine/src/base/__tests__/BaseProvider.test.ts +392 -0
  35. package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +346 -0
  36. package/packages/context-engine/src/processors/__tests__/ToolCall.test.ts +552 -0
  37. package/packages/database/migrations/0038_add_image_user_settings.sql +1 -0
  38. package/packages/database/migrations/meta/0038_snapshot.json +7580 -0
  39. package/packages/database/migrations/meta/_journal.json +7 -0
  40. package/packages/database/src/core/migrations.json +6 -0
  41. package/packages/database/src/models/user.ts +3 -1
  42. package/packages/database/src/schemas/user.ts +1 -0
  43. package/packages/file-loaders/src/loaders/docx/index.test.ts +0 -1
  44. package/packages/file-loaders/src/loaders/excel/__snapshots__/index.test.ts.snap +30 -0
  45. package/packages/file-loaders/src/loaders/excel/index.test.ts +8 -0
  46. package/packages/file-loaders/src/loaders/pptx/index.test.ts +25 -0
  47. package/packages/file-loaders/src/utils/parser-utils.test.ts +155 -0
  48. package/packages/file-loaders/vitest.config.mts +8 -0
  49. package/packages/types/src/serverConfig.ts +7 -1
  50. package/packages/types/src/user/settings/image.ts +3 -0
  51. package/packages/types/src/user/settings/index.ts +3 -0
  52. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +3 -0
  53. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +8 -3
  54. package/src/app/[variants]/(main)/settings/image/index.tsx +74 -0
  55. package/src/components/FormInput/FormSliderWithInput.tsx +40 -0
  56. package/src/components/FormInput/index.ts +1 -0
  57. package/src/envs/image.ts +27 -0
  58. package/src/hooks/useFetchAiImageConfig.ts +12 -17
  59. package/src/locales/default/setting.ts +8 -0
  60. package/src/server/globalConfig/index.ts +5 -0
  61. package/src/store/global/initialState.ts +1 -0
  62. package/src/store/image/slices/generationConfig/action.test.ts +17 -0
  63. package/src/store/image/slices/generationConfig/action.ts +18 -21
  64. package/src/store/image/slices/generationConfig/initialState.ts +3 -2
  65. package/src/store/user/slices/common/action.ts +1 -0
  66. package/src/store/user/slices/settings/selectors/settings.ts +3 -0
@@ -3,10 +3,12 @@ import { isDesktop } from '@/const/version';
3
3
  import { appEnv, getAppConfig } from '@/envs/app';
4
4
  import { authEnv } from '@/envs/auth';
5
5
  import { fileEnv } from '@/envs/file';
6
+ import { imageEnv } from '@/envs/image';
6
7
  import { knowledgeEnv } from '@/envs/knowledge';
7
8
  import { langfuseEnv } from '@/envs/langfuse';
8
9
  import { parseSystemAgent } from '@/server/globalConfig/parseSystemAgent';
9
10
  import { GlobalServerConfig } from '@/types/serverConfig';
11
+ import { cleanObject } from '@/utils/object';
10
12
 
11
13
  import { genServerLLMConfig } from './_deprecated';
12
14
  import { genServerAiProvidersConfig } from './genServerAiProviderConfig';
@@ -61,6 +63,9 @@ export const getServerGlobalConfig = async () => {
61
63
  enabledAccessCode: ACCESS_CODES?.length > 0,
62
64
 
63
65
  enabledOAuthSSO: enableNextAuth,
66
+ image: cleanObject({
67
+ defaultImageNum: imageEnv.AI_IMAGE_DEFAULT_IMAGE_NUM,
68
+ }),
64
69
  /**
65
70
  * @deprecated
66
71
  */
@@ -30,6 +30,7 @@ export enum SettingsTabs {
30
30
  Agent = 'agent',
31
31
  Common = 'common',
32
32
  Hotkey = 'hotkey',
33
+ Image = 'image',
33
34
  LLM = 'llm',
34
35
  Provider = 'provider',
35
36
  Proxy = 'proxy',
@@ -6,6 +6,18 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
7
  import { useImageStore } from '@/store/image';
8
8
 
9
+ const { currentImageSettingsMock } = vi.hoisted(() => ({
10
+ currentImageSettingsMock: vi.fn(() => ({
11
+ defaultImageNum: 4,
12
+ })),
13
+ }));
14
+
15
+ vi.mock('@/store/user/slices/settings/selectors', () => ({
16
+ settingsSelectors: {
17
+ currentImageSettings: currentImageSettingsMock,
18
+ },
19
+ }));
20
+
9
21
  // Test fixtures
10
22
  const customModelSchema: ModelParamsSchema = {
11
23
  prompt: { default: '' },
@@ -74,6 +86,7 @@ const initialTestState = {
74
86
 
75
87
  beforeEach(() => {
76
88
  vi.clearAllMocks();
89
+ currentImageSettingsMock.mockReturnValue({ defaultImageNum: 4 });
77
90
  useImageStore.setState(initialTestState);
78
91
  });
79
92
 
@@ -483,6 +496,7 @@ describe('GenerationConfigAction', () => {
483
496
  });
484
497
 
485
498
  it('should initialize with remembered model when user is logged in', () => {
499
+ currentImageSettingsMock.mockReturnValueOnce({ defaultImageNum: 6 });
486
500
  const { result } = renderHook(() => useImageStore());
487
501
 
488
502
  useImageStore.setState({
@@ -499,6 +513,7 @@ describe('GenerationConfigAction', () => {
499
513
  expect(result.current.provider).toBe('fal');
500
514
  expect(result.current.parameters).toEqual(fluxSchnellDefaultValues);
501
515
  expect(result.current.isInit).toBe(true);
516
+ expect(result.current.imageNum).toBe(6);
502
517
  });
503
518
 
504
519
  it('should handle initialization without remembered preferences', () => {
@@ -511,6 +526,7 @@ describe('GenerationConfigAction', () => {
511
526
  });
512
527
 
513
528
  expect(result.current.isInit).toBe(true);
529
+ expect(result.current.imageNum).toBe(4);
514
530
  });
515
531
 
516
532
  it('should handle initialization errors gracefully', () => {
@@ -523,6 +539,7 @@ describe('GenerationConfigAction', () => {
523
539
  });
524
540
 
525
541
  expect(result.current.isInit).toBe(true);
542
+ expect(result.current.imageNum).toBe(4);
526
543
  });
527
544
  });
528
545
  });
@@ -12,6 +12,7 @@ import { aiProviderSelectors, getAiInfraStoreState } from '@/store/aiInfra';
12
12
  import { useGlobalStore } from '@/store/global';
13
13
  import { useUserStore } from '@/store/user';
14
14
  import { authSelectors } from '@/store/user/selectors';
15
+ import { settingsSelectors } from '@/store/user/slices/settings/selectors';
15
16
 
16
17
  import type { ImageStore } from '../../store';
17
18
  import { calculateInitialAspectRatio } from '../../utils/aspectRatio';
@@ -40,6 +41,7 @@ export interface GenerationConfigAction {
40
41
  setAspectRatio(aspectRatio: string): void;
41
42
 
42
43
  // 初始化相关方法
44
+ _initializeDefaultImageConfig(): void;
43
45
  initializeImageConfig(
44
46
  isLogin?: boolean,
45
47
  lastSelectedImageModel?: string,
@@ -327,46 +329,41 @@ export const createGenerationConfigSlice: StateCreator<
327
329
  set((state) => ({ parameters: { ...state.parameters, seed } }), false, `reuseSeed/${seed}`);
328
330
  },
329
331
 
332
+ _initializeDefaultImageConfig: () => {
333
+ const { defaultImageNum } = settingsSelectors.currentImageSettings(useUserStore.getState());
334
+ set({ imageNum: defaultImageNum, isInit: true }, false, 'initializeImageConfig/default');
335
+ },
336
+
330
337
  initializeImageConfig: (isLogin, lastSelectedImageModel, lastSelectedImageProvider) => {
331
- // If no parameters are passed, get from store (backward compatibility)
332
- let actualIsLogin = isLogin;
333
- let actualLastSelectedImageModel = lastSelectedImageModel;
334
- let actualLastSelectedImageProvider = lastSelectedImageProvider;
335
-
336
- if (typeof isLogin === 'undefined') {
337
- const globalStatus = useGlobalStore.getState().status;
338
- actualIsLogin = authSelectors.isLogin(useUserStore.getState());
339
- actualLastSelectedImageModel = globalStatus.lastSelectedImageModel;
340
- actualLastSelectedImageProvider = globalStatus.lastSelectedImageProvider;
341
- }
338
+ const { _initializeDefaultImageConfig } = get();
339
+ const { defaultImageNum } = settingsSelectors.currentImageSettings(useUserStore.getState());
342
340
 
343
- if (actualIsLogin && actualLastSelectedImageModel && actualLastSelectedImageProvider) {
341
+ if (isLogin && lastSelectedImageModel && lastSelectedImageProvider) {
344
342
  try {
345
343
  const { defaultValues, parametersSchema, initialActiveRatio } = prepareModelConfigState(
346
- actualLastSelectedImageModel,
347
- actualLastSelectedImageProvider,
344
+ lastSelectedImageModel,
345
+ lastSelectedImageProvider,
348
346
  );
349
347
 
350
348
  set(
351
349
  {
352
- model: actualLastSelectedImageModel,
353
- provider: actualLastSelectedImageProvider,
350
+ model: lastSelectedImageModel,
351
+ provider: lastSelectedImageProvider,
354
352
  parameters: defaultValues,
355
353
  parametersSchema,
356
354
  isAspectRatioLocked: false,
357
355
  activeAspectRatio: initialActiveRatio,
356
+ imageNum: defaultImageNum,
358
357
  isInit: true,
359
358
  },
360
359
  false,
361
- `initializeImageConfig/${actualLastSelectedImageModel}/${actualLastSelectedImageProvider}`,
360
+ `initializeImageConfig/${lastSelectedImageModel}/${lastSelectedImageProvider}`,
362
361
  );
363
362
  } catch {
364
- // If restoration fails, simply mark as initialized to use default configuration
365
- set({ isInit: true }, false, 'initializeImageConfig/fallback');
363
+ _initializeDefaultImageConfig();
366
364
  }
367
365
  } else {
368
- // No remembered model, directly mark as initialized (use default values)
369
- set({ isInit: true }, false, 'initializeImageConfig/default');
366
+ _initializeDefaultImageConfig();
370
367
  }
371
368
  },
372
369
  });
@@ -7,9 +7,10 @@ import {
7
7
  gptImage1ParamsSchema,
8
8
  } from 'model-bank';
9
9
 
10
+ import { DEFAULT_IMAGE_CONFIG } from '@/const/settings';
11
+
10
12
  export const DEFAULT_AI_IMAGE_PROVIDER = ModelProvider.OpenAI;
11
13
  export const DEFAULT_AI_IMAGE_MODEL = 'gpt-image-1';
12
- export const DEFAULT_IMAGE_NUM = 4;
13
14
 
14
15
  export interface GenerationConfigState {
15
16
  parameters: RuntimeImageGenParams;
@@ -34,7 +35,7 @@ export const DEFAULT_IMAGE_GENERATION_PARAMETERS: RuntimeImageGenParams =
34
35
  export const initialGenerationConfigState: GenerationConfigState = {
35
36
  model: DEFAULT_AI_IMAGE_MODEL,
36
37
  provider: DEFAULT_AI_IMAGE_PROVIDER,
37
- imageNum: DEFAULT_IMAGE_NUM,
38
+ imageNum: DEFAULT_IMAGE_CONFIG.defaultImageNum,
38
39
  parameters: DEFAULT_IMAGE_GENERATION_PARAMETERS,
39
40
  parametersSchema: gptImage1ParamsSchema,
40
41
  isAspectRatioLocked: false,
@@ -78,6 +78,7 @@ export const createCommonSlice: StateCreator<
78
78
  // merge settings
79
79
  const serverSettings: PartialDeep<UserSettings> = {
80
80
  defaultAgent: serverConfig.defaultAgent,
81
+ image: serverConfig.image,
81
82
  languageModel: serverConfig.languageModel,
82
83
  systemAgent: serverConfig.systemAgent,
83
84
  };
@@ -25,6 +25,8 @@ export const currentLLMSettings = (s: UserStore): UserModelProviderConfig =>
25
25
  export const getProviderConfigById = (provider: string) => (s: UserStore) =>
26
26
  currentLLMSettings(s)[provider as GlobalLLMProviderKey] as ProviderConfig | undefined;
27
27
 
28
+ const currentImageSettings = (s: UserStore) => currentSettings(s).image;
29
+
28
30
  const currentTTS = (s: UserStore) => merge(DEFAULT_TTS_CONFIG, currentSettings(s).tts);
29
31
 
30
32
  const defaultAgent = (s: UserStore) => merge(DEFAULT_AGENT, currentSettings(s).defaultAgent);
@@ -44,6 +46,7 @@ const getHotkeyById = (id: HotkeyId) => (s: UserStore) =>
44
46
  merge(DEFAULT_HOTKEY_CONFIG, currentSettings(s).hotkey)[id];
45
47
 
46
48
  export const settingsSelectors = {
49
+ currentImageSettings,
47
50
  currentSettings,
48
51
  currentSystemAgent,
49
52
  currentTTS,