setup text to speech models in Podcasts
This commit is contained in:
parent
9390ea82f2
commit
68cd7d7dda
6 changed files with 72 additions and 14 deletions
|
|
@ -16,6 +16,13 @@ class Model(ObjectModel):
|
||||||
provider: str
|
provider: str
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_models_by_type(cls, model_type):
|
||||||
|
models = repo_query(
|
||||||
|
"SELECT * FROM model WHERE type=$model_type;", {"model_type": model_type}
|
||||||
|
)
|
||||||
|
return [Model(**model) for model in models]
|
||||||
|
|
||||||
|
|
||||||
class DefaultModels(BaseModel):
|
class DefaultModels(BaseModel):
|
||||||
default_chat_model: Optional[str] = None
|
default_chat_model: Optional[str] = None
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ from open_notebook.models.llms import (
|
||||||
VertexAnthropicLanguageModel,
|
VertexAnthropicLanguageModel,
|
||||||
)
|
)
|
||||||
from open_notebook.models.speech_to_text_models import OpenAISpeechToTextModel
|
from open_notebook.models.speech_to_text_models import OpenAISpeechToTextModel
|
||||||
|
from open_notebook.models.text_to_speech_models import (
|
||||||
|
ElevenLabsTextToSpeechModel,
|
||||||
|
OpenAITextToSpeechModel,
|
||||||
|
)
|
||||||
|
|
||||||
# Unified model class map with type information
|
# Unified model class map with type information
|
||||||
MODEL_CLASS_MAP = {
|
MODEL_CLASS_MAP = {
|
||||||
|
|
@ -38,7 +42,10 @@ MODEL_CLASS_MAP = {
|
||||||
"speech_to_text": {
|
"speech_to_text": {
|
||||||
"openai": OpenAISpeechToTextModel,
|
"openai": OpenAISpeechToTextModel,
|
||||||
},
|
},
|
||||||
"text_to_speech": {"openai": None, "elevenlabs": None},
|
"text_to_speech": {
|
||||||
|
"openai": OpenAITextToSpeechModel,
|
||||||
|
"elevenlabs": ElevenLabsTextToSpeechModel,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
26
open_notebook/models/text_to_speech_models.py
Normal file
26
open_notebook/models/text_to_speech_models.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
"""
|
||||||
|
Classes for supporting different text to speech models
|
||||||
|
"""
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TextToSpeechModel(ABC):
|
||||||
|
"""
|
||||||
|
Abstract base class for text to speech models.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_name: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OpenAITextToSpeechModel(TextToSpeechModel):
|
||||||
|
model_name: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ElevenLabsTextToSpeechModel(TextToSpeechModel):
|
||||||
|
model_name: str
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import ClassVar, List, Literal, Optional
|
from typing import ClassVar, List, Optional
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from podcastfy.client import generate_podcast
|
from podcastfy.client import generate_podcast
|
||||||
|
|
@ -31,7 +31,7 @@ class PodcastConfig(ObjectModel):
|
||||||
ending_message: Optional[str] = None
|
ending_message: Optional[str] = None
|
||||||
wordcount: int = Field(ge=400, le=10000)
|
wordcount: int = Field(ge=400, le=10000)
|
||||||
creativity: float = Field(ge=0, le=1)
|
creativity: float = Field(ge=0, le=1)
|
||||||
provider: Literal["openai", "elevenlabs", "edge"] = Field(default="openai")
|
provider: str = Field(default="openai")
|
||||||
voice1: Optional[str] = None
|
voice1: Optional[str] = None
|
||||||
voice2: Optional[str] = None
|
voice2: Optional[str] = None
|
||||||
model: str
|
model: str
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
from streamlit_tags import st_tags
|
from streamlit_tags import st_tags
|
||||||
|
|
||||||
|
from open_notebook.domain.models import Model
|
||||||
from open_notebook.plugins.podcasts import (
|
from open_notebook.plugins.podcasts import (
|
||||||
PodcastConfig,
|
PodcastConfig,
|
||||||
PodcastEpisode,
|
PodcastEpisode,
|
||||||
|
|
@ -17,6 +20,21 @@ st.set_page_config(
|
||||||
|
|
||||||
version_sidebar()
|
version_sidebar()
|
||||||
|
|
||||||
|
text_to_speech_models = Model.get_models_by_type("text_to_speech")
|
||||||
|
|
||||||
|
|
||||||
|
provider_models: Dict[str, List[str]] = {}
|
||||||
|
|
||||||
|
for model in text_to_speech_models:
|
||||||
|
if model.provider not in provider_models:
|
||||||
|
provider_models[model.provider] = []
|
||||||
|
provider_models[model.provider].append(model.name)
|
||||||
|
|
||||||
|
|
||||||
|
if len(text_to_speech_models) == 0:
|
||||||
|
st.error("No text to speech models found. Please set one up in the Settings page.")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
episodes_tab, templates_tab = st.tabs(["Episodes", "Templates"])
|
episodes_tab, templates_tab = st.tabs(["Episodes", "Templates"])
|
||||||
|
|
||||||
with episodes_tab:
|
with episodes_tab:
|
||||||
|
|
@ -76,7 +94,7 @@ with templates_tab:
|
||||||
pd_cfg["ending_message"] = st.text_input(
|
pd_cfg["ending_message"] = st.text_input(
|
||||||
"Ending Message", placeholder="Thank you for listening!"
|
"Ending Message", placeholder="Thank you for listening!"
|
||||||
)
|
)
|
||||||
pd_cfg["provider"] = st.selectbox("Provider", ["openai", "elevenlabs", "edge"])
|
pd_cfg["provider"] = st.selectbox("Provider", provider_models.keys())
|
||||||
pd_cfg["voice1"] = st.text_input(
|
pd_cfg["voice1"] = st.text_input(
|
||||||
"Voice 1", help="You can use Elevenlabs voice ID"
|
"Voice 1", help="You can use Elevenlabs voice ID"
|
||||||
)
|
)
|
||||||
|
|
@ -86,7 +104,8 @@ with templates_tab:
|
||||||
pd_cfg["voice2"] = st.text_input(
|
pd_cfg["voice2"] = st.text_input(
|
||||||
"Voice 2", help="You can use Elevenlabs voice ID"
|
"Voice 2", help="You can use Elevenlabs voice ID"
|
||||||
)
|
)
|
||||||
pd_cfg["model"] = st.text_input("Model")
|
|
||||||
|
pd_cfg["model"] = st.selectbox("Model", provider_models[pd_cfg["provider"]])
|
||||||
st.caption(
|
st.caption(
|
||||||
"OpenAI: tts-1 or tts-1-hd, Elevenlabs: eleven_multilingual_v2, eleven_turbo_v2_5"
|
"OpenAI: tts-1 or tts-1-hd, Elevenlabs: eleven_multilingual_v2, eleven_turbo_v2_5"
|
||||||
)
|
)
|
||||||
|
|
@ -183,8 +202,8 @@ with templates_tab:
|
||||||
)
|
)
|
||||||
pd_config.provider = st.selectbox(
|
pd_config.provider = st.selectbox(
|
||||||
"Provider",
|
"Provider",
|
||||||
["openai", "elevenlabs", "edge"],
|
list(provider_models.keys()),
|
||||||
index=["openai", "elevenlabs", "edge"].index(pd_config.provider),
|
index=list(provider_models.keys()).index(pd_config.provider),
|
||||||
key=f"provider_{pd_config.id}",
|
key=f"provider_{pd_config.id}",
|
||||||
)
|
)
|
||||||
pd_config.voice1 = st.text_input(
|
pd_config.voice1 = st.text_input(
|
||||||
|
|
@ -202,8 +221,11 @@ with templates_tab:
|
||||||
key=f"voice2_{pd_config.id}",
|
key=f"voice2_{pd_config.id}",
|
||||||
help="You can use Elevenlabs voice ID",
|
help="You can use Elevenlabs voice ID",
|
||||||
)
|
)
|
||||||
pd_config.model = st.text_input(
|
pd_config.model = st.selectbox(
|
||||||
"Model", value=pd_config.model, key=f"model_{pd_config.id}"
|
"Model",
|
||||||
|
provider_models[pd_config.provider],
|
||||||
|
index=provider_models[pd_config.provider].index(pd_config.model),
|
||||||
|
key=f"model_{pd_config.id}",
|
||||||
)
|
)
|
||||||
st.caption(
|
st.caption(
|
||||||
"OpenAI: tts-1 or tts-1-hd, Elevenlabs: eleven_multilingual_v2, eleven_turbo_v2_5"
|
"OpenAI: tts-1 or tts-1-hd, Elevenlabs: eleven_multilingual_v2, eleven_turbo_v2_5"
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ provider_status["openrouter"] = (
|
||||||
and os.environ.get("OPENROUTER_BASE_URL") is not None
|
and os.environ.get("OPENROUTER_BASE_URL") is not None
|
||||||
)
|
)
|
||||||
provider_status["anthropic"] = os.environ.get("ANTHROPIC_API_KEY") is not None
|
provider_status["anthropic"] = os.environ.get("ANTHROPIC_API_KEY") is not None
|
||||||
provider_status["eleven_labs"] = os.environ.get("ELEVENLABS_API_KEY") is not None
|
provider_status["elevenlabs"] = os.environ.get("ELEVENLABS_API_KEY") is not None
|
||||||
provider_status["litellm"] = (
|
provider_status["litellm"] = (
|
||||||
provider_status["ollama"]
|
provider_status["ollama"]
|
||||||
or provider_status["vertexai"]
|
or provider_status["vertexai"]
|
||||||
|
|
@ -104,7 +104,6 @@ with model_tab:
|
||||||
st.warning(f"No models available for {model_type}")
|
st.warning(f"No models available for {model_type}")
|
||||||
|
|
||||||
|
|
||||||
# todo: check for each type of model
|
|
||||||
def get_selected_index(models, model_id, default=0):
|
def get_selected_index(models, model_id, default=0):
|
||||||
"""Returns the index of the selected model in the list of models"""
|
"""Returns the index of the selected model in the list of models"""
|
||||||
if not model_id or not models:
|
if not model_id or not models:
|
||||||
|
|
@ -216,6 +215,3 @@ with model_defaults_tab:
|
||||||
if st.button("Save Defaults", key="save_defaults"):
|
if st.button("Save Defaults", key="save_defaults"):
|
||||||
DefaultModels.update(defs)
|
DefaultModels.update(defs)
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
# todo: return an error if a selected model is no longer supported
|
|
||||||
# todo: do this check on the app homepage as well
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue