@lobehub/chat 0.101.6 → 0.102.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 (89) hide show
  1. package/.i18nrc.js +1 -1
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +38 -4
  4. package/README.zh-CN.md +41 -9
  5. package/__mocks__/zustand/traditional.ts +4 -4
  6. package/locales/en_US/chat.json +11 -1
  7. package/locales/en_US/error.json +6 -0
  8. package/locales/en_US/setting.json +40 -1
  9. package/locales/ja_JP/chat.json +11 -1
  10. package/locales/ja_JP/error.json +6 -0
  11. package/locales/ja_JP/setting.json +40 -1
  12. package/locales/ko_KR/chat.json +11 -1
  13. package/locales/ko_KR/error.json +6 -0
  14. package/locales/ko_KR/setting.json +40 -1
  15. package/locales/ru_RU/chat.json +11 -1
  16. package/locales/ru_RU/error.json +6 -0
  17. package/locales/ru_RU/setting.json +40 -1
  18. package/locales/zh_CN/chat.json +10 -1
  19. package/locales/zh_CN/error.json +6 -0
  20. package/locales/zh_CN/setting.json +36 -1
  21. package/locales/zh_TW/chat.json +11 -1
  22. package/locales/zh_TW/error.json +6 -0
  23. package/locales/zh_TW/setting.json +40 -1
  24. package/next.config.mjs +2 -20
  25. package/package.json +4 -3
  26. package/src/app/api/openai/chat/route.ts +5 -41
  27. package/src/app/api/openai/{createAzureOpenai.ts → createBizOpenAI/createAzureOpenai.ts} +1 -1
  28. package/src/app/api/openai/{createOpenai.ts → createBizOpenAI/createOpenai.ts} +1 -1
  29. package/src/app/api/openai/createBizOpenAI/index.ts +46 -0
  30. package/src/app/api/openai/models/route.ts +1 -1
  31. package/src/app/api/openai/stt/route.ts +29 -0
  32. package/src/app/api/openai/tts/route.ts +17 -0
  33. package/src/app/api/tts/edge-speech/route.ts +9 -0
  34. package/src/app/api/tts/microsoft-speech/route.ts +9 -0
  35. package/src/app/chat/(desktop)/features/ChatHeader.tsx +3 -2
  36. package/src/app/chat/(desktop)/features/ChatInput/Footer.tsx +62 -0
  37. package/src/app/chat/(desktop)/features/ChatInput/index.tsx +0 -1
  38. package/src/app/chat/(desktop)/features/SessionHeader.tsx +2 -2
  39. package/src/app/chat/(desktop)/features/SideBar.tsx +7 -1
  40. package/src/app/chat/(mobile)/features/ChatHeader.tsx +29 -11
  41. package/src/app/chat/(mobile)/features/ChatInput/Mobile.tsx +20 -4
  42. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +23 -15
  43. package/src/app/chat/features/ChatInput/ActionBar/config.ts +16 -6
  44. package/src/app/chat/features/ChatInput/ActionBar/index.tsx +40 -16
  45. package/src/app/chat/features/ChatInput/STT/index.tsx +146 -0
  46. package/src/app/chat/features/ChatInput/Topic/index.tsx +1 -3
  47. package/src/app/chat/features/Conversation/ChatList/Actions/Assistant.tsx +2 -2
  48. package/src/app/chat/features/Conversation/ChatList/Actions/User.tsx +2 -2
  49. package/src/app/chat/features/Conversation/ChatList/Actions/customAction.ts +9 -2
  50. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +8 -1
  51. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +33 -27
  52. package/src/app/chat/features/Conversation/ChatList/Extras/AudioPlayer.tsx +139 -0
  53. package/src/app/chat/features/Conversation/ChatList/Extras/ExtraContainer.tsx +13 -0
  54. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +122 -0
  55. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +18 -11
  56. package/src/app/chat/settings/features/HeaderContent.tsx +6 -4
  57. package/src/app/chat/settings/features/SubmitAgentButton/index.tsx +2 -3
  58. package/src/app/settings/features/SideBar/List.tsx +2 -1
  59. package/src/app/settings/features/SideBar/index.tsx +1 -1
  60. package/src/app/settings/features/UpgradeAlert.tsx +1 -1
  61. package/src/app/settings/tts/TTS/index.tsx +73 -0
  62. package/src/app/settings/tts/TTS/options.ts +30 -0
  63. package/src/app/settings/tts/index.tsx +21 -0
  64. package/src/app/settings/tts/layout.tsx +1 -0
  65. package/src/app/settings/tts/page.tsx +3 -0
  66. package/src/components/HotKeys/index.tsx +4 -7
  67. package/src/const/layoutTokens.ts +3 -0
  68. package/src/const/settings.ts +22 -1
  69. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +119 -0
  70. package/src/features/AgentSetting/AgentTTS/index.tsx +115 -0
  71. package/src/features/AgentSetting/AgentTTS/options.ts +16 -0
  72. package/src/features/AgentSetting/index.tsx +2 -1
  73. package/src/hooks/useSTT.ts +61 -0
  74. package/src/hooks/useTTS.ts +87 -0
  75. package/src/locales/default/chat.ts +10 -1
  76. package/src/locales/default/error.ts +6 -0
  77. package/src/locales/default/setting.ts +35 -1
  78. package/src/services/_url.ts +7 -0
  79. package/src/store/global/initialState.ts +1 -0
  80. package/src/store/global/selectors/settings.test.ts +32 -0
  81. package/src/store/global/selectors/settings.ts +9 -1
  82. package/src/store/session/slices/agentConfig/selectors.ts +9 -0
  83. package/src/store/session/slices/chat/actions/translate.ts +24 -1
  84. package/src/store/session/slices/chat/selectors/utils.test.ts +16 -0
  85. package/src/store/session/slices/session/reducers/session.test.ts +24 -0
  86. package/src/types/chatMessage.ts +6 -0
  87. package/src/types/session.ts +17 -0
  88. package/src/types/settings.ts +13 -2
  89. package/src/app/chat/(mobile)/features/ChatInput/style.mobile.ts +0 -19
@@ -0,0 +1,119 @@
1
+ import { AudioPlayer } from '@lobehub/tts/react';
2
+ import { Alert, Highlighter } from '@lobehub/ui';
3
+ import { Button, RefSelectProps, Select, SelectProps } from 'antd';
4
+ import { useTheme } from 'antd-style';
5
+ import { forwardRef, useCallback, useState } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { useTTS } from '@/hooks/useTTS';
10
+ import { ChatMessageError } from '@/types/chatMessage';
11
+ import { TTSServer } from '@/types/session';
12
+ import { getMessageError } from '@/utils/fetch';
13
+
14
+ interface SelectWithTTSPreviewProps extends SelectProps {
15
+ server: TTSServer;
16
+ }
17
+
18
+ const SelectWithTTSPreview = forwardRef<RefSelectProps, SelectWithTTSPreviewProps>(
19
+ ({ value, options, server, onSelect, ...rest }, ref) => {
20
+ const [error, setError] = useState<ChatMessageError>();
21
+ const [voice, setVoice] = useState<string>(value);
22
+ const { t } = useTranslation('welcome');
23
+ const theme = useTheme();
24
+ const PREVIEW_TEXT = ['Lobe Chat', t('slogan.title'), t('slogan.desc1')].join('. ');
25
+
26
+ const setDefaultError = useCallback(
27
+ (err?: any) => {
28
+ setError({ body: err, message: t('tts.responseError', { ns: 'error' }), type: 500 });
29
+ },
30
+ [t],
31
+ );
32
+
33
+ const { isGlobalLoading, audio, stop, start, response, setText } = useTTS(PREVIEW_TEXT, {
34
+ onError: (err) => {
35
+ stop();
36
+ setDefaultError(err);
37
+ },
38
+ onErrorRetry: (err) => {
39
+ stop();
40
+ setDefaultError(err);
41
+ },
42
+ onSuccess: async () => {
43
+ if (!response) return;
44
+ if (response.status === 200) return;
45
+ const message = await getMessageError(response);
46
+ if (message) {
47
+ setError(message);
48
+ } else {
49
+ setDefaultError();
50
+ }
51
+ stop();
52
+ },
53
+ server,
54
+ voice,
55
+ });
56
+
57
+ const handleCloseError = useCallback(() => {
58
+ setError(undefined);
59
+ stop();
60
+ }, [stop]);
61
+
62
+ const handleRetry = useCallback(() => {
63
+ setError(undefined);
64
+ stop();
65
+ start();
66
+ }, [stop, start]);
67
+
68
+ const handleSelect: SelectProps['onSelect'] = (value, option) => {
69
+ stop();
70
+ setVoice(value as string);
71
+ setText([PREVIEW_TEXT, option?.label].join(' - '));
72
+ onSelect?.(value, option);
73
+ };
74
+ return (
75
+ <Flexbox gap={8}>
76
+ <Flexbox align={'center'} gap={8} horizontal style={{ width: '100%' }}>
77
+ <Select onSelect={handleSelect} options={options} ref={ref} value={value} {...rest} />
78
+ <AudioPlayer
79
+ allowPause={false}
80
+ audio={audio}
81
+ buttonActive
82
+ buttonSize={{ blockSize: 36, fontSize: 16 }}
83
+ buttonStyle={{ border: `1px solid ${theme.colorBorder}` }}
84
+ isLoading={isGlobalLoading}
85
+ onInitPlay={start}
86
+ onLoadingStop={stop}
87
+ showSlider={false}
88
+ showTime={false}
89
+ style={{ flex: 'none', padding: 0, width: 'unset' }}
90
+ title={t('settingTTS.voice.preview', { ns: 'setting' })}
91
+ />
92
+ </Flexbox>
93
+ {error && (
94
+ <Alert
95
+ action={
96
+ <Button onClick={handleRetry} size={'small'} type={'primary'}>
97
+ {t('retry', { ns: 'common' })}
98
+ </Button>
99
+ }
100
+ closable
101
+ extra={
102
+ error.body && (
103
+ <Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
104
+ {JSON.stringify(error.body, null, 2)}
105
+ </Highlighter>
106
+ )
107
+ }
108
+ message={error.message}
109
+ onClose={handleCloseError}
110
+ style={{ alignItems: 'center', width: '100%' }}
111
+ type="error"
112
+ />
113
+ )}
114
+ </Flexbox>
115
+ );
116
+ },
117
+ );
118
+
119
+ export default SelectWithTTSPreview;
@@ -0,0 +1,115 @@
1
+ import { VoiceList } from '@lobehub/tts';
2
+ import { Form, ItemGroup } from '@lobehub/ui';
3
+ import { Form as AFrom, Select, Switch } from 'antd';
4
+ import isEqual from 'fast-deep-equal';
5
+ import { debounce } from 'lodash-es';
6
+ import { Mic } from 'lucide-react';
7
+ import { memo, useEffect } from 'react';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import { FORM_STYLE } from '@/const/layoutTokens';
11
+ import SelectWithTTSPreview from '@/features/AgentSetting/AgentTTS/SelectWithTTSPreview';
12
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
13
+
14
+ import { useStore } from '../store';
15
+ import { ttsOptions } from './options';
16
+
17
+ const TTS_SETTING_KEY = 'tts';
18
+ const { openaiVoiceOptions, localeOptions } = VoiceList;
19
+
20
+ const AgentTTS = memo(() => {
21
+ const { t } = useTranslation('setting');
22
+ const updateConfig = useStore((s) => s.setAgentConfig);
23
+ const [form] = AFrom.useForm();
24
+ const voiceList = useGlobalStore((s) => {
25
+ const locale = settingsSelectors.currentLanguage(s);
26
+ return (all?: boolean) => new VoiceList(all ? undefined : locale);
27
+ });
28
+ const config = useStore((s) => s.config, isEqual);
29
+
30
+ useEffect(() => {
31
+ form.setFieldsValue(config);
32
+ }, [config]);
33
+
34
+ const showAllLocaleVoice = config.tts.showAllLocaleVoice;
35
+
36
+ const { edgeVoiceOptions, microsoftVoiceOptions } = voiceList(showAllLocaleVoice);
37
+
38
+ const tts: ItemGroup = {
39
+ children: [
40
+ {
41
+ children: <Select options={ttsOptions} />,
42
+ desc: t('settingTTS.ttsService.desc'),
43
+ label: t('settingTTS.ttsService.title'),
44
+ name: [TTS_SETTING_KEY, 'ttsService'],
45
+ },
46
+ {
47
+ children: <Switch />,
48
+ desc: t('settingTTS.showAllLocaleVoice.desc'),
49
+ hidden: config.tts.ttsService === 'openai',
50
+ label: t('settingTTS.showAllLocaleVoice.title'),
51
+ minWidth: undefined,
52
+ name: [TTS_SETTING_KEY, 'showAllLocaleVoice'],
53
+ valuePropName: 'checked',
54
+ },
55
+ {
56
+ children: <SelectWithTTSPreview options={openaiVoiceOptions} server={'openai'} />,
57
+ desc: t('settingTTS.voice.desc'),
58
+ hidden: config.tts.ttsService !== 'openai',
59
+ label: t('settingTTS.voice.title'),
60
+ name: [TTS_SETTING_KEY, 'voice', 'openai'],
61
+ },
62
+ {
63
+ children: <SelectWithTTSPreview options={edgeVoiceOptions} server={'edge'} />,
64
+ desc: t('settingTTS.voice.desc'),
65
+ divider: false,
66
+ hidden: config.tts.ttsService !== 'edge',
67
+ label: t('settingTTS.voice.title'),
68
+ name: [TTS_SETTING_KEY, 'voice', 'edge'],
69
+ },
70
+ {
71
+ children: <SelectWithTTSPreview options={microsoftVoiceOptions} server={'microsoft'} />,
72
+ desc: t('settingTTS.voice.desc'),
73
+ divider: false,
74
+ hidden: config.tts.ttsService !== 'microsoft',
75
+ label: t('settingTTS.voice.title'),
76
+ name: [TTS_SETTING_KEY, 'voice', 'microsoft'],
77
+ },
78
+ {
79
+ children: (
80
+ <Select
81
+ options={[
82
+ { label: t('settingTheme.lang.autoMode'), value: 'auto' },
83
+ ...(localeOptions || []),
84
+ ]}
85
+ />
86
+ ),
87
+ desc: t('settingTTS.sttLocale.desc'),
88
+ label: t('settingTTS.sttLocale.title'),
89
+ name: [TTS_SETTING_KEY, 'sttLocale'],
90
+ },
91
+ ],
92
+ icon: Mic,
93
+ title: t('settingTTS.title'),
94
+ };
95
+
96
+ return (
97
+ <Form
98
+ form={form}
99
+ initialValues={{
100
+ [TTS_SETTING_KEY]: {
101
+ voice: {
102
+ edge: edgeVoiceOptions?.[0].value,
103
+ microsoft: microsoftVoiceOptions?.[0].value,
104
+ openai: openaiVoiceOptions?.[0].value,
105
+ },
106
+ },
107
+ }}
108
+ items={[tts]}
109
+ onValuesChange={debounce(updateConfig, 100)}
110
+ {...FORM_STYLE}
111
+ />
112
+ );
113
+ });
114
+
115
+ export default AgentTTS;
@@ -0,0 +1,16 @@
1
+ import { SelectProps } from 'antd';
2
+
3
+ export const ttsOptions: SelectProps['options'] = [
4
+ {
5
+ label: 'OpenAI',
6
+ value: 'openai',
7
+ },
8
+ {
9
+ label: 'Edge Speech',
10
+ value: 'edge',
11
+ },
12
+ {
13
+ label: 'Microsoft Speech',
14
+ value: 'microsoft',
15
+ },
16
+ ];
@@ -4,6 +4,7 @@ import AgentConfig from './AgentConfig';
4
4
  import AgentMeta from './AgentMeta';
5
5
  import AgentPlugin from './AgentPlugin';
6
6
  import AgentPrompt from './AgentPrompt';
7
+ import AgentTTS from './AgentTTS';
7
8
  import StoreUpdater, { StoreUpdaterProps } from './StoreUpdater';
8
9
  import { Provider, createStore } from './store';
9
10
 
@@ -13,10 +14,10 @@ const AgentSettings = memo<AgentSettingsProps>((props) => {
13
14
  return (
14
15
  <Provider createStore={createStore}>
15
16
  <StoreUpdater {...props} />
16
-
17
17
  <AgentPrompt />
18
18
  <AgentMeta />
19
19
  <AgentConfig />
20
+ <AgentTTS />
20
21
  <AgentPlugin />
21
22
  </Provider>
22
23
  );
@@ -0,0 +1,61 @@
1
+ import { getRecordMineType } from '@lobehub/tts';
2
+ import {
3
+ OpenAISTTOptions,
4
+ SpeechRecognitionOptions,
5
+ useOpenAISTT,
6
+ useSpeechRecognition,
7
+ } from '@lobehub/tts/react';
8
+ import isEqual from 'fast-deep-equal';
9
+ import { SWRConfiguration } from 'swr';
10
+
11
+ import { createHeaderWithOpenAI } from '@/services/_header';
12
+ import { OPENAI_URLS } from '@/services/_url';
13
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
14
+ import { useSessionStore } from '@/store/session';
15
+ import { agentSelectors } from '@/store/session/slices/agentConfig';
16
+
17
+ interface STTConfig extends SWRConfiguration {
18
+ onTextChange: (value: string) => void;
19
+ }
20
+
21
+ export const useSTT = (config: STTConfig) => {
22
+ const ttsSettings = useGlobalStore(settingsSelectors.currentTTS, isEqual);
23
+ const ttsAgentSettings = useSessionStore(agentSelectors.currentAgentTTS, isEqual);
24
+ const locale = useGlobalStore(settingsSelectors.currentLanguage);
25
+
26
+ const autoStop = ttsSettings.sttAutoStop;
27
+ const sttLocale =
28
+ ttsAgentSettings?.sttLocale && ttsAgentSettings.sttLocale !== 'auto'
29
+ ? ttsAgentSettings.sttLocale
30
+ : locale;
31
+
32
+ let useSelectedSTT;
33
+ let options: any = {};
34
+
35
+ switch (ttsSettings.sttServer) {
36
+ case 'openai': {
37
+ useSelectedSTT = useOpenAISTT;
38
+ options = {
39
+ api: {
40
+ headers: createHeaderWithOpenAI(),
41
+ serviceUrl: OPENAI_URLS.stt,
42
+ },
43
+ autoStop,
44
+ options: {
45
+ mineType: getRecordMineType(),
46
+ model: ttsSettings.openAI.sttModel,
47
+ },
48
+ } as OpenAISTTOptions;
49
+ break;
50
+ }
51
+ case 'browser': {
52
+ options = {
53
+ autoStop,
54
+ } as SpeechRecognitionOptions;
55
+ useSelectedSTT = useSpeechRecognition;
56
+ break;
57
+ }
58
+ }
59
+
60
+ return useSelectedSTT(sttLocale, { ...config, ...options });
61
+ };
@@ -0,0 +1,87 @@
1
+ import { VoiceList } from '@lobehub/tts';
2
+ import {
3
+ EdgeSpeechOptions,
4
+ MicrosoftSpeechOptions,
5
+ OpenAITTSOptions,
6
+ useEdgeSpeech,
7
+ useMicrosoftSpeech,
8
+ useOpenAITTS,
9
+ } from '@lobehub/tts/react';
10
+ import isEqual from 'fast-deep-equal';
11
+ import { SWRConfiguration } from 'swr';
12
+
13
+ import { createHeaderWithOpenAI } from '@/services/_header';
14
+ import { OPENAI_URLS, TTS_URL } from '@/services/_url';
15
+ import { settingsSelectors, useGlobalStore } from '@/store/global';
16
+ import { useSessionStore } from '@/store/session';
17
+ import { agentSelectors } from '@/store/session/slices/agentConfig';
18
+ import { TTSServer } from '@/types/session';
19
+
20
+ interface TTSConfig extends SWRConfiguration {
21
+ server?: TTSServer;
22
+ voice?: string;
23
+ }
24
+
25
+ export const useTTS = (content: string, config?: TTSConfig) => {
26
+ const ttsSettings = useGlobalStore(settingsSelectors.currentTTS, isEqual);
27
+ const ttsAgentSettings = useSessionStore(agentSelectors.currentAgentTTS, isEqual);
28
+ const voiceList = useGlobalStore((s) => new VoiceList(settingsSelectors.currentLanguage(s)));
29
+
30
+ let useSelectedTTS;
31
+ let options: any = {};
32
+ switch (config?.server || ttsAgentSettings.ttsService) {
33
+ case 'openai': {
34
+ useSelectedTTS = useOpenAITTS;
35
+ options = {
36
+ api: {
37
+ headers: createHeaderWithOpenAI(),
38
+ serviceUrl: OPENAI_URLS.tts,
39
+ },
40
+ options: {
41
+ model: ttsSettings.openAI.ttsModel,
42
+ voice:
43
+ config?.voice ||
44
+ ttsAgentSettings.voice.openai ||
45
+ VoiceList.openaiVoiceOptions?.[0].value,
46
+ },
47
+ } as OpenAITTSOptions;
48
+ break;
49
+ }
50
+ case 'edge': {
51
+ useSelectedTTS = useEdgeSpeech;
52
+ options = {
53
+ api: {
54
+ /**
55
+ * @description client fetch
56
+ * serviceUrl: TTS_URL.edge,
57
+ */
58
+ },
59
+ options: {
60
+ voice:
61
+ config?.voice || ttsAgentSettings.voice.edge || voiceList.edgeVoiceOptions?.[0].value,
62
+ },
63
+ } as EdgeSpeechOptions;
64
+ break;
65
+ }
66
+ case 'microsoft': {
67
+ useSelectedTTS = useMicrosoftSpeech;
68
+ options = {
69
+ api: {
70
+ serviceUrl: TTS_URL.microsoft,
71
+ },
72
+ options: {
73
+ voice:
74
+ config?.voice ||
75
+ ttsAgentSettings.voice.microsoft ||
76
+ voiceList.microsoftVoiceOptions?.[0].value,
77
+ },
78
+ } as MicrosoftSpeechOptions;
79
+ break;
80
+ }
81
+ }
82
+
83
+ return useSelectedTTS(content, {
84
+ ...config,
85
+ ...options,
86
+ });
87
+ };
@@ -38,6 +38,11 @@ export default {
38
38
  withSystemRole: '包含助手角色设定',
39
39
  },
40
40
  stop: '停止',
41
+ stt: {
42
+ action: '语音输入',
43
+ loading: '识别中...',
44
+ prettifying: '润色中...',
45
+ },
41
46
  temp: '临时',
42
47
  tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
43
48
  tokenTag: {
@@ -58,9 +63,13 @@ export default {
58
63
  title: '话题列表',
59
64
  },
60
65
  translate: {
66
+ action: '翻译',
61
67
  clear: '删除翻译',
62
68
  },
63
- translateTo: '翻译',
69
+ tts: {
70
+ action: '语音朗读',
71
+ clear: '删除语音',
72
+ },
64
73
  updateAgent: '更新助理信息',
65
74
  upload: {
66
75
  actionTooltip: '上传图片',
@@ -35,6 +35,12 @@ export default {
35
35
  NoAPIKey: 'OpenAI API Key 为空,请添加自定义 OpenAI API Key',
36
36
  /* eslint-enable */
37
37
  },
38
+ stt: {
39
+ responseError: '服务请求失败,请检查配置或重试',
40
+ },
41
+ tts: {
42
+ responseError: '服务请求失败,请检查配置或重试',
43
+ },
38
44
  unlock: {
39
45
  apikey: {
40
46
  addProxyUrl: '添加 OpenAI 代理地址(可选)',
@@ -109,7 +109,6 @@ export default {
109
109
  },
110
110
  title: '助手信息',
111
111
  },
112
-
113
112
  settingChat: {
114
113
  chatStyleType: {
115
114
  title: '聊天窗口样式',
@@ -195,6 +194,40 @@ export default {
195
194
  },
196
195
  title: '系统设置',
197
196
  },
197
+ settingTTS: {
198
+ openai: {
199
+ sttModel: 'OpenAI 语音识别模型',
200
+ ttsModel: 'OpenAI 语音合成模型',
201
+ },
202
+ showAllLocaleVoice: {
203
+ desc: '关闭则只显示当前语种的声源',
204
+ title: '显示所有语种声源',
205
+ },
206
+ stt: '语音识别设置',
207
+ sttAutoStop: {
208
+ desc: '关闭后,语音识别将不会自动结束,需要手动点击结束按钮',
209
+ title: '自动结束语音识别',
210
+ },
211
+ sttLocale: {
212
+ desc: '语音输入的语种,此选项可提高语音识别准确率',
213
+ title: '语音识别语种',
214
+ },
215
+ sttService: {
216
+ desc: '其中 broswer 为浏览器原生的语音识别服务',
217
+ title: '语音识别服务',
218
+ },
219
+ title: '语音服务',
220
+ tts: '语音合成设置',
221
+ ttsService: {
222
+ desc: '如使用 OpenAI 语音合成服务,需要保证 OpenAI 模型服务已开启',
223
+ title: '语音合成服务',
224
+ },
225
+ voice: {
226
+ desc: '为当前助手挑选一个声音,不同 TTS 服务支持的声源不同',
227
+ preview: '试听声源',
228
+ title: '语音合成声源',
229
+ },
230
+ },
198
231
  settingTheme: {
199
232
  avatar: {
200
233
  title: '头像',
@@ -234,5 +267,6 @@ export default {
234
267
  agent: '默认助手',
235
268
  common: '通用设置',
236
269
  llm: '语言模型',
270
+ tts: '语音服务',
237
271
  },
238
272
  };
@@ -6,4 +6,11 @@ export const URLS = {
6
6
  export const OPENAI_URLS = {
7
7
  chat: '/api/openai/chat',
8
8
  models: '/api/openai/models',
9
+ stt: '/api/openai/stt',
10
+ tts: '/api/openai/tts',
11
+ };
12
+
13
+ export const TTS_URL = {
14
+ edge: '/api/tts/edge-speech',
15
+ microsoft: '/api/tts/microsoft-speech',
9
16
  };
@@ -11,6 +11,7 @@ export enum SettingsTabs {
11
11
  Agent = 'agent',
12
12
  Common = 'common',
13
13
  LLM = 'llm',
14
+ TTS = 'tts',
14
15
  }
15
16
 
16
17
  export interface Guide {
@@ -20,12 +20,28 @@ describe('settingsSelectors', () => {
20
20
  systemRole: '',
21
21
  model: LanguageModel.GPT3_5,
22
22
  params: {},
23
+ tts: {
24
+ showAllLocaleVoice: false,
25
+ sttLocale: 'auto',
26
+ ttsService: 'openai',
27
+ voice: {
28
+ openai: 'alloy',
29
+ },
30
+ },
23
31
  },
24
32
  meta: {
25
33
  avatar: 'Default Agent',
26
34
  description: 'Default agent for testing',
27
35
  },
28
36
  },
37
+ tts: {
38
+ openAI: {
39
+ sttModel: 'whisper-1',
40
+ ttsModel: 'tts-1',
41
+ },
42
+ sttAutoStop: true,
43
+ sttServer: 'openai',
44
+ },
29
45
  languageModel: {
30
46
  openAI: {
31
47
  OPENAI_API_KEY: 'openai-api-key',
@@ -59,12 +75,28 @@ describe('settingsSelectors', () => {
59
75
  top_p: 1,
60
76
  },
61
77
  plugins: [],
78
+ tts: {
79
+ showAllLocaleVoice: false,
80
+ sttLocale: 'auto',
81
+ ttsService: 'openai',
82
+ voice: {
83
+ openai: 'alloy',
84
+ },
85
+ },
62
86
  },
63
87
  meta: {
64
88
  avatar: 'Default Agent',
65
89
  description: 'Default agent for testing',
66
90
  },
67
91
  },
92
+ tts: {
93
+ openAI: {
94
+ sttModel: 'whisper-1',
95
+ ttsModel: 'tts-1',
96
+ },
97
+ sttAutoStop: true,
98
+ sttServer: 'openai',
99
+ },
68
100
  languageModel: {
69
101
  openAI: {
70
102
  OPENAI_API_KEY: 'openai-api-key',
@@ -1,7 +1,12 @@
1
1
  import { DEFAULT_OPENAI_MODEL_LIST } from '@/const/llm';
2
2
  import { DEFAULT_LANG } from '@/const/locale';
3
3
  import { DEFAULT_AGENT_META } from '@/const/meta';
4
- import { DEFAULT_AGENT, DEFAULT_AGENT_CONFIG, DEFAULT_SETTINGS } from '@/const/settings';
4
+ import {
5
+ DEFAULT_AGENT,
6
+ DEFAULT_AGENT_CONFIG,
7
+ DEFAULT_SETTINGS,
8
+ DEFAULT_TTS_CONFIG,
9
+ } from '@/const/settings';
5
10
  import { Locales } from '@/locales/resources';
6
11
  import { GlobalSettings } from '@/types/settings';
7
12
  import { isOnServerSide } from '@/utils/env';
@@ -11,6 +16,8 @@ import { GlobalStore } from '../store';
11
16
 
12
17
  const currentSettings = (s: GlobalStore) => merge(DEFAULT_SETTINGS, s.settings);
13
18
 
19
+ const currentTTS = (s: GlobalStore) => merge(DEFAULT_TTS_CONFIG, s.settings.tts);
20
+
14
21
  const defaultAgent = (s: GlobalStore) => merge(DEFAULT_AGENT, s.settings.defaultAgent);
15
22
 
16
23
  const defaultAgentConfig = (s: GlobalStore) => merge(DEFAULT_AGENT_CONFIG, defaultAgent(s).config);
@@ -49,6 +56,7 @@ const currentLanguage = (s: GlobalStore) => {
49
56
  export const settingsSelectors = {
50
57
  currentLanguage,
51
58
  currentSettings,
59
+ currentTTS,
52
60
  defaultAgent,
53
61
  defaultAgentConfig,
54
62
  defaultAgentMeta,
@@ -2,9 +2,11 @@ import { t } from 'i18next';
2
2
 
3
3
  import { DEFAULT_OPENAI_MODEL_LIST, VISION_MODEL_WHITE_LIST } from '@/const/llm';
4
4
  import { DEFAULT_AVATAR, DEFAULT_BACKGROUND_COLOR } from '@/const/meta';
5
+ import { DEFAUTT_AGENT_TTS_CONFIG } from '@/const/settings';
5
6
  import { SessionStore } from '@/store/session';
6
7
  import { LanguageModel } from '@/types/llm';
7
8
  import { MetaData } from '@/types/meta';
9
+ import { LobeAgentTTSConfig } from '@/types/session';
8
10
  import { merge } from '@/utils/merge';
9
11
 
10
12
  import { sessionSelectors } from '../session/selectors';
@@ -78,6 +80,12 @@ const showTokenTag = (s: SessionStore) => {
78
80
  return DEFAULT_OPENAI_MODEL_LIST.includes(model);
79
81
  };
80
82
 
83
+ const currentAgentTTS = (s: SessionStore): LobeAgentTTSConfig => {
84
+ const config = currentAgentConfig(s);
85
+
86
+ return config?.tts || DEFAUTT_AGENT_TTS_CONFIG;
87
+ };
88
+
81
89
  export const agentSelectors = {
82
90
  currentAgentAvatar,
83
91
  currentAgentBackgroundColor,
@@ -87,6 +95,7 @@ export const agentSelectors = {
87
95
  currentAgentModel,
88
96
  currentAgentPlugins,
89
97
  currentAgentSystemRole,
98
+ currentAgentTTS,
90
99
  currentAgentTitle,
91
100
  getAvatar,
92
101
  getDescription,