@lobehub/chat 0.101.7 → 0.102.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.
- package/.i18nrc.js +1 -1
- package/CHANGELOG.md +50 -0
- package/README.md +38 -4
- package/README.zh-CN.md +41 -9
- package/__mocks__/zustand/traditional.ts +4 -4
- package/locales/en_US/chat.json +11 -1
- package/locales/en_US/error.json +6 -0
- package/locales/en_US/setting.json +40 -1
- package/locales/ja_JP/chat.json +11 -1
- package/locales/ja_JP/error.json +6 -0
- package/locales/ja_JP/setting.json +40 -1
- package/locales/ko_KR/chat.json +11 -1
- package/locales/ko_KR/error.json +6 -0
- package/locales/ko_KR/setting.json +40 -1
- package/locales/ru_RU/chat.json +11 -1
- package/locales/ru_RU/error.json +6 -0
- package/locales/ru_RU/setting.json +40 -1
- package/locales/zh_CN/chat.json +10 -1
- package/locales/zh_CN/error.json +6 -0
- package/locales/zh_CN/setting.json +36 -1
- package/locales/zh_TW/chat.json +11 -1
- package/locales/zh_TW/error.json +6 -0
- package/locales/zh_TW/setting.json +40 -1
- package/next.config.mjs +2 -20
- package/package.json +4 -3
- package/src/app/api/openai/chat/route.ts +5 -41
- package/src/app/api/openai/{createAzureOpenai.ts → createBizOpenAI/createAzureOpenai.ts} +1 -1
- package/src/app/api/openai/{createOpenai.ts → createBizOpenAI/createOpenai.ts} +1 -1
- package/src/app/api/openai/createBizOpenAI/index.ts +46 -0
- package/src/app/api/openai/models/route.ts +1 -1
- package/src/app/api/openai/stt/route.ts +29 -0
- package/src/app/api/openai/tts/route.ts +17 -0
- package/src/app/api/tts/edge-speech/route.ts +9 -0
- package/src/app/api/tts/microsoft-speech/route.ts +9 -0
- package/src/app/chat/(desktop)/features/ChatHeader.tsx +3 -2
- package/src/app/chat/(desktop)/features/ChatInput/index.tsx +0 -1
- package/src/app/chat/(desktop)/features/SessionHeader.tsx +2 -2
- package/src/app/chat/(mobile)/features/ChatHeader.tsx +29 -11
- package/src/app/chat/(mobile)/features/ChatInput/Mobile.tsx +20 -4
- package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +23 -15
- package/src/app/chat/features/ChatInput/ActionBar/config.ts +16 -6
- package/src/app/chat/features/ChatInput/ActionBar/index.tsx +40 -16
- package/src/app/chat/features/ChatInput/STT/index.tsx +146 -0
- package/src/app/chat/features/ChatInput/Topic/index.tsx +1 -3
- package/src/app/chat/features/Conversation/ChatList/Actions/Assistant.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Actions/User.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Actions/customAction.ts +9 -2
- package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +8 -1
- package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +33 -27
- package/src/app/chat/features/Conversation/ChatList/Extras/AudioPlayer.tsx +139 -0
- package/src/app/chat/features/Conversation/ChatList/Extras/ExtraContainer.tsx +13 -0
- package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +122 -0
- package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +18 -11
- package/src/app/chat/settings/features/HeaderContent.tsx +6 -4
- package/src/app/chat/settings/features/SubmitAgentButton/index.tsx +2 -3
- package/src/app/settings/features/SideBar/List.tsx +2 -1
- package/src/app/settings/features/SideBar/index.tsx +1 -1
- package/src/app/settings/features/UpgradeAlert.tsx +1 -1
- package/src/app/settings/tts/TTS/index.tsx +73 -0
- package/src/app/settings/tts/TTS/options.ts +30 -0
- package/src/app/settings/tts/index.tsx +21 -0
- package/src/app/settings/tts/layout.tsx +1 -0
- package/src/app/settings/tts/page.tsx +3 -0
- package/src/components/HotKeys/index.tsx +4 -7
- package/src/const/layoutTokens.ts +3 -0
- package/src/const/settings.ts +22 -1
- package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +119 -0
- package/src/features/AgentSetting/AgentTTS/index.tsx +115 -0
- package/src/features/AgentSetting/AgentTTS/options.ts +16 -0
- package/src/features/AgentSetting/index.tsx +2 -1
- package/src/hooks/useSTT.ts +61 -0
- package/src/hooks/useTTS.ts +87 -0
- package/src/locales/default/chat.ts +10 -1
- package/src/locales/default/error.ts +6 -0
- package/src/locales/default/setting.ts +35 -1
- package/src/services/_url.ts +7 -0
- package/src/store/global/initialState.ts +1 -0
- package/src/store/global/selectors/settings.test.ts +32 -0
- package/src/store/global/selectors/settings.ts +9 -1
- package/src/store/session/slices/agentConfig/selectors.ts +9 -0
- package/src/store/session/slices/chat/actions/translate.ts +24 -1
- package/src/store/session/slices/chat/selectors/utils.test.ts +16 -0
- package/src/store/session/slices/session/reducers/session.test.ts +24 -0
- package/src/types/chatMessage.ts +6 -0
- package/src/types/session.ts +17 -0
- package/src/types/settings.ts +13 -2
- package/src/app/chat/(mobile)/features/ChatInput/style.mobile.ts +0 -19
|
@@ -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
|
-
|
|
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
|
};
|
package/src/services/_url.ts
CHANGED
|
@@ -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
|
};
|
|
@@ -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 {
|
|
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,
|
|
@@ -16,13 +16,15 @@ const t = setNamespace('chat/translate');
|
|
|
16
16
|
* 翻译事件
|
|
17
17
|
*/
|
|
18
18
|
export interface ChatTranslateAction {
|
|
19
|
-
|
|
19
|
+
clearTTS: (id: string) => void;
|
|
20
20
|
|
|
21
|
+
clearTranslate: (id: string) => void;
|
|
21
22
|
/**
|
|
22
23
|
* 翻译消息
|
|
23
24
|
* @param id
|
|
24
25
|
*/
|
|
25
26
|
translateMessage: (id: string, targetLang: string) => Promise<void>;
|
|
27
|
+
ttsMessage: (id: string, init?: boolean) => void;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export const chatTranslate: StateCreator<
|
|
@@ -31,6 +33,15 @@ export const chatTranslate: StateCreator<
|
|
|
31
33
|
[],
|
|
32
34
|
ChatTranslateAction
|
|
33
35
|
> = (set, get) => ({
|
|
36
|
+
clearTTS: (id) => {
|
|
37
|
+
get().dispatchMessage({
|
|
38
|
+
id,
|
|
39
|
+
key: 'tts',
|
|
40
|
+
type: 'updateMessageExtra',
|
|
41
|
+
value: null,
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
|
|
34
45
|
clearTranslate: (id) => {
|
|
35
46
|
get().dispatchMessage({
|
|
36
47
|
id,
|
|
@@ -85,4 +96,16 @@ export const chatTranslate: StateCreator<
|
|
|
85
96
|
|
|
86
97
|
toggleChatLoading(false);
|
|
87
98
|
},
|
|
99
|
+
|
|
100
|
+
ttsMessage: (id, init) => {
|
|
101
|
+
const { dispatchMessage } = get();
|
|
102
|
+
dispatchMessage({
|
|
103
|
+
id,
|
|
104
|
+
key: 'tts',
|
|
105
|
+
type: 'updateMessageExtra',
|
|
106
|
+
value: {
|
|
107
|
+
init: Boolean(init),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
},
|
|
88
111
|
});
|
|
@@ -41,6 +41,14 @@ beforeEach(() => {
|
|
|
41
41
|
temperature: 0.6,
|
|
42
42
|
},
|
|
43
43
|
systemRole: '',
|
|
44
|
+
tts: {
|
|
45
|
+
ttsService: 'openai',
|
|
46
|
+
sttLocale: 'auto',
|
|
47
|
+
showAllLocaleVoice: false,
|
|
48
|
+
voice: {
|
|
49
|
+
openai: 'alloy',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
44
52
|
},
|
|
45
53
|
type: 'agent',
|
|
46
54
|
createAt: 1690110700808,
|
|
@@ -215,6 +223,14 @@ describe('organizeChats', () => {
|
|
|
215
223
|
params: {
|
|
216
224
|
temperature: 0.6,
|
|
217
225
|
},
|
|
226
|
+
tts: {
|
|
227
|
+
ttsService: 'openai',
|
|
228
|
+
sttLocale: 'auto',
|
|
229
|
+
showAllLocaleVoice: false,
|
|
230
|
+
voice: {
|
|
231
|
+
openai: 'alloy',
|
|
232
|
+
},
|
|
233
|
+
},
|
|
218
234
|
systemRole: '',
|
|
219
235
|
},
|
|
220
236
|
createAt: 1690110700808,
|
|
@@ -312,6 +312,14 @@ describe('sessionsReducer', () => {
|
|
|
312
312
|
model: 'gpt-3.5-turbo',
|
|
313
313
|
params: {},
|
|
314
314
|
systemRole: 'system-role',
|
|
315
|
+
tts: {
|
|
316
|
+
showAllLocaleVoice: false,
|
|
317
|
+
sttLocale: 'auto',
|
|
318
|
+
ttsService: 'openai',
|
|
319
|
+
voice: {
|
|
320
|
+
openai: 'alloy',
|
|
321
|
+
},
|
|
322
|
+
},
|
|
315
323
|
},
|
|
316
324
|
type: 'agent',
|
|
317
325
|
meta: {
|
|
@@ -354,6 +362,14 @@ describe('sessionsReducer', () => {
|
|
|
354
362
|
model: 'gpt-3.5-turbo',
|
|
355
363
|
params: {},
|
|
356
364
|
systemRole: 'system',
|
|
365
|
+
tts: {
|
|
366
|
+
showAllLocaleVoice: false,
|
|
367
|
+
sttLocale: 'auto',
|
|
368
|
+
ttsService: 'openai',
|
|
369
|
+
voice: {
|
|
370
|
+
openai: 'alloy',
|
|
371
|
+
},
|
|
372
|
+
},
|
|
357
373
|
},
|
|
358
374
|
} as LobeAgentSession,
|
|
359
375
|
session2: {
|
|
@@ -379,6 +395,14 @@ describe('sessionsReducer', () => {
|
|
|
379
395
|
draft.session1.config = {
|
|
380
396
|
model: LanguageModel.GPT4,
|
|
381
397
|
params: {},
|
|
398
|
+
tts: {
|
|
399
|
+
ttsService: 'openai',
|
|
400
|
+
sttLocale: 'auto',
|
|
401
|
+
showAllLocaleVoice: false,
|
|
402
|
+
voice: {
|
|
403
|
+
openai: 'alloy',
|
|
404
|
+
},
|
|
405
|
+
},
|
|
382
406
|
systemRole: 'system',
|
|
383
407
|
};
|
|
384
408
|
});
|
package/src/types/chatMessage.ts
CHANGED
|
@@ -22,6 +22,10 @@ export interface OpenAIFunctionCall {
|
|
|
22
22
|
export interface ChatTranslate extends Translate {
|
|
23
23
|
content?: string;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
export interface ChatTTS {
|
|
27
|
+
init?: boolean;
|
|
28
|
+
}
|
|
25
29
|
export interface ChatMessage extends BaseDataModel {
|
|
26
30
|
/**
|
|
27
31
|
* @title 内容
|
|
@@ -34,6 +38,8 @@ export interface ChatMessage extends BaseDataModel {
|
|
|
34
38
|
fromModel?: string;
|
|
35
39
|
// 翻译
|
|
36
40
|
translate?: ChatTranslate;
|
|
41
|
+
// TTS
|
|
42
|
+
tts?: ChatTTS;
|
|
37
43
|
} & Record<string, any>;
|
|
38
44
|
|
|
39
45
|
files?: string[];
|