@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
|
@@ -207,6 +207,44 @@
|
|
|
207
207
|
},
|
|
208
208
|
"title": "Настройки системы"
|
|
209
209
|
},
|
|
210
|
+
"settingTTS": {
|
|
211
|
+
"showAllLocaleVoice": {
|
|
212
|
+
"desc": "Если отключено, отображаются только голоса текущего языка",
|
|
213
|
+
"title": "Показать все голоса локали"
|
|
214
|
+
},
|
|
215
|
+
"sttService": {
|
|
216
|
+
"desc": "где broswer - это встроенная в браузер служба распознавания речи",
|
|
217
|
+
"title": "Служба распознавания речи"
|
|
218
|
+
},
|
|
219
|
+
"title": "Служба речи",
|
|
220
|
+
"ttsService": {
|
|
221
|
+
"desc": "Если используется услуга синтеза речи OpenAI, убедитесь, что услуга модели OpenAI включена",
|
|
222
|
+
"title": "Служба синтеза речи"
|
|
223
|
+
},
|
|
224
|
+
"voice": {
|
|
225
|
+
"title": "Голосовой синтез",
|
|
226
|
+
"desc": "Выберите голос для текущего помощника, различные службы TTS поддерживают разные источники звука",
|
|
227
|
+
"preview": "Предварительный просмотр голоса"
|
|
228
|
+
},
|
|
229
|
+
"openai": {
|
|
230
|
+
"sttModel": "Модель распознавания речи OpenAI",
|
|
231
|
+
"ttsModel": "Модель синтеза речи OpenAI"
|
|
232
|
+
},
|
|
233
|
+
"stt": "Настройки распознавания речи",
|
|
234
|
+
"sttLocale": {
|
|
235
|
+
"desc": "Язык речи для ввода речи, этот параметр может повысить точность распознавания речи",
|
|
236
|
+
"title": "Язык распознавания речи"
|
|
237
|
+
},
|
|
238
|
+
"sttPersisted": {
|
|
239
|
+
"desc": "При включении распознавание речи не будет автоматически завершаться, необходимо вручную нажать кнопку завершения",
|
|
240
|
+
"title": "Вручную завершить распознавание речи"
|
|
241
|
+
},
|
|
242
|
+
"tts": "Настройки синтеза речи",
|
|
243
|
+
"sttAutoStop": {
|
|
244
|
+
"desc": "После отключения распознавания речи оно не будет автоматически останавливаться, вам нужно будет вручную нажать кнопку завершения",
|
|
245
|
+
"title": "Автоматическое завершение распознавания речи"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
210
248
|
"settingTheme": {
|
|
211
249
|
"avatar": {
|
|
212
250
|
"title": "Аватар"
|
|
@@ -245,6 +283,7 @@
|
|
|
245
283
|
"tab": {
|
|
246
284
|
"agent": "Помощник по умолчанию",
|
|
247
285
|
"common": "Общие настройки",
|
|
248
|
-
"llm": "Пользовательский API GPT"
|
|
286
|
+
"llm": "Пользовательский API GPT",
|
|
287
|
+
"tts": "Сервис речи"
|
|
249
288
|
}
|
|
250
289
|
}
|
package/locales/zh_CN/chat.json
CHANGED
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"withSystemRole": "包含助手角色设定"
|
|
36
36
|
},
|
|
37
37
|
"stop": "停止",
|
|
38
|
+
"stt": {
|
|
39
|
+
"action": "语音输入",
|
|
40
|
+
"loading": "识别中...",
|
|
41
|
+
"prettifying": "润色中..."
|
|
42
|
+
},
|
|
38
43
|
"temp": "临时",
|
|
39
44
|
"tokenDetail": "角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}",
|
|
40
45
|
"tokenTag": {
|
|
@@ -55,9 +60,13 @@
|
|
|
55
60
|
"title": "话题列表"
|
|
56
61
|
},
|
|
57
62
|
"translate": {
|
|
63
|
+
"action": "翻译",
|
|
58
64
|
"clear": "删除翻译"
|
|
59
65
|
},
|
|
60
|
-
"
|
|
66
|
+
"tts": {
|
|
67
|
+
"action": "语音朗读",
|
|
68
|
+
"clear": "删除语音"
|
|
69
|
+
},
|
|
61
70
|
"updateAgent": "更新助理信息",
|
|
62
71
|
"upload": {
|
|
63
72
|
"actionTooltip": "上传图片",
|
package/locales/zh_CN/error.json
CHANGED
|
@@ -28,6 +28,12 @@
|
|
|
28
28
|
"OpenAIBizError": "请求 OpenAI 服务出错,请根据以下信息排查或重试",
|
|
29
29
|
"NoAPIKey": "OpenAI API Key 为空,请添加自定义 OpenAI API Key"
|
|
30
30
|
},
|
|
31
|
+
"stt": {
|
|
32
|
+
"responseError": "服务请求失败,请检查配置或重试"
|
|
33
|
+
},
|
|
34
|
+
"tts": {
|
|
35
|
+
"responseError": "服务请求失败,请检查配置或重试"
|
|
36
|
+
},
|
|
31
37
|
"unlock": {
|
|
32
38
|
"apikey": {
|
|
33
39
|
"addProxyUrl": "添加 OpenAI 代理地址(可选)",
|
|
@@ -194,6 +194,40 @@
|
|
|
194
194
|
},
|
|
195
195
|
"title": "系统设置"
|
|
196
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
|
+
},
|
|
197
231
|
"settingTheme": {
|
|
198
232
|
"avatar": {
|
|
199
233
|
"title": "头像"
|
|
@@ -232,6 +266,7 @@
|
|
|
232
266
|
"tab": {
|
|
233
267
|
"agent": "默认助手",
|
|
234
268
|
"common": "通用设置",
|
|
235
|
-
"llm": "语言模型"
|
|
269
|
+
"llm": "语言模型",
|
|
270
|
+
"tts": "语音服务"
|
|
236
271
|
}
|
|
237
272
|
}
|
package/locales/zh_TW/chat.json
CHANGED
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"withSystemRole": "包含助手角色設定"
|
|
36
36
|
},
|
|
37
37
|
"stop": "停止",
|
|
38
|
+
"stt": {
|
|
39
|
+
"action": "語音輸入",
|
|
40
|
+
"loading": "辨識中...",
|
|
41
|
+
"prettifying": "美化中..."
|
|
42
|
+
},
|
|
38
43
|
"temp": "臨時",
|
|
39
44
|
"tokenDetail": "角色設定: {{systemRoleToken}} · 歷史訊息: {{chatsToken}}",
|
|
40
45
|
"tokenTag": {
|
|
@@ -57,9 +62,14 @@
|
|
|
57
62
|
"openNewTopic": "開啟新話題"
|
|
58
63
|
},
|
|
59
64
|
"translate": {
|
|
60
|
-
"clear": "刪除翻譯"
|
|
65
|
+
"clear": "刪除翻譯",
|
|
66
|
+
"action": "翻譯"
|
|
61
67
|
},
|
|
62
68
|
"translateTo": "翻譯",
|
|
69
|
+
"tts": {
|
|
70
|
+
"action": "語音朗讀",
|
|
71
|
+
"clear": "清除語音"
|
|
72
|
+
},
|
|
63
73
|
"updateAgent": "更新助理資訊",
|
|
64
74
|
"upload": {
|
|
65
75
|
"actionTooltip": "上傳圖片",
|
package/locales/zh_TW/error.json
CHANGED
|
@@ -28,6 +28,12 @@
|
|
|
28
28
|
"PluginServerError": "外掛伺服器請求回傳錯誤。請根據下面的錯誤資訊檢查您的外掛描述檔案、外掛設定或伺服器實作",
|
|
29
29
|
"NoAPIKey": "OpenAI API 金鑰為空,請添加自訂 OpenAI API 金鑰"
|
|
30
30
|
},
|
|
31
|
+
"stt": {
|
|
32
|
+
"responseError": "服務請求失敗,請檢查配置或重試"
|
|
33
|
+
},
|
|
34
|
+
"tts": {
|
|
35
|
+
"responseError": "服務請求失敗,請檢查配置或重試"
|
|
36
|
+
},
|
|
31
37
|
"unlock": {
|
|
32
38
|
"apikey": {
|
|
33
39
|
"title": "使用自定義 API Key",
|
|
@@ -207,6 +207,44 @@
|
|
|
207
207
|
},
|
|
208
208
|
"title": "系統設定"
|
|
209
209
|
},
|
|
210
|
+
"settingTTS": {
|
|
211
|
+
"showAllLocaleVoice": {
|
|
212
|
+
"desc": "關閉則只顯示當前語種的聲源",
|
|
213
|
+
"title": "顯示所有語種聲源"
|
|
214
|
+
},
|
|
215
|
+
"sttService": {
|
|
216
|
+
"desc": "其中 broswer 為瀏覽器原生的語音識別服務",
|
|
217
|
+
"title": "語音識別服務"
|
|
218
|
+
},
|
|
219
|
+
"title": "語音服務",
|
|
220
|
+
"ttsService": {
|
|
221
|
+
"desc": "如使用 OpenAI 語音合成服務,需要保證 OpenAI 模型服務已開啟",
|
|
222
|
+
"title": "語音合成服務"
|
|
223
|
+
},
|
|
224
|
+
"voice": {
|
|
225
|
+
"title": "語音合成聲源",
|
|
226
|
+
"desc": "為當前助手挑選一個聲音,不同 TTS 服務支持的聲源不同",
|
|
227
|
+
"preview": "預覽"
|
|
228
|
+
},
|
|
229
|
+
"openai": {
|
|
230
|
+
"sttModel": "OpenAI 語音識別模型",
|
|
231
|
+
"ttsModel": "OpenAI 語音合成模型"
|
|
232
|
+
},
|
|
233
|
+
"stt": "語音識別設定",
|
|
234
|
+
"sttLocale": {
|
|
235
|
+
"desc": "語音輸入的語言,此選項可提高語音識別準確率",
|
|
236
|
+
"title": "語音識別語言"
|
|
237
|
+
},
|
|
238
|
+
"sttPersisted": {
|
|
239
|
+
"desc": "開啟後,語音識別將不會自動結束,需要手動點擊結束按鈕",
|
|
240
|
+
"title": "手動結束語音識別"
|
|
241
|
+
},
|
|
242
|
+
"tts": "語音合成設定",
|
|
243
|
+
"sttAutoStop": {
|
|
244
|
+
"desc": "關閉後,語音識別將不會自動結束,需要手動點擊結束按鈕",
|
|
245
|
+
"title": "自動結束語音識別"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
210
248
|
"settingTheme": {
|
|
211
249
|
"avatar": {
|
|
212
250
|
"title": "頭像"
|
|
@@ -245,6 +283,7 @@
|
|
|
245
283
|
"tab": {
|
|
246
284
|
"agent": "預設助理",
|
|
247
285
|
"common": "通用設定",
|
|
248
|
-
"llm": "語言模型"
|
|
286
|
+
"llm": "語言模型",
|
|
287
|
+
"tts": "語音服務"
|
|
249
288
|
}
|
|
250
289
|
}
|
package/next.config.mjs
CHANGED
|
@@ -40,25 +40,7 @@ const nextConfig = {
|
|
|
40
40
|
remotePatterns: [
|
|
41
41
|
{
|
|
42
42
|
hostname: 'registry.npmmirror.com',
|
|
43
|
-
pathname: '/@lobehub
|
|
44
|
-
port: '',
|
|
45
|
-
protocol: 'https',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
hostname: 'registry.npmmirror.com',
|
|
49
|
-
pathname: '/@lobehub/assets-emoji-anim/1.0.0/files/assets/**',
|
|
50
|
-
port: '',
|
|
51
|
-
protocol: 'https',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
hostname: 'registry.npmmirror.com',
|
|
55
|
-
pathname: '/@lobehub/assets-logo/1.1.0/files/assets/**',
|
|
56
|
-
port: '',
|
|
57
|
-
protocol: 'https',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
hostname: 'registry.npmmirror.com',
|
|
61
|
-
pathname: '/@lobehub/assets-favicons/latest/files/assets/**',
|
|
43
|
+
pathname: '/@lobehub/**',
|
|
62
44
|
port: '',
|
|
63
45
|
protocol: 'https',
|
|
64
46
|
},
|
|
@@ -69,7 +51,7 @@ const nextConfig = {
|
|
|
69
51
|
|
|
70
52
|
reactStrictMode: true,
|
|
71
53
|
|
|
72
|
-
transpilePackages: ['antd-style', '@lobehub/ui'],
|
|
54
|
+
transpilePackages: ['antd-style', '@lobehub/ui', '@lobehub/tts'],
|
|
73
55
|
|
|
74
56
|
webpack(config) {
|
|
75
57
|
config.experiments = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.102.1",
|
|
4
4
|
"description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
"@icons-pack/react-simple-icons": "^9",
|
|
71
71
|
"@lobehub/chat-plugin-sdk": "latest",
|
|
72
72
|
"@lobehub/chat-plugins-gateway": "latest",
|
|
73
|
+
"@lobehub/tts": "latest",
|
|
73
74
|
"@lobehub/ui": "latest",
|
|
74
75
|
"@vercel/analytics": "^1",
|
|
75
76
|
"ahooks": "^3",
|
|
@@ -127,8 +128,8 @@
|
|
|
127
128
|
"@ducanh2912/next-pwa": "^9.7.2",
|
|
128
129
|
"@lobehub/i18n-cli": "latest",
|
|
129
130
|
"@lobehub/lint": "latest",
|
|
130
|
-
"@next/bundle-analyzer": "14.0.
|
|
131
|
-
"@next/eslint-plugin-next": "14.0.
|
|
131
|
+
"@next/bundle-analyzer": "14.0.3",
|
|
132
|
+
"@next/eslint-plugin-next": "14.0.3",
|
|
132
133
|
"@testing-library/jest-dom": "^6",
|
|
133
134
|
"@testing-library/react": "^14",
|
|
134
135
|
"@types/chroma-js": "^2",
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
2
|
-
|
|
3
|
-
import { getServerConfig } from '@/config/server';
|
|
4
|
-
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
5
|
-
import { ChatErrorType, ErrorType } from '@/types/fetch';
|
|
6
1
|
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
7
2
|
|
|
8
|
-
import {
|
|
9
|
-
import { createAzureOpenai } from '../createAzureOpenai';
|
|
10
|
-
import { createOpenai } from '../createOpenai';
|
|
11
|
-
import { createErrorResponse } from '../errorResponse';
|
|
3
|
+
import { createBizOpenAI } from '../createBizOpenAI';
|
|
12
4
|
import { createChatCompletion } from './createChatCompletion';
|
|
13
5
|
|
|
14
6
|
export const runtime = 'edge';
|
|
@@ -16,38 +8,10 @@ export const runtime = 'edge';
|
|
|
16
8
|
export const POST = async (req: Request) => {
|
|
17
9
|
const payload = (await req.json()) as OpenAIChatStreamPayload;
|
|
18
10
|
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
const result = checkAuth({ accessCode, apiKey });
|
|
22
|
-
|
|
23
|
-
if (!result.auth) {
|
|
24
|
-
return createErrorResponse(result.error as ErrorType);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let openai: OpenAI;
|
|
28
|
-
|
|
29
|
-
const { USE_AZURE_OPENAI } = getServerConfig();
|
|
30
|
-
const useAzureOpenAI = useAzure || USE_AZURE_OPENAI;
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
if (useAzureOpenAI) {
|
|
34
|
-
openai = createAzureOpenai({
|
|
35
|
-
apiVersion,
|
|
36
|
-
endpoint,
|
|
37
|
-
model: payload.model,
|
|
38
|
-
userApiKey: apiKey,
|
|
39
|
-
});
|
|
40
|
-
} else {
|
|
41
|
-
openai = createOpenai(apiKey, endpoint);
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
if ((error as Error).cause === ChatErrorType.NoAPIKey) {
|
|
45
|
-
return createErrorResponse(ChatErrorType.NoAPIKey);
|
|
46
|
-
}
|
|
11
|
+
const openaiOrErrResponse = createBizOpenAI(req, payload.model);
|
|
47
12
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
13
|
+
// if resOrOpenAI is a Response, it means there is an error,just return it
|
|
14
|
+
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
|
|
51
15
|
|
|
52
|
-
return createChatCompletion({ openai, payload });
|
|
16
|
+
return createChatCompletion({ openai: openaiOrErrResponse, payload });
|
|
53
17
|
};
|
|
@@ -4,7 +4,7 @@ import urlJoin from 'url-join';
|
|
|
4
4
|
import { getServerConfig } from '@/config/server';
|
|
5
5
|
import { ChatErrorType } from '@/types/fetch';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// create Azure OpenAI Instance
|
|
8
8
|
export const createAzureOpenai = (params: {
|
|
9
9
|
apiVersion?: string | null;
|
|
10
10
|
endpoint?: string | null;
|
|
@@ -3,7 +3,7 @@ import OpenAI from 'openai';
|
|
|
3
3
|
import { getServerConfig } from '@/config/server';
|
|
4
4
|
import { ChatErrorType } from '@/types/fetch';
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// create OpenAI instance
|
|
7
7
|
export const createOpenai = (userApiKey: string | null, endpoint?: string | null) => {
|
|
8
8
|
const { OPENAI_API_KEY, OPENAI_PROXY_URL } = getServerConfig();
|
|
9
9
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import OpenAI from 'openai/index';
|
|
2
|
+
|
|
3
|
+
import { checkAuth } from '@/app/api/auth';
|
|
4
|
+
import { getServerConfig } from '@/config/server';
|
|
5
|
+
import { getOpenAIAuthFromRequest } from '@/const/fetch';
|
|
6
|
+
import { ChatErrorType, ErrorType } from '@/types/fetch';
|
|
7
|
+
|
|
8
|
+
import { createErrorResponse } from '../errorResponse';
|
|
9
|
+
import { createAzureOpenai } from './createAzureOpenai';
|
|
10
|
+
import { createOpenai } from './createOpenai';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* createOpenAI Instance with Auth and azure openai support
|
|
14
|
+
* if auth not pass ,just return error response
|
|
15
|
+
*/
|
|
16
|
+
export const createBizOpenAI = (req: Request, model: string): Response | OpenAI => {
|
|
17
|
+
const { apiKey, accessCode, endpoint, useAzure, apiVersion } = getOpenAIAuthFromRequest(req);
|
|
18
|
+
|
|
19
|
+
const result = checkAuth({ accessCode, apiKey });
|
|
20
|
+
|
|
21
|
+
if (!result.auth) {
|
|
22
|
+
return createErrorResponse(result.error as ErrorType);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let openai: OpenAI;
|
|
26
|
+
|
|
27
|
+
const { USE_AZURE_OPENAI } = getServerConfig();
|
|
28
|
+
const useAzureOpenAI = useAzure || USE_AZURE_OPENAI;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
if (useAzureOpenAI) {
|
|
32
|
+
openai = createAzureOpenai({ apiVersion, endpoint, model, userApiKey: apiKey });
|
|
33
|
+
} else {
|
|
34
|
+
openai = createOpenai(apiKey, endpoint);
|
|
35
|
+
}
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if ((error as Error).cause === ChatErrorType.NoAPIKey) {
|
|
38
|
+
return createErrorResponse(ChatErrorType.NoAPIKey);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.error(error); // log error to trace it
|
|
42
|
+
return createErrorResponse(ChatErrorType.InternalServerError);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return openai;
|
|
46
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { OpenAISTTPayload } from '@lobehub/tts';
|
|
2
|
+
import { createOpenaiAudioTranscriptions } from '@lobehub/tts/server';
|
|
3
|
+
|
|
4
|
+
import { createBizOpenAI } from '../createBizOpenAI';
|
|
5
|
+
|
|
6
|
+
export const runtime = 'edge';
|
|
7
|
+
|
|
8
|
+
export const POST = async (req: Request) => {
|
|
9
|
+
const formData = await req.formData();
|
|
10
|
+
const speechBlob = formData.get('speech') as Blob;
|
|
11
|
+
const optionsString = formData.get('options') as string;
|
|
12
|
+
const payload = {
|
|
13
|
+
options: JSON.parse(optionsString),
|
|
14
|
+
speech: speechBlob,
|
|
15
|
+
} as OpenAISTTPayload;
|
|
16
|
+
|
|
17
|
+
const openaiOrErrResponse = createBizOpenAI(req, payload.options.model);
|
|
18
|
+
|
|
19
|
+
// if resOrOpenAI is a Response, it means there is an error,just return it
|
|
20
|
+
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
|
|
21
|
+
|
|
22
|
+
const res = await createOpenaiAudioTranscriptions({ openai: openaiOrErrResponse, payload });
|
|
23
|
+
|
|
24
|
+
return new Response(JSON.stringify(res), {
|
|
25
|
+
headers: {
|
|
26
|
+
'content-type': 'application/json;charset=UTF-8',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OpenAITTSPayload } from '@lobehub/tts';
|
|
2
|
+
import { createOpenaiAudioSpeech } from '@lobehub/tts/server';
|
|
3
|
+
|
|
4
|
+
import { createBizOpenAI } from '../createBizOpenAI';
|
|
5
|
+
|
|
6
|
+
export const runtime = 'edge';
|
|
7
|
+
|
|
8
|
+
export const POST = async (req: Request) => {
|
|
9
|
+
const payload = (await req.json()) as OpenAITTSPayload;
|
|
10
|
+
|
|
11
|
+
const openaiOrErrResponse = createBizOpenAI(req, payload.options.model);
|
|
12
|
+
|
|
13
|
+
// if resOrOpenAI is a Response, it means there is an error,just return it
|
|
14
|
+
if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
|
|
15
|
+
|
|
16
|
+
return await createOpenaiAudioSpeech({ openai: openaiOrErrResponse, payload });
|
|
17
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EdgeSpeechPayload, EdgeSpeechTTS } from '@lobehub/tts';
|
|
2
|
+
|
|
3
|
+
export const runtime = 'edge';
|
|
4
|
+
|
|
5
|
+
export const POST = async (req: Request) => {
|
|
6
|
+
const payload = (await req.json()) as EdgeSpeechPayload;
|
|
7
|
+
|
|
8
|
+
return await EdgeSpeechTTS.createRequest({ payload });
|
|
9
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MicrosoftSpeechPayload, MicrosoftSpeechTTS } from '@lobehub/tts';
|
|
2
|
+
|
|
3
|
+
export const runtime = 'edge';
|
|
4
|
+
|
|
5
|
+
export const POST = async (req: Request) => {
|
|
6
|
+
const payload = (await req.json()) as MicrosoftSpeechPayload;
|
|
7
|
+
|
|
8
|
+
return await MicrosoftSpeechTTS.createRequest({ payload });
|
|
9
|
+
};
|
|
@@ -7,6 +7,7 @@ import { memo } from 'react';
|
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
|
9
9
|
|
|
10
|
+
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
10
11
|
import { useGlobalStore } from '@/store/global';
|
|
11
12
|
import { useSessionChatInit, useSessionStore } from '@/store/session';
|
|
12
13
|
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
@@ -85,7 +86,7 @@ const Header = memo(() => {
|
|
|
85
86
|
<ActionIcon
|
|
86
87
|
icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
|
|
87
88
|
onClick={() => toggleConfig()}
|
|
88
|
-
size={
|
|
89
|
+
size={DESKTOP_HEADER_ICON_SIZE}
|
|
89
90
|
title={t('roleAndArchive')}
|
|
90
91
|
/>
|
|
91
92
|
{!isInbox && (
|
|
@@ -94,7 +95,7 @@ const Header = memo(() => {
|
|
|
94
95
|
onClick={() => {
|
|
95
96
|
router.push(pathString('/chat/settings', { hash: location.hash }));
|
|
96
97
|
}}
|
|
97
|
-
size={
|
|
98
|
+
size={DESKTOP_HEADER_ICON_SIZE}
|
|
98
99
|
title={t('header.session', { ns: 'setting' })}
|
|
99
100
|
/>
|
|
100
101
|
)}
|
|
@@ -5,6 +5,7 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
+
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
8
9
|
import { useSessionStore } from '@/store/session';
|
|
9
10
|
|
|
10
11
|
import SessionSearchBar from '../../features/SessionSearchBar';
|
|
@@ -28,11 +29,10 @@ const Header = memo(() => {
|
|
|
28
29
|
<Flexbox className={styles.top} gap={16} padding={16}>
|
|
29
30
|
<Flexbox distribution={'space-between'} horizontal>
|
|
30
31
|
<Logo className={styles.logo} size={36} type={'text'} />
|
|
31
|
-
|
|
32
32
|
<ActionIcon
|
|
33
33
|
icon={MessageSquarePlus}
|
|
34
34
|
onClick={() => createSession()}
|
|
35
|
-
size={
|
|
35
|
+
size={DESKTOP_HEADER_ICON_SIZE}
|
|
36
36
|
style={{ flex: 'none' }}
|
|
37
37
|
title={t('newAgent')}
|
|
38
38
|
/>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ActionIcon, MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
2
|
-
import {
|
|
1
|
+
import { ActionIcon, Icon, MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
2
|
+
import { Dropdown, MenuProps } from 'antd';
|
|
3
|
+
import { Clock3, MoreHorizontal, Settings, Share2 } from 'lucide-react';
|
|
3
4
|
import { useRouter } from 'next/navigation';
|
|
4
|
-
import { memo } from 'react';
|
|
5
|
+
import { memo, useState } from 'react';
|
|
5
6
|
import { useTranslation } from 'react-i18next';
|
|
6
7
|
|
|
7
8
|
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
@@ -15,6 +16,7 @@ import ShareButton from '../../features/ChatHeader/ShareButton';
|
|
|
15
16
|
const MobileHeader = memo(() => {
|
|
16
17
|
const { t } = useTranslation('chat');
|
|
17
18
|
const router = useRouter();
|
|
19
|
+
const [open, setOpen] = useState(false);
|
|
18
20
|
|
|
19
21
|
const [isInbox, title] = useSessionStore((s) => [
|
|
20
22
|
sessionSelectors.isInboxSession(s),
|
|
@@ -25,21 +27,37 @@ const MobileHeader = memo(() => {
|
|
|
25
27
|
|
|
26
28
|
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
27
29
|
|
|
30
|
+
const items: MenuProps['items'] = [
|
|
31
|
+
{
|
|
32
|
+
icon: <Icon icon={Share2} />,
|
|
33
|
+
key: 'share',
|
|
34
|
+
label: t('share', { ns: 'common' }),
|
|
35
|
+
onClick: () => setOpen(true),
|
|
36
|
+
},
|
|
37
|
+
!isInbox && {
|
|
38
|
+
icon: <Icon icon={Settings} />,
|
|
39
|
+
key: 'settings',
|
|
40
|
+
label: t('header.session', { ns: 'setting' }),
|
|
41
|
+
onClick: () => router.push(pathString('/chat/settings', { hash: location.hash })),
|
|
42
|
+
},
|
|
43
|
+
].filter(Boolean) as MenuProps['items'];
|
|
44
|
+
|
|
28
45
|
return (
|
|
29
46
|
<MobileNavBar
|
|
30
47
|
center={<MobileNavBarTitle title={displayTitle} />}
|
|
31
48
|
onBackClick={() => router.push('/chat')}
|
|
32
49
|
right={
|
|
33
50
|
<>
|
|
34
|
-
<ShareButton />
|
|
35
51
|
<ActionIcon icon={Clock3} onClick={() => toggleConfig()} size={MOBILE_HEADER_ICON_SIZE} />
|
|
36
|
-
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
<ShareButton mobile open={open} setOpen={setOpen} />
|
|
53
|
+
<Dropdown
|
|
54
|
+
menu={{
|
|
55
|
+
items,
|
|
56
|
+
}}
|
|
57
|
+
trigger={['click']}
|
|
58
|
+
>
|
|
59
|
+
<ActionIcon icon={MoreHorizontal} />
|
|
60
|
+
</Dropdown>
|
|
43
61
|
</>
|
|
44
62
|
}
|
|
45
63
|
showBackButton
|