@lobehub/chat 0.160.8 → 0.161.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/setting.json +17 -0
  3. package/locales/bg-BG/setting.json +17 -0
  4. package/locales/de-DE/setting.json +17 -0
  5. package/locales/en-US/setting.json +17 -0
  6. package/locales/es-ES/setting.json +17 -0
  7. package/locales/fr-FR/setting.json +17 -0
  8. package/locales/it-IT/setting.json +17 -0
  9. package/locales/ja-JP/setting.json +17 -0
  10. package/locales/ko-KR/setting.json +17 -0
  11. package/locales/nl-NL/setting.json +17 -0
  12. package/locales/pl-PL/setting.json +37 -20
  13. package/locales/pt-BR/setting.json +17 -0
  14. package/locales/ru-RU/setting.json +37 -20
  15. package/locales/tr-TR/setting.json +37 -20
  16. package/locales/vi-VN/setting.json +17 -0
  17. package/locales/zh-CN/clerk.json +1 -1
  18. package/locales/zh-CN/setting.json +17 -0
  19. package/locales/zh-TW/clerk.json +1 -1
  20. package/locales/zh-TW/setting.json +17 -0
  21. package/package.json +23 -23
  22. package/src/app/(main)/settings/_layout/Desktop/Header.tsx +58 -25
  23. package/src/app/(main)/settings/_layout/Desktop/index.tsx +18 -1
  24. package/src/app/(main)/settings/common/page.tsx +1 -0
  25. package/src/app/(main)/settings/hooks/useCategory.tsx +49 -12
  26. package/src/app/(main)/settings/system-agent/features/Translation.tsx +60 -0
  27. package/src/app/(main)/settings/system-agent/index.tsx +9 -0
  28. package/src/app/(main)/settings/system-agent/page.tsx +14 -0
  29. package/src/app/@modal/(.)settings/modal/index.tsx +5 -0
  30. package/src/app/@modal/(.)settings/modal/layout.tsx +13 -1
  31. package/src/app/@modal/_layout/SettingModalLayout.tsx +45 -41
  32. package/src/app/@modal/chat/(.)settings/modal/features/CategoryContent.tsx +1 -1
  33. package/src/app/@modal/chat/(.)settings/modal/features/useCategory.tsx +6 -6
  34. package/src/app/@modal/chat/(.)settings/modal/layout.tsx +4 -0
  35. package/src/app/@modal/layout.tsx +1 -0
  36. package/src/const/settings/agent.ts +36 -0
  37. package/src/const/settings/common.ts +8 -0
  38. package/src/const/settings/index.ts +15 -172
  39. package/src/const/settings/llm.ts +110 -0
  40. package/src/const/settings/sync.ts +5 -0
  41. package/src/const/settings/systemAgent.ts +12 -0
  42. package/src/const/settings/tool.ts +5 -0
  43. package/src/const/settings/tts.ts +10 -0
  44. package/src/features/AgentSetting/AgentModal/ModelSelect.tsx +4 -52
  45. package/src/features/ModelSelect/index.tsx +67 -0
  46. package/src/layout/AuthProvider/Clerk/useAppearance.ts +4 -0
  47. package/src/locales/default/clerk.ts +1 -1
  48. package/src/locales/default/setting.ts +24 -7
  49. package/src/server/translation.ts +22 -8
  50. package/src/store/chat/slices/enchance/action.ts +9 -2
  51. package/src/store/global/initialState.ts +1 -0
  52. package/src/store/user/selectors.ts +1 -0
  53. package/src/store/user/slices/settings/actions/general.test.ts +22 -0
  54. package/src/store/user/slices/settings/actions/general.ts +11 -0
  55. package/src/store/user/slices/settings/selectors/index.ts +1 -0
  56. package/src/store/user/slices/settings/selectors/settings.ts +10 -1
  57. package/src/store/user/slices/settings/selectors/systemAgent.ts +14 -0
  58. package/src/types/settings/index.ts +3 -0
  59. package/src/types/settings/systemAgent.ts +8 -0
@@ -1,65 +1,17 @@
1
- import { Select, SelectProps } from 'antd';
2
- import { createStyles } from 'antd-style';
3
- import isEqual from 'fast-deep-equal';
4
- import { memo, useMemo } from 'react';
1
+ import { memo } from 'react';
5
2
 
6
- import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
7
- import { useUserStore } from '@/store/user';
8
- import { modelProviderSelectors } from '@/store/user/selectors';
9
- import { ModelProviderCard } from '@/types/llm';
3
+ import Select from '@/features/ModelSelect';
10
4
 
11
5
  import { useStore } from '../store';
12
6
 
13
- const useStyles = createStyles(({ css, prefixCls }) => ({
14
- select: css`
15
- .${prefixCls}-select-dropdown .${prefixCls}-select-item-option-grouped {
16
- padding-inline-start: 12px;
17
- }
18
- `,
19
- }));
20
- interface ModelOption {
21
- label: any;
22
- provider: string;
23
- value: string;
24
- }
25
-
26
7
  const ModelSelect = memo(() => {
27
8
  const [model, updateConfig] = useStore((s) => [s.config.model, s.setAgentConfig]);
28
- const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual);
29
-
30
- const { styles } = useStyles();
31
-
32
- const options = useMemo<SelectProps['options']>(() => {
33
- const getChatModels = (provider: ModelProviderCard) =>
34
- provider.chatModels.map((model) => ({
35
- label: <ModelItemRender {...model} />,
36
- provider: provider.id,
37
- value: model.id,
38
- }));
39
-
40
- if (enabledList.length === 1) {
41
- const provider = enabledList[0];
42
-
43
- return getChatModels(provider);
44
- }
45
-
46
- return enabledList.map((provider) => ({
47
- label: <ProviderItemRender provider={provider.id} />,
48
- options: getChatModels(provider),
49
- }));
50
- }, [enabledList]);
51
9
 
52
10
  return (
53
11
  <Select
54
- className={styles.select}
55
- onChange={(model, option) => {
56
- updateConfig({
57
- model,
58
- provider: (option as unknown as ModelOption).provider,
59
- });
12
+ onChange={(props) => {
13
+ updateConfig(props);
60
14
  }}
61
- options={options}
62
- popupMatchSelectWidth={false}
63
15
  value={model}
64
16
  />
65
17
  );
@@ -0,0 +1,67 @@
1
+ import { Select, SelectProps } from 'antd';
2
+ import { createStyles } from 'antd-style';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { memo, useMemo } from 'react';
5
+
6
+ import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
7
+ import { useUserStore } from '@/store/user';
8
+ import { modelProviderSelectors } from '@/store/user/selectors';
9
+ import { ModelProviderCard } from '@/types/llm';
10
+
11
+ const useStyles = createStyles(({ css, prefixCls }) => ({
12
+ select: css`
13
+ .${prefixCls}-select-dropdown .${prefixCls}-select-item-option-grouped {
14
+ padding-inline-start: 12px;
15
+ }
16
+ `,
17
+ }));
18
+ interface ModelOption {
19
+ label: any;
20
+ provider: string;
21
+ value: string;
22
+ }
23
+
24
+ interface ModelSelectProps {
25
+ onChange?: (props: { model: string; provider: string }) => void;
26
+ value?: string;
27
+ }
28
+
29
+ const ModelSelect = memo<ModelSelectProps>(({ value, onChange }) => {
30
+ const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual);
31
+
32
+ const { styles } = useStyles();
33
+
34
+ const options = useMemo<SelectProps['options']>(() => {
35
+ const getChatModels = (provider: ModelProviderCard) =>
36
+ provider.chatModels.map((model) => ({
37
+ label: <ModelItemRender {...model} />,
38
+ provider: provider.id,
39
+ value: model.id,
40
+ }));
41
+
42
+ if (enabledList.length === 1) {
43
+ const provider = enabledList[0];
44
+
45
+ return getChatModels(provider);
46
+ }
47
+
48
+ return enabledList.map((provider) => ({
49
+ label: <ProviderItemRender provider={provider.id} />,
50
+ options: getChatModels(provider),
51
+ }));
52
+ }, [enabledList]);
53
+
54
+ return (
55
+ <Select
56
+ className={styles.select}
57
+ onChange={(model, option) => {
58
+ onChange?.({ model, provider: (option as unknown as ModelOption).provider });
59
+ }}
60
+ options={options}
61
+ popupMatchSelectWidth={false}
62
+ value={value}
63
+ />
64
+ );
65
+ });
66
+
67
+ export default ModelSelect;
@@ -44,6 +44,10 @@ export const useStyles = createStyles(
44
44
  `,
45
45
  navbar: css`
46
46
  background: ${isDarkMode ? token.colorBgContainer : token.colorFillTertiary};
47
+
48
+ @media (max-width: 768px) {
49
+ background: ${token.colorBgContainer};
50
+ }
47
51
  `,
48
52
  navbarButton: css`
49
53
  line-height: 2;
@@ -636,7 +636,7 @@ export default {
636
636
  mobileButton__menu: '菜单',
637
637
  navbar: {
638
638
  account: '个人资料',
639
- description: '管理您的帐户信息。',
639
+ description: '管理您的帐户信息',
640
640
  security: '安全',
641
641
  title: '帐户',
642
642
  },
@@ -2,6 +2,14 @@ export default {
2
2
  about: {
3
3
  title: '关于',
4
4
  },
5
+ agentTab: {
6
+ chat: '聊天偏好',
7
+ meta: '助手信息',
8
+ modal: '模型设置',
9
+ plugin: '插件设置',
10
+ prompt: '角色设定',
11
+ tts: '语音服务',
12
+ },
5
13
  analytics: {
6
14
  telemetry: {
7
15
  desc: '通过选择发送遥测数据,你可以帮助我们改善 LobeChat 整体用户体验',
@@ -363,14 +371,23 @@ export default {
363
371
  title: 'WebRTC 同步',
364
372
  },
365
373
  },
374
+ systemAgent: {
375
+ title: '系统助手',
376
+ translation: {
377
+ label: '翻译模型',
378
+ modelDesc: '指定用于翻译的模型',
379
+ title: '翻译助手设置',
380
+ },
381
+ },
366
382
  tab: {
367
- about: '关于',
368
- agent: '默认助手',
369
- common: '通用设置',
370
- experiment: '实验',
371
- llm: '语言模型',
372
- sync: '云端同步',
373
- tts: '语音服务',
383
+ 'about': '关于',
384
+ 'agent': '默认助手',
385
+ 'common': '通用设置',
386
+ 'experiment': '实验',
387
+ 'llm': '语言模型',
388
+ 'sync': '云端同步',
389
+ 'system-agent': '系统助手',
390
+ 'tts': '语音服务',
374
391
  },
375
392
  tools: {
376
393
  builtins: {
@@ -2,23 +2,37 @@
2
2
 
3
3
  import { get } from 'lodash-es';
4
4
  import { cookies } from 'next/headers';
5
- import { readFileSync } from 'node:fs';
5
+ import { existsSync, readFileSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
 
8
8
  import { DEFAULT_LANG, LOBE_LOCALE_COOKIE } from '@/const/locale';
9
9
  import { NS, normalizeLocale } from '@/locales/resources';
10
+ import { isDev } from '@/utils/env';
10
11
 
11
- export const translation = async (ns: NS) => {
12
- const cookieStore = cookies();
13
- const defaultLang = cookieStore.get(LOBE_LOCALE_COOKIE);
14
- const lng = defaultLang?.value || DEFAULT_LANG;
15
- const filepath = join(process.cwd(), `locales/${normalizeLocale(lng)}/${ns}.json`);
16
- const file = readFileSync(filepath, 'utf8');
17
- const i18ns = JSON.parse(file);
12
+ export const translation = async (ns: NS = 'common') => {
13
+ let i18ns = {};
14
+ try {
15
+ const cookieStore = cookies();
16
+ const defaultLang = cookieStore.get(LOBE_LOCALE_COOKIE);
17
+ const lng = defaultLang?.value || DEFAULT_LANG;
18
+ let filepath = join(process.cwd(), `locales/${normalizeLocale(lng)}/${ns}.json`);
19
+ const isExist = existsSync(filepath);
20
+ if (!isExist)
21
+ filepath = join(
22
+ process.cwd(),
23
+ `locales/${normalizeLocale(isDev ? 'zh-CN' : DEFAULT_LANG)}/${ns}.json`,
24
+ );
25
+ const file = readFileSync(filepath, 'utf8');
26
+ i18ns = JSON.parse(file);
27
+ } catch (e) {
28
+ console.error('Error while reading translation file', e);
29
+ }
18
30
 
19
31
  return {
20
32
  t: (key: string, options: { [key: string]: string } = {}) => {
33
+ if (!i18ns) return key;
21
34
  let content = get(i18ns, key);
35
+ if (!content) return key;
22
36
  if (options) {
23
37
  Object.entries(options).forEach(([key, value]) => {
24
38
  content = content.replace(`{{${key}}}`, value);
@@ -9,7 +9,10 @@ import { chatService } from '@/services/chat';
9
9
  import { messageService } from '@/services/message';
10
10
  import { chatSelectors } from '@/store/chat/selectors';
11
11
  import { ChatStore } from '@/store/chat/store';
12
+ import { useUserStore } from '@/store/user';
13
+ import { systemAgentSelectors } from '@/store/user/selectors';
12
14
  import { ChatTTS, ChatTranslate } from '@/types/message';
15
+ import { merge } from '@/utils/merge';
13
16
  import { setNamespace } from '@/utils/storeDebug';
14
17
 
15
18
  const n = setNamespace('enhance');
@@ -48,12 +51,16 @@ export const chatEnhance: StateCreator<
48
51
  topicId: get().activeTopicId,
49
52
  ...data,
50
53
  }),
54
+
51
55
  translateMessage: async (id, targetLang) => {
52
56
  const { internal_toggleChatLoading, updateMessageTranslate, internal_dispatchMessage } = get();
53
57
 
54
58
  const message = chatSelectors.getMessageById(id)(get());
55
59
  if (!message) return;
56
60
 
61
+ // Get current agent for translation
62
+ const translationSetting = systemAgentSelectors.translation(useUserStore.getState());
63
+
57
64
  // create translate extra
58
65
  await updateMessageTranslate(id, { content: '', from: '', to: targetLang });
59
66
 
@@ -69,7 +76,7 @@ export const chatEnhance: StateCreator<
69
76
 
70
77
  await updateMessageTranslate(id, { content, from, to: targetLang });
71
78
  },
72
- params: chainLangDetect(message.content),
79
+ params: merge(translationSetting, chainLangDetect(message.content)),
73
80
  trace: get().getCurrentTracePayload({ traceName: TraceNameMap.LanguageDetect }),
74
81
  });
75
82
 
@@ -95,7 +102,7 @@ export const chatEnhance: StateCreator<
95
102
  }
96
103
  }
97
104
  },
98
- params: chainTranslate(message.content, targetLang),
105
+ params: merge(translationSetting, chainTranslate(message.content, targetLang)),
99
106
  trace: get().getCurrentTracePayload({ traceName: TraceNameMap.Translator }),
100
107
  });
101
108
  },
@@ -25,6 +25,7 @@ export enum SettingsTabs {
25
25
  Common = 'common',
26
26
  LLM = 'llm',
27
27
  Sync = 'sync',
28
+ SystemAgent = 'system-agent',
28
29
  TTS = 'tts',
29
30
  }
30
31
 
@@ -5,4 +5,5 @@ export {
5
5
  modelProviderSelectors,
6
6
  settingsSelectors,
7
7
  syncSettingsSelectors,
8
+ systemAgentSelectors,
8
9
  } from './slices/settings/selectors';
@@ -112,4 +112,26 @@ describe('SettingsAction', () => {
112
112
  expect(userService.updateUserSettings).toHaveBeenCalledWith({ defaultAgent: updatedAgent });
113
113
  });
114
114
  });
115
+
116
+ describe('setTranslationSystemAgent', () => {
117
+ it('should set partial settings', async () => {
118
+ const { result } = renderHook(() => useUserStore());
119
+ const systemAgentSettings: Partial<GlobalSettings> = {
120
+ systemAgent: {
121
+ translation: {
122
+ model: 'testmodel',
123
+ provider: 'provider',
124
+ },
125
+ },
126
+ };
127
+
128
+ // Perform the action
129
+ await act(async () => {
130
+ await result.current.setTranslationSystemAgent('provider', 'testmodel');
131
+ });
132
+
133
+ // Assert that updateUserSettings was called with the correct settings
134
+ expect(userService.updateUserSettings).toHaveBeenCalledWith(systemAgentSettings);
135
+ });
136
+ });
115
137
  });
@@ -16,6 +16,7 @@ export interface GeneralSettingsAction {
16
16
  importAppSettings: (settings: GlobalSettings) => Promise<void>;
17
17
  resetSettings: () => Promise<void>;
18
18
  setSettings: (settings: DeepPartial<GlobalSettings>) => Promise<void>;
19
+ setTranslationSystemAgent: (provider: string, model: string) => Promise<void>;
19
20
  switchLocale: (locale: LocaleMode) => Promise<void>;
20
21
  switchThemeMode: (themeMode: ThemeMode) => Promise<void>;
21
22
  updateDefaultAgent: (agent: DeepPartial<LobeAgentSettings>) => Promise<void>;
@@ -50,6 +51,16 @@ export const generalSettingsSlice: StateCreator<
50
51
  await userService.updateUserSettings(diffs);
51
52
  await get().refreshUserConfig();
52
53
  },
54
+ setTranslationSystemAgent: async (provider, model) => {
55
+ await get().setSettings({
56
+ systemAgent: {
57
+ translation: {
58
+ model: model,
59
+ provider: provider,
60
+ },
61
+ },
62
+ });
63
+ },
53
64
  switchLocale: async (locale) => {
54
65
  await get().setSettings({ language: locale });
55
66
 
@@ -2,3 +2,4 @@ export { modelConfigSelectors } from './modelConfig';
2
2
  export { modelProviderSelectors } from './modelProvider';
3
3
  export { settingsSelectors } from './settings';
4
4
  export { syncSettingsSelectors } from './sync';
5
+ export { systemAgentSelectors } from './systemAgent';
@@ -1,6 +1,11 @@
1
1
  import { DEFAULT_LANG } from '@/const/locale';
2
2
  import { DEFAULT_AGENT_META } from '@/const/meta';
3
- import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_TTS_CONFIG } from '@/const/settings';
3
+ import {
4
+ DEFAULT_AGENT,
5
+ DEFAULT_AGENT_CONFIG,
6
+ DEFAULT_SYSTEM_AGENT_CONFIG,
7
+ DEFAULT_TTS_CONFIG,
8
+ } from '@/const/settings';
4
9
  import { Locales } from '@/locales/resources';
5
10
  import { GeneralModelProviderConfig, GlobalLLMProviderKey, GlobalSettings } from '@/types/settings';
6
11
  import { isOnServerSide } from '@/utils/env';
@@ -53,9 +58,13 @@ export const currentThemeMode = (s: UserStore) => {
53
58
  const dalleConfig = (s: UserStore) => currentSettings(s).tool?.dalle || {};
54
59
  const isDalleAutoGenerating = (s: UserStore) => currentSettings(s).tool?.dalle?.autoGenerate;
55
60
 
61
+ const currentSystemAgent = (s: UserStore) =>
62
+ merge(DEFAULT_SYSTEM_AGENT_CONFIG, currentSettings(s).systemAgent);
63
+
56
64
  export const settingsSelectors = {
57
65
  currentLanguage,
58
66
  currentSettings,
67
+ currentSystemAgent,
59
68
  currentTTS,
60
69
  currentThemeMode,
61
70
  dalleConfig,
@@ -0,0 +1,14 @@
1
+ import { DEFAULT_SYSTEM_AGENT_CONFIG } from '@/const/settings';
2
+ import type { UserStore } from '@/store/user';
3
+ import { merge } from '@/utils/merge';
4
+
5
+ import { currentSettings } from './settings';
6
+
7
+ const currentSystemAgent = (s: UserStore) =>
8
+ merge(DEFAULT_SYSTEM_AGENT_CONFIG, currentSettings(s).systemAgent);
9
+
10
+ const translation = (s: UserStore) => currentSystemAgent(s).translation;
11
+
12
+ export const systemAgentSelectors = {
13
+ translation,
14
+ };
@@ -3,6 +3,7 @@ import type { LobeAgentSession } from '@/types/session';
3
3
  import { GlobalBaseSettings } from './base';
4
4
  import { GlobalLLMConfig } from './modelProvider';
5
5
  import { GlobalSyncSettings } from './sync';
6
+ import { GlobalSystemAgentConfig } from './systemAgent';
6
7
  import { GlobalTTSConfig } from './tts';
7
8
 
8
9
  export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
@@ -10,6 +11,7 @@ export type GlobalDefaultAgent = Pick<LobeAgentSession, 'config' | 'meta'>;
10
11
  export * from './base';
11
12
  export * from './modelProvider';
12
13
  export * from './sync';
14
+ export * from './systemAgent';
13
15
  export * from './tts';
14
16
 
15
17
  export interface GlobalTool {
@@ -25,6 +27,7 @@ export interface GlobalSettings extends GlobalBaseSettings {
25
27
  defaultAgent: GlobalDefaultAgent;
26
28
  languageModel: GlobalLLMConfig;
27
29
  sync: GlobalSyncSettings;
30
+ systemAgent: GlobalSystemAgentConfig;
28
31
  tool: GlobalTool;
29
32
  tts: GlobalTTSConfig;
30
33
  }
@@ -0,0 +1,8 @@
1
+ export interface GlobalTranslationConfig {
2
+ model: string;
3
+ provider: string;
4
+ }
5
+
6
+ export interface GlobalSystemAgentConfig {
7
+ translation: GlobalTranslationConfig;
8
+ }