feat(tts): add OpenAI-compatible custom server TTS provider with configurable model, pitch, and volume (#859)

Co-authored-by: Alexander Busse <alex@ableph.net>
This commit is contained in:
Alexander Busse
2026-04-12 10:33:15 +03:00
committed by GitHub
co-authored by Alexander Busse
parent 8a836d4aed
commit 055b6f6af0
9 changed files with 285 additions and 77 deletions
+18 -3
View File
@@ -31,11 +31,15 @@ export function useMessageTTS(): UseMessageTTSReturn {
const sayVoice = useConfigStore((state) => state.sayVoice);
const browserVoice = useConfigStore((state) => state.browserVoice);
const openaiVoice = useConfigStore((state) => state.openaiVoice);
const openaiCompatibleVoice = useConfigStore((state) => state.openaiCompatibleVoice);
const openaiCompatibleUrl = useConfigStore((state) => state.openaiCompatibleUrl);
const openaiCompatibleTtsModel = useConfigStore((state) => state.openaiCompatibleTtsModel);
const summarizeMessageTTS = useConfigStore((state) => state.summarizeMessageTTS);
const summarizeCharacterThreshold = useConfigStore((state) => state.summarizeCharacterThreshold);
const showMessageTTSButtons = useConfigStore((state) => state.showMessageTTSButtons);
const shouldCheckOpenAIAvailability = showMessageTTSButtons && voiceProvider === 'openai';
const isServerProvider = voiceProvider === 'openai' || voiceProvider === 'openai-compatible';
const shouldCheckOpenAIAvailability = showMessageTTSButtons && isServerProvider;
const shouldCheckSayAvailability = showMessageTTSButtons && voiceProvider === 'say';
const { speak: speakServerTTS, stop: stopServerTTS, isAvailable: isServerTTSAvailable } = useServerTTS({
@@ -72,11 +76,18 @@ export function useMessageTTS(): UseMessageTTSReturn {
textToSpeak = sanitizeForTTS(text);
}
if (voiceProvider === 'openai' && isServerTTSAvailable) {
if (isServerProvider && isServerTTSAvailable) {
const voice = voiceProvider === 'openai-compatible' ? openaiCompatibleVoice : openaiVoice;
const baseURL = voiceProvider === 'openai-compatible' ? openaiCompatibleUrl : undefined;
const model = voiceProvider === 'openai-compatible' ? openaiCompatibleTtsModel : undefined;
await speakServerTTS(textToSpeak, {
voice: openaiVoice,
voice,
model,
speed: speechRate,
pitch: speechPitch,
volume: speechVolume,
summarize: false, // We already summarized client-side
baseURL,
onEnd: () => setIsPlaying(false),
onError: () => setIsPlaying(false),
});
@@ -110,12 +121,16 @@ export function useMessageTTS(): UseMessageTTSReturn {
}
}, [
voiceProvider,
isServerProvider,
speechRate,
speechPitch,
speechVolume,
sayVoice,
browserVoice,
openaiVoice,
openaiCompatibleVoice,
openaiCompatibleUrl,
openaiCompatibleTtsModel,
summarizeMessageTTS,
summarizeCharacterThreshold,
isServerTTSAvailable,