From 8d5dcaac11c1cf0dcc3127e75a7a2bc147cf35b3 Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Thu, 18 May 2023 19:29:33 -0700 Subject: [PATCH] Extract ElevenLabs module config --- .../chat/components/message/ChatMessage.tsx | 4 +- src/apps/chat/editors/chat-stream.ts | 8 ++-- src/common/state/store-settings.ts | 23 +---------- src/modules/elevenlabs/ElevenlabsSettings.tsx | 4 +- src/modules/elevenlabs/elevenlabs.client.ts | 7 +++- .../elevenlabs/store-module-elevenlabs.ts | 39 +++++++++++++++++++ 6 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 src/modules/elevenlabs/store-module-elevenlabs.ts diff --git a/src/apps/chat/components/message/ChatMessage.tsx b/src/apps/chat/components/message/ChatMessage.tsx index bd81d5351..87d0cbe89 100644 --- a/src/apps/chat/components/message/ChatMessage.tsx +++ b/src/apps/chat/components/message/ChatMessage.tsx @@ -15,8 +15,8 @@ import ReplayIcon from '@mui/icons-material/Replay'; import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest'; import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; +import { canUseElevenLabs, speakText } from '~/modules/elevenlabs/elevenlabs.client'; import { canUseProdia } from '~/modules/prodia/prodia.client'; -import { requireUserKeyElevenLabs, speakText } from '~/modules/elevenlabs/elevenlabs.client'; import { DMessage } from '~/common/state/store-chats'; import { InlineTextarea } from '~/common/components/InlineTextarea'; @@ -175,7 +175,7 @@ export function ChatMessage(props: { message: DMessage, isBottom: boolean, onMes const renderMarkdown = useSettingsStore(state => state.renderMarkdown) && !fromSystem; const isImaginable = canUseProdia(); const isImaginableEnabled = messageText?.length > 5 && !messageText.startsWith('https://images.prodia.xyz/') && !(messageText.startsWith('/imagine') || messageText.startsWith('/img')); - const isSpeakable = !!useSettingsStore(state => state.elevenLabsVoiceId) || !requireUserKeyElevenLabs; + const isSpeakable = canUseElevenLabs(); const closeOperationsMenu = () => setMenuAnchor(null); diff --git a/src/apps/chat/editors/chat-stream.ts b/src/apps/chat/editors/chat-stream.ts index 2da500dde..3a77484ce 100644 --- a/src/apps/chat/editors/chat-stream.ts +++ b/src/apps/chat/editors/chat-stream.ts @@ -3,14 +3,13 @@ import { SystemPurposeId, SystemPurposes } from '../../../data'; import { DLLMId } from '~/modules/llms/llm.types'; import { OpenAI } from '~/modules/openai/openai.types'; import { autoTitle } from '~/modules/aifn/autotitle/autoTitle'; +import { findOpenAILlmRefOrThrow } from '~/modules/llms/store-llms'; import { speakText } from '~/modules/elevenlabs/elevenlabs.client'; +import { useModuleElevenlabsStore } from '~/modules/elevenlabs/store-module-elevenlabs'; import { createDMessage, DMessage, useChatStore } from '~/common/state/store-chats'; import { useSettingsStore } from '~/common/state/store-settings'; -import { findOpenAILlmRefOrThrow } from '~/modules/llms/store-llms'; - - /** * The main "chat" function. TODO: this is here so we can soon move it to the data model. @@ -71,7 +70,8 @@ async function streamAssistantMessage( ) { const openAILlmId = findOpenAILlmRefOrThrow(llmId); - const { modelTemperature, modelMaxResponseTokens, elevenLabsAutoSpeak } = useSettingsStore.getState(); + const { modelTemperature, modelMaxResponseTokens } = useSettingsStore.getState(); + const { elevenLabsAutoSpeak } = useModuleElevenlabsStore.getState(); const payload: OpenAI.API.Chat.Request = { api: getOpenAISettings(), model: openAILlmId, diff --git a/src/common/state/store-settings.ts b/src/common/state/store-settings.ts index 1464a8700..aa44e0ff3 100644 --- a/src/common/state/store-settings.ts +++ b/src/common/state/store-settings.ts @@ -49,17 +49,6 @@ interface SettingsStore { modelMaxResponseTokens: number; setModelMaxResponseTokens: (modelMaxResponseTokens: number) => void; - // ElevenLabs Text to Speech settings - - elevenLabsApiKey: string; - setElevenLabsApiKey: (apiKey: string) => void; - - elevenLabsVoiceId: string; - setElevenLabsVoiceId: (voiceId: string) => void; - - elevenLabsAutoSpeak: 'off' | 'firstLine'; - setElevenLabsAutoSpeak: (autoSpeak: 'off' | 'firstLine') => void; - // Google Custom Search settings googleCloudApiKey: string; @@ -116,18 +105,8 @@ export const useSettingsStore = create()( modelMaxResponseTokens: 1024, setModelMaxResponseTokens: (modelMaxResponseTokens: number) => set({ modelMaxResponseTokens: modelMaxResponseTokens }), - // ElevenLabs Text to Speech settings - - elevenLabsApiKey: '', - setElevenLabsApiKey: (elevenLabsApiKey: string) => set({ elevenLabsApiKey }), - - elevenLabsVoiceId: '', - setElevenLabsVoiceId: (elevenLabsVoiceId: string) => set({ elevenLabsVoiceId }), - - elevenLabsAutoSpeak: 'firstLine', - setElevenLabsAutoSpeak: (elevenLabsAutoSpeak: 'off' | 'firstLine') => set({ elevenLabsAutoSpeak }), - // Google Custom Search settings + googleCloudApiKey: '', setGoogleCloudApiKey: (googleApiKey: string) => set({ googleCloudApiKey: googleApiKey }), diff --git a/src/modules/elevenlabs/ElevenlabsSettings.tsx b/src/modules/elevenlabs/ElevenlabsSettings.tsx index 966d0d0b8..0205f2cba 100644 --- a/src/modules/elevenlabs/ElevenlabsSettings.tsx +++ b/src/modules/elevenlabs/ElevenlabsSettings.tsx @@ -10,9 +10,9 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Section } from '~/common/components/Section'; import { settingsCol1Width, settingsGap } from '~/common/theme'; -import { useSettingsStore } from '~/common/state/store-settings'; import { isElevenLabsEnabled, requireUserKeyElevenLabs, useElevenLabsVoices } from './elevenlabs.client'; +import { useModuleElevenlabsStore } from './store-module-elevenlabs'; export function ElevenlabsSettings() { @@ -20,7 +20,7 @@ export function ElevenlabsSettings() { const [showApiKeyValue, setShowApiKeyValue] = React.useState(false); // external state - const { apiKey, setApiKey, voiceId, setVoiceId, autoSpeak, setAutoSpeak } = useSettingsStore(state => ({ + const { apiKey, setApiKey, voiceId, setVoiceId, autoSpeak, setAutoSpeak } = useModuleElevenlabsStore(state => ({ apiKey: state.elevenLabsApiKey, setApiKey: state.setElevenLabsApiKey, voiceId: state.elevenLabsVoiceId, setVoiceId: state.setElevenLabsVoiceId, autoSpeak: state.elevenLabsAutoSpeak, setAutoSpeak: state.setElevenLabsAutoSpeak, diff --git a/src/modules/elevenlabs/elevenlabs.client.ts b/src/modules/elevenlabs/elevenlabs.client.ts index 1c8e1fba9..6408a3fe1 100644 --- a/src/modules/elevenlabs/elevenlabs.client.ts +++ b/src/modules/elevenlabs/elevenlabs.client.ts @@ -3,9 +3,13 @@ import { useQuery } from '@tanstack/react-query'; import { useSettingsStore } from '~/common/state/store-settings'; +import { useModuleElevenlabsStore } from './store-module-elevenlabs'; + export const requireUserKeyElevenLabs = !process.env.HAS_SERVER_KEY_ELEVENLABS; +export const canUseElevenLabs = (): boolean => !!useModuleElevenlabsStore.getState().elevenLabsVoiceId || !requireUserKeyElevenLabs; + export const isValidElevenLabsApiKey = (apiKey?: string) => !!apiKey && apiKey.trim()?.length >= 32; export const isElevenLabsEnabled = (apiKey?: string) => apiKey ? isValidElevenLabsApiKey(apiKey) : !requireUserKeyElevenLabs; @@ -14,7 +18,8 @@ export const isElevenLabsEnabled = (apiKey?: string) => apiKey ? isValidElevenLa export async function speakText(text: string) { if (!(text?.trim())) return; - const { elevenLabsApiKey, elevenLabsVoiceId, preferredLanguage } = useSettingsStore.getState(); + const { preferredLanguage } = useSettingsStore.getState(); + const { elevenLabsApiKey, elevenLabsVoiceId } = useModuleElevenlabsStore.getState(); if (!isElevenLabsEnabled(elevenLabsApiKey)) return; try { diff --git a/src/modules/elevenlabs/store-module-elevenlabs.ts b/src/modules/elevenlabs/store-module-elevenlabs.ts new file mode 100644 index 000000000..d211ebc0f --- /dev/null +++ b/src/modules/elevenlabs/store-module-elevenlabs.ts @@ -0,0 +1,39 @@ +import { create } from 'zustand'; +import { persist } from 'zustand/middleware'; + + +interface ModuleElevenlabsStore { + + // ElevenLabs Text to Speech settings + + elevenLabsApiKey: string; + setElevenLabsApiKey: (apiKey: string) => void; + + elevenLabsVoiceId: string; + setElevenLabsVoiceId: (voiceId: string) => void; + + elevenLabsAutoSpeak: 'off' | 'firstLine'; + setElevenLabsAutoSpeak: (autoSpeak: 'off' | 'firstLine') => void; + +} + +export const useModuleElevenlabsStore = create()( + persist( + (set) => ({ + + // ElevenLabs Text to Speech settings + + elevenLabsApiKey: '', + setElevenLabsApiKey: (elevenLabsApiKey: string) => set({ elevenLabsApiKey }), + + elevenLabsVoiceId: '', + setElevenLabsVoiceId: (elevenLabsVoiceId: string) => set({ elevenLabsVoiceId }), + + elevenLabsAutoSpeak: 'firstLine', + setElevenLabsAutoSpeak: (elevenLabsAutoSpeak: 'off' | 'firstLine') => set({ elevenLabsAutoSpeak }), + + }), + { + name: 'app-module-elevenlabs', + }), +); \ No newline at end of file