Skip to content

Commit 4fa2ef4

Browse files
✨ feat: support TTS & STT (lobehub#443)
* ✨ feat(tts): Add tts and stt basic features * ✨ feat(tts): Handle error * 💄 style(tts): Add alert to error handler * 🐛 fix(tts): Error display * ♻️ refactor: refactor the openai initial code to the createBizOpenAI * ♻️ refactor(tts): Refactor header config * ✨ feat: Add TTS voice preview * 🐛 fix(tts): Fix header * 🐛 fix: Fix api --------- Co-authored-by: Arvin Xu <[email protected]>
1 parent 26ef087 commit 4fa2ef4

File tree

87 files changed

+1937
-230
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1937
-230
lines changed

.i18nrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ module.exports = defineConfig({
88
output: 'locales',
99
outputLocales: ['zh_TW', 'en_US', 'ru_RU', 'ja_JP', 'ko_KR'],
1010
temperature: 0,
11-
modelName: 'gpt-3.5-turbo',
11+
modelName: 'gpt-3.5-turbo-1106',
1212
});

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ This project provides some additional configuration items set with environment v
317317
| NPM | Repository | Description | Version |
318318
| ------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
319319
| [@lobehub/ui][lobe-ui-link] | [lobehub/lobe-ui][lobe-ui-github] | Lobe UI is an open-source UI component library dedicated to building AIGC web applications. | [![][lobe-ui-shield]][lobe-ui-link] |
320+
| [@lobehub/tts][lobe-tts-link] | [lobehub/lobe-tts][lobe-tts-github] | Lobe TTS is a high-quality & reliable TTS/STT React Hooks library | [![][lobe-tts-shield]][lobe-tts-link] |
320321
| [@lobehub/lint][lobe-lint-link] | [lobehub/lobe-lint][lobe-lint-github] | LobeLint provides configurations for ESlint, Stylelint, Commitlint, Prettier, Remark, and Semantic Release for LobeHub. | [![][lobe-lint-shield]][lobe-lint-link] |
321322
| @lobehub/assets | [lobehub/assets][lobe-assets-github] | Logo assets, favicons, webfonts for LobeHub. | |
322323

@@ -483,6 +484,9 @@ This project is [MIT](./LICENSE) licensed.
483484
[lobe-lint-link]: https://www.npmjs.com/package/@lobehub/lint
484485
[lobe-lint-shield]: https://img.shields.io/npm/v/@lobehub/lint?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square
485486
[lobe-theme]: https://github.com/lobehub/sd-webui-lobe-theme
487+
[lobe-tts-github]: https://github.com/lobehub/lobe-tts
488+
[lobe-tts-link]: https://www.npmjs.com/package/@lobehub/tts
489+
[lobe-tts-shield]: https://img.shields.io/npm/v/@lobehub/tts?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square
486490
[lobe-ui-github]: https://github.com/lobehub/lobe-ui
487491
[lobe-ui-link]: https://www.npmjs.com/package/@lobehub/ui
488492
[lobe-ui-shield]: https://img.shields.io/npm/v/@lobehub/ui?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square

README.zh-CN.md

+4
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ $ docker run -d -p 3210:3210 \
317317
| NPM | 仓库 | 描述 | 版本 |
318318
| ------------------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------- | --------------------------------------- |
319319
| [@lobehub/ui][lobe-ui-link] | [lobehub/lobe-ui][lobe-ui-github] | Lobe UI 是一个专为构建 AIGC 网页应用程序而设计的开源 UI 组件库。 | [![][lobe-ui-shield]][lobe-ui-link] |
320+
| [@lobehub/tts][lobe-tts-link] | [lobehub/lobe-tts][lobe-tts-github] | Lobe TTS 是一个专为 TTS/STT 建设的语音合成 / 识别 React Hooks 库 | [![][lobe-tts-shield]][lobe-tts-link] |
320321
| [@lobehub/lint][lobe-lint-link] | [lobehub/lobe-lint][lobe-lint-github] | LobeLint 为 LobeHub 提供 ESlint,Stylelint,Commitlint,Prettier,Remark 和 Semantic Release 的配置。 | [![][lobe-lint-shield]][lobe-lint-link] |
321322
| @lobehub/assets | [lobehub/assets][lobe-assets-github] | LobeHub 的 Logo 资源、favicon、网页字体。 | |
322323

@@ -483,6 +484,9 @@ This project is [MIT](./LICENSE) licensed.
483484
[lobe-lint-link]: https://www.npmjs.com/package/@lobehub/lint
484485
[lobe-lint-shield]: https://img.shields.io/npm/v/@lobehub/lint?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square
485486
[lobe-theme]: https://github.com/lobehub/sd-webui-lobe-theme
487+
[lobe-tts-github]: https://github.com/lobehub/lobe-tts
488+
[lobe-tts-link]: https://www.npmjs.com/package/@lobehub/tts
489+
[lobe-tts-shield]: https://img.shields.io/npm/v/@lobehub/tts?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square
486490
[lobe-ui-github]: https://github.com/lobehub/lobe-ui
487491
[lobe-ui-link]: https://www.npmjs.com/package/@lobehub/ui
488492
[lobe-ui-shield]: https://img.shields.io/npm/v/@lobehub/ui?color=369eff&labelColor=black&logo=npm&logoColor=white&style=flat-square

__mocks__/zustand/traditional.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ const createImpl = (createState: any) => {
1515

1616
// Reset all stores after each test run
1717
beforeEach(() => {
18-
act(() =>
19-
{ for (const resetFn of storeResetFns) {
18+
act(() => {
19+
for (const resetFn of storeResetFns) {
2020
resetFn();
21-
} },
22-
);
21+
}
22+
});
2323
});
2424

2525
export const createWithEqualityFn = (f: any) => (f === undefined ? createImpl : createImpl(f));

locales/en_US/chat.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
"withSystemRole": "Include Agent Role Setting"
3636
},
3737
"stop": "Stop",
38+
"stt": {
39+
"action": "Voice Input",
40+
"loading": "Recognizing...",
41+
"prettifying": "Prettifying..."
42+
},
3843
"temp": "Temporary",
3944
"tokenDetail": "Role Setting: {{systemRoleToken}} · Chat History: {{chatsToken}}",
4045
"tokenTag": {
@@ -57,9 +62,14 @@
5762
"openNewTopic": "Open a new topic"
5863
},
5964
"translate": {
60-
"clear": "Clear Translation"
65+
"clear": "Clear Translation",
66+
"action": "Translate"
6167
},
6268
"translateTo": "Translate",
69+
"tts": {
70+
"action": "Text to Speech",
71+
"clear": "Clear Speech"
72+
},
6373
"updateAgent": "Update Agent Information",
6474
"upload": {
6575
"actionTooltip": "Upload Image",

locales/en_US/error.json

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"PluginServerError": "Plugin server request returned an error. Please check your plugin manifest file, plugin configuration, or server implementation based on the error information below",
2929
"NoAPIKey": "OpenAI API Key is empty, please add a custom OpenAI API Key"
3030
},
31+
"stt": {
32+
"responseError": "Service request failed, please check the configuration or try again"
33+
},
34+
"tts": {
35+
"responseError": "Service request failed, please check the configuration or try again"
36+
},
3137
"unlock": {
3238
"apikey": {
3339
"title": "Use Custom API Key",

locales/en_US/setting.json

+40-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,44 @@
207207
},
208208
"title": "System Settings"
209209
},
210+
"settingTTS": {
211+
"showAllLocaleVoice": {
212+
"desc": "If disabled, only voices for the current language will be displayed",
213+
"title": "Show all locale voices"
214+
},
215+
"sttService": {
216+
"desc": "The 'browser' option refers to the native speech recognition service in the browser",
217+
"title": "Speech-to-Text Service"
218+
},
219+
"title": "Speech Services",
220+
"ttsService": {
221+
"desc": "If using the OpenAI text-to-speech service, ensure that the OpenAI model service is enabled",
222+
"title": "Text-to-Speech Service"
223+
},
224+
"voice": {
225+
"title": "Text-to-Speech Voices",
226+
"desc": "Select a voice for the current assistant, different TTS services support different voices",
227+
"preview": "Preview Voice"
228+
},
229+
"openai": {
230+
"sttModel": "OpenAI Speech Recognition Model",
231+
"ttsModel": "OpenAI Text-to-Speech Model"
232+
},
233+
"stt": "Speech Recognition Settings",
234+
"sttLocale": {
235+
"desc": "The language of the speech input, this option can improve the accuracy of speech recognition",
236+
"title": "Speech Recognition Language"
237+
},
238+
"sttPersisted": {
239+
"desc": "When enabled, speech recognition will not automatically end and requires manual click on the end button",
240+
"title": "Manually End Speech Recognition"
241+
},
242+
"tts": "Text-to-Speech Settings",
243+
"sttAutoStop": {
244+
"desc": "When disabled, speech recognition will not automatically stop and will require manual intervention to end the process.",
245+
"title": "Automatic Speech Recognition Termination"
246+
}
247+
},
210248
"settingTheme": {
211249
"avatar": {
212250
"title": "Avatar"
@@ -245,6 +283,7 @@
245283
"tab": {
246284
"agent": "Default Agent",
247285
"common": "Common Settings",
248-
"llm": "Custom LLM API"
286+
"llm": "Custom LLM API",
287+
"tts": "Speech Services"
249288
}
250289
}

locales/ja_JP/chat.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
"withSystemRole": "エージェントの役割設定を含む"
3636
},
3737
"stop": "停止",
38+
"stt": {
39+
"action": "音声入力",
40+
"loading": "読み取り中...",
41+
"prettifying": "美化中..."
42+
},
3843
"temp": "一時",
3944
"tokenDetail": "役割設定: {{systemRoleToken}} · チャット履歴: {{chatsToken}}",
4045
"tokenTag": {
@@ -57,9 +62,14 @@
5762
"openNewTopic": "新しいトピックを開く"
5863
},
5964
"translate": {
60-
"clear": "翻訳をクリア"
65+
"clear": "翻訳をクリア",
66+
"action": "翻訳"
6167
},
6268
"translateTo": "翻訳",
69+
"tts": {
70+
"action": "音声読み上げ",
71+
"clear": "音声削除"
72+
},
6373
"updateAgent": "エージェント情報を更新",
6474
"upload": {
6575
"actionTooltip": "画像をアップロード",

locales/ja_JP/error.json

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"PluginApiNotFound": "申し訳ありませんが、プラグインのマニフェストに指定されたAPIが見つかりませんでした。リクエストメソッドとプラグインのマニフェストのAPIが一致しているかどうかを確認してください",
2929
"NoAPIKey": "OpenAI APIキーが空です。カスタムOpenAI APIキーを追加してください。"
3030
},
31+
"stt": {
32+
"responseError": "サービスリクエストが失敗しました。設定を確認するか、もう一度お試しください"
33+
},
34+
"tts": {
35+
"responseError": "サービスリクエストが失敗しました。設定を確認するか、もう一度お試しください"
36+
},
3137
"unlock": {
3238
"apikey": {
3339
"title": "カスタムAPIキーの使用",

locales/ja_JP/setting.json

+40-1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,44 @@
194194
},
195195
"title": "システム設定"
196196
},
197+
"settingTTS": {
198+
"showAllLocaleVoice": {
199+
"desc": "关闭すると、現在の言語の音声のみが表示されます",
200+
"title": "すべての言語の音声を表示"
201+
},
202+
"sttService": {
203+
"desc": "ブラウザはブラウザのネイティブ音声認識サービスです",
204+
"title": "音声認識サービス"
205+
},
206+
"title": "音声サービス",
207+
"ttsService": {
208+
"desc": "OpenAIの音声合成サービスを使用する場合、OpenAIモデルサービスが有効になっていることを確認する必要があります",
209+
"title": "音声合成サービス"
210+
},
211+
"voice": {
212+
"title": "音声合成音声源",
213+
"desc": "現在のアシスタントに適した音声を選択します。異なるTTSサービスは異なる音声をサポートしています",
214+
"preview": "プレビュー"
215+
},
216+
"openai": {
217+
"sttModel": "OpenAI 音声認識モデル",
218+
"ttsModel": "OpenAI 音声合成モデル"
219+
},
220+
"stt": "音声認識設定",
221+
"sttLocale": {
222+
"desc": "音声入力の言語、このオプションを選択すると音声認識の精度が向上します",
223+
"title": "音声認識言語"
224+
},
225+
"sttPersisted": {
226+
"desc": "有効にすると、音声認識が自動的に終了せず、手動で終了ボタンをクリックする必要があります",
227+
"title": "音声認識の手動終了"
228+
},
229+
"tts": "音声合成設定",
230+
"sttAutoStop": {
231+
"desc": "オフにすると、音声認識が自動的に終了せず、手動で終了ボタンをクリックする必要があります",
232+
"title": "音声認識の自動終了"
233+
}
234+
},
197235
"settingTheme": {
198236
"avatar": {
199237
"title": "アバター"
@@ -232,6 +270,7 @@
232270
"tab": {
233271
"agent": "デフォルトのアシスタント",
234272
"common": "一般設定",
235-
"llm": "言語モデル"
273+
"llm": "言語モデル",
274+
"tts": "音声サービス"
236275
}
237276
}

locales/ko_KR/chat.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
"withSystemRole": "도우미 역할 포함"
3636
},
3737
"stop": "정지",
38+
"stt": {
39+
"action": "음성 입력",
40+
"loading": "인식 중...",
41+
"prettifying": "미화 중..."
42+
},
3843
"temp": "임시",
3944
"tokenDetail": "역할 설정: {{systemRoleToken}} · 대화 기록: {{chatsToken}}",
4045
"tokenTag": {
@@ -57,9 +62,14 @@
5762
"openNewTopic": "새로운 주제 열기"
5863
},
5964
"translate": {
60-
"clear": "번역 지우기"
65+
"clear": "번역 지우기",
66+
"action": "번역"
6167
},
6268
"translateTo": "번역",
69+
"tts": {
70+
"action": "음성 읽기",
71+
"clear": "음성 삭제"
72+
},
6373
"updateAgent": "도우미 정보 업데이트",
6474
"upload": {
6575
"actionTooltip": "이미지 업로드",

locales/ko_KR/error.json

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"OpenAIBizError": "OpenAI 서비스 요청 중 오류가 발생했습니다. 아래 정보를 확인하고 문제를 해결하거나 다시 시도해주세요.",
2929
"NoAPIKey": "OpenAI API 키가 비어 있습니다. 사용자 정의 OpenAI API 키를 추가해주세요."
3030
},
31+
"stt": {
32+
"responseError": "서비스 요청이 실패했습니다. 구성을 확인하거나 다시 시도해주세요."
33+
},
34+
"tts": {
35+
"responseError": "서비스 요청이 실패했습니다. 구성을 확인하거나 다시 시도해주세요."
36+
},
3137
"unlock": {
3238
"apikey": {
3339
"title": "사용자 정의 API 키 사용",

locales/ko_KR/setting.json

+40-1
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,44 @@
194194
},
195195
"title": "시스템 설정"
196196
},
197+
"settingTTS": {
198+
"showAllLocaleVoice": {
199+
"desc": "현재 언어의 음성만 표시하려면 닫으십시오",
200+
"title": "모든 언어 음성 표시"
201+
},
202+
"sttService": {
203+
"desc": "브라우저는 브라우저 기본 음성 인식 서비스입니다",
204+
"title": "음성 인식 서비스"
205+
},
206+
"title": "음성 서비스",
207+
"ttsService": {
208+
"desc": "OpenAI 음성 합성 서비스를 사용하는 경우 OpenAI 모델 서비스가 열려 있어야 합니다",
209+
"title": "음성 합성 서비스"
210+
},
211+
"voice": {
212+
"title": "음성 합성 음성",
213+
"desc": "현재 어시스턴트에 대한 음성을 선택하십시오. 각기 다른 TTS 서비스는 다른 음성을 지원합니다.",
214+
"preview": "프리뷰 음성"
215+
},
216+
"openai": {
217+
"sttModel": "OpenAI 음성 인식 모델",
218+
"ttsModel": "OpenAI 음성 합성 모델"
219+
},
220+
"stt": "음성 인식 설정",
221+
"sttLocale": {
222+
"desc": "음성 입력의 언어, 이 옵션을 통해 음성 인식 정확도를 높일 수 있습니다.",
223+
"title": "음성 인식 언어"
224+
},
225+
"sttPersisted": {
226+
"desc": "활성화하면 음성 인식이 자동으로 종료되지 않고, 수동으로 종료 버튼을 클릭해야 합니다.",
227+
"title": "음성 인식 수동 종료"
228+
},
229+
"tts": "음성 합성 설정",
230+
"sttAutoStop": {
231+
"desc": "자동으로 종료되지 않고 수동으로 종료 버튼을 클릭해야 하는 음성 인식을 사용하지 않습니다.",
232+
"title": "음성 인식 자동 종료"
233+
}
234+
},
197235
"settingTheme": {
198236
"avatar": {
199237
"title": "아바타"
@@ -232,6 +270,7 @@
232270
"tab": {
233271
"agent": "기본 도우미",
234272
"common": "일반 설정",
235-
"llm": "언어 모델"
273+
"llm": "언어 모델",
274+
"tts": "음성 서비스"
236275
}
237276
}

locales/ru_RU/chat.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
"withSystemRole": "С указанием роли помощника"
3636
},
3737
"stop": "Остановить",
38+
"stt": {
39+
"action": "Голосовой ввод",
40+
"loading": "Идет распознавание...",
41+
"prettifying": "Форматирование..."
42+
},
3843
"temp": "Временный",
3944
"tokenDetail": "Роль помощника: {{systemRoleToken}} · История сообщений: {{chatsToken}}",
4045
"tokenTag": {
@@ -57,9 +62,14 @@
5762
"openNewTopic": "Открыть новую тему"
5863
},
5964
"translate": {
60-
"clear": "Очистить перевод"
65+
"clear": "Очистить перевод",
66+
"action": "перевести"
6167
},
6268
"translateTo": "Перевести на",
69+
"tts": {
70+
"action": "воспроизвести речь",
71+
"clear": "очистить речь"
72+
},
6373
"updateAgent": "Обновить информацию о помощнике",
6474
"upload": {
6575
"actionTooltip": "Загрузить изображение",

locales/ru_RU/error.json

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"PluginServerError": "Запрос сервера плагина возвратил ошибку. Проверьте файл манифеста плагина, конфигурацию плагина или реализацию сервера на основе информации об ошибке ниже",
2929
"NoAPIKey": "Ключ OpenAI API пуст, пожалуйста, добавьте свой собственный ключ OpenAI API"
3030
},
31+
"stt": {
32+
"responseError": "Ошибка запроса сервиса. Пожалуйста, проверьте конфигурацию или повторите попытку"
33+
},
34+
"tts": {
35+
"responseError": "Ошибка запроса сервиса. Пожалуйста, проверьте конфигурацию или повторите попытку"
36+
},
3137
"unlock": {
3238
"apikey": {
3339
"title": "Использовать собственный ключ API",

0 commit comments

Comments
 (0)