model selector and model suggestions
This commit is contained in:
parent
80353a97c9
commit
9ba5709a3c
8 changed files with 182 additions and 115 deletions
|
|
@ -112,8 +112,6 @@ class ObjectModel(BaseModel):
|
||||||
from open_notebook.domain.models import model_manager
|
from open_notebook.domain.models import model_manager
|
||||||
from open_notebook.models import EmbeddingModel
|
from open_notebook.models import EmbeddingModel
|
||||||
|
|
||||||
EMBEDDING_MODEL: EmbeddingModel = model_manager.embedding_model
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.model_validate(self.model_dump(), strict=True)
|
self.model_validate(self.model_dump(), strict=True)
|
||||||
data = self._prepare_save_data()
|
data = self._prepare_save_data()
|
||||||
|
|
@ -122,6 +120,7 @@ class ObjectModel(BaseModel):
|
||||||
if self.needs_embedding():
|
if self.needs_embedding():
|
||||||
embedding_content = self.get_embedding_content()
|
embedding_content = self.get_embedding_content()
|
||||||
if embedding_content:
|
if embedding_content:
|
||||||
|
EMBEDDING_MODEL: EmbeddingModel = model_manager.embedding_model
|
||||||
data["embedding"] = EMBEDDING_MODEL.embed(embedding_content)
|
data["embedding"] = EMBEDDING_MODEL.embed(embedding_content)
|
||||||
|
|
||||||
if self.id is None:
|
if self.id is None:
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,9 @@ class ModelManager:
|
||||||
)
|
)
|
||||||
return cached_model
|
return cached_model
|
||||||
|
|
||||||
assert model_id, "Model ID cannot be empty"
|
if not model_id:
|
||||||
|
return None
|
||||||
|
|
||||||
model: Model = Model.get(model_id)
|
model: Model = Model.get(model_id)
|
||||||
|
|
||||||
if not model:
|
if not model:
|
||||||
|
|
@ -160,9 +162,6 @@ class ModelManager:
|
||||||
elif model_type == "large_context":
|
elif model_type == "large_context":
|
||||||
model_id = self.defaults.large_context_model
|
model_id = self.defaults.large_context_model
|
||||||
|
|
||||||
if not model_id:
|
|
||||||
raise ValueError(f"No default model configured for type: {model_type}")
|
|
||||||
|
|
||||||
return self.get_model(model_id, **kwargs)
|
return self.get_model(model_id, **kwargs)
|
||||||
|
|
||||||
def clear_cache(self):
|
def clear_cache(self):
|
||||||
|
|
|
||||||
|
|
@ -11,3 +11,27 @@ youtube_transcripts:
|
||||||
- de
|
- de
|
||||||
- hi
|
- hi
|
||||||
- ja
|
- ja
|
||||||
|
suggested_models:
|
||||||
|
openai:
|
||||||
|
language:
|
||||||
|
- gpt-4o-mini
|
||||||
|
embedding:
|
||||||
|
- text-embedding-3-small
|
||||||
|
text_to_speech:
|
||||||
|
- tts-1-hd
|
||||||
|
speech_to_text:
|
||||||
|
- whisper-1
|
||||||
|
gemini:
|
||||||
|
language:
|
||||||
|
- gemini-1.5-flash
|
||||||
|
text_to_speech:
|
||||||
|
- default
|
||||||
|
xai:
|
||||||
|
language:
|
||||||
|
- grok-beta
|
||||||
|
anthropic:
|
||||||
|
language:
|
||||||
|
- claude-3-5-sonnet-20241022
|
||||||
|
elevenlabs:
|
||||||
|
text_to_speech:
|
||||||
|
- eleven_turbo_v2_5
|
||||||
|
|
@ -2,9 +2,10 @@ import asyncio
|
||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
from open_notebook.domain.models import Model
|
from open_notebook.domain.models import DefaultModels
|
||||||
from open_notebook.domain.notebook import Note, Notebook, text_search, vector_search
|
from open_notebook.domain.notebook import Note, Notebook, text_search, vector_search
|
||||||
from open_notebook.graphs.ask import graph as ask_graph
|
from open_notebook.graphs.ask import graph as ask_graph
|
||||||
|
from pages.components.model_selector import model_selector
|
||||||
from pages.stream_app.utils import convert_source_references, setup_page
|
from pages.stream_app.utils import convert_source_references, setup_page
|
||||||
|
|
||||||
setup_page("🔍 Search")
|
setup_page("🔍 Search")
|
||||||
|
|
@ -52,23 +53,26 @@ with ask_tab:
|
||||||
"The LLM will answer your query based on the documents in your knowledge base. "
|
"The LLM will answer your query based on the documents in your knowledge base. "
|
||||||
)
|
)
|
||||||
question = st.text_input("Question", "")
|
question = st.text_input("Question", "")
|
||||||
models = Model.get_models_by_type("language")
|
default_model = DefaultModels().load().default_chat_model
|
||||||
strategy_model: Model = st.selectbox(
|
strategy_model = model_selector(
|
||||||
"Query Strategy Model",
|
"Query Strategy Model",
|
||||||
models,
|
"strategy_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_model,
|
||||||
|
model_type="language",
|
||||||
help="This is the LLM that will be responsible for strategizing the search",
|
help="This is the LLM that will be responsible for strategizing the search",
|
||||||
)
|
)
|
||||||
answer_model: Model = st.selectbox(
|
answer_model = model_selector(
|
||||||
"Indivual Answer Model",
|
"Individual Answer Model",
|
||||||
models,
|
"answer_model",
|
||||||
format_func=lambda x: x.name,
|
model_type="language",
|
||||||
|
selected_id=default_model,
|
||||||
help="This is the LLM that will be responsible for processing individual subqueries",
|
help="This is the LLM that will be responsible for processing individual subqueries",
|
||||||
)
|
)
|
||||||
final_answer_model: Model = st.selectbox(
|
final_answer_model = model_selector(
|
||||||
"Final Answer Model",
|
"Final Answer Model",
|
||||||
models,
|
"final_answer_model",
|
||||||
format_func=lambda x: x.name,
|
model_type="language",
|
||||||
|
selected_id=default_model,
|
||||||
help="This is the LLM that will be responsible for processing the final answer",
|
help="This is the LLM that will be responsible for processing the final answer",
|
||||||
)
|
)
|
||||||
ask_bt = st.button("Ask")
|
ask_bt = st.button("Ask")
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ import os
|
||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
|
from open_notebook.config import CONFIG
|
||||||
from open_notebook.domain.models import DefaultModels, Model, model_manager
|
from open_notebook.domain.models import DefaultModels, Model, model_manager
|
||||||
from open_notebook.domain.transformation import DefaultTransformations, Transformation
|
from open_notebook.domain.transformation import DefaultTransformations, Transformation
|
||||||
from open_notebook.models import MODEL_CLASS_MAP
|
from open_notebook.models import MODEL_CLASS_MAP
|
||||||
|
from pages.components.model_selector import model_selector
|
||||||
from pages.stream_app.utils import setup_page
|
from pages.stream_app.utils import setup_page
|
||||||
|
|
||||||
setup_page("⚙️ Settings")
|
setup_page("⚙️ Settings")
|
||||||
|
|
@ -59,8 +61,41 @@ provider_status["litellm"] = (
|
||||||
available_providers = [k for k, v in provider_status.items() if v]
|
available_providers = [k for k, v in provider_status.items() if v]
|
||||||
unavailable_providers = [k for k, v in provider_status.items() if not v]
|
unavailable_providers = [k for k, v in provider_status.items() if not v]
|
||||||
|
|
||||||
|
|
||||||
|
def generate_new_models(models, suggested_models):
|
||||||
|
# Create a set of existing model keys for efficient lookup
|
||||||
|
existing_model_keys = {
|
||||||
|
f"{model.provider}-{model.name}-{model.type}" for model in models
|
||||||
|
}
|
||||||
|
|
||||||
|
new_models = []
|
||||||
|
|
||||||
|
# Iterate through suggested models by provider
|
||||||
|
for provider, types in suggested_models.items():
|
||||||
|
# Iterate through each type (language, embedding, etc.)
|
||||||
|
for type_, model_list in types.items():
|
||||||
|
for model_name in model_list:
|
||||||
|
model_key = f"{provider}-{model_name}-{type_}"
|
||||||
|
|
||||||
|
# Check if model already exists
|
||||||
|
if model_key not in existing_model_keys:
|
||||||
|
new_models.append(
|
||||||
|
{
|
||||||
|
"name": model_name,
|
||||||
|
"type": type_,
|
||||||
|
"provider": provider,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_models
|
||||||
|
|
||||||
|
|
||||||
|
default_models = DefaultModels().model_dump()
|
||||||
|
all_models = Model.get_all()
|
||||||
|
|
||||||
with model_tab:
|
with model_tab:
|
||||||
st.subheader("Add Model")
|
st.subheader("Add Model")
|
||||||
|
|
||||||
provider = st.selectbox("Provider", available_providers)
|
provider = st.selectbox("Provider", available_providers)
|
||||||
if len(unavailable_providers) > 0:
|
if len(unavailable_providers) > 0:
|
||||||
st.caption(
|
st.caption(
|
||||||
|
|
@ -92,8 +127,20 @@ with model_tab:
|
||||||
model = Model(name=model_name, provider=provider, type=model_type)
|
model = Model(name=model_name, provider=provider, type=model_type)
|
||||||
model.save()
|
model.save()
|
||||||
st.success("Saved")
|
st.success("Saved")
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
all_models = Model.get_all()
|
suggested_models = CONFIG.get("suggested_models", [])
|
||||||
|
recommendations = generate_new_models(all_models, suggested_models)
|
||||||
|
if len(recommendations) > 0:
|
||||||
|
with st.expander("💁♂️ Recommended models to get you started.."):
|
||||||
|
for recommendation in recommendations:
|
||||||
|
st.markdown(
|
||||||
|
f"**{recommendation['name']}** ({recommendation['provider']}, {recommendation['type']})"
|
||||||
|
)
|
||||||
|
if st.button("Add", key=f"add_{recommendation['name']}"):
|
||||||
|
new_model = Model(**recommendation)
|
||||||
|
new_model.save()
|
||||||
|
st.rerun()
|
||||||
st.subheader("Configured Models")
|
st.subheader("Configured Models")
|
||||||
model_types_available = {
|
model_types_available = {
|
||||||
# "vision": False,
|
# "vision": False,
|
||||||
|
|
@ -114,20 +161,7 @@ with model_tab:
|
||||||
if not available:
|
if not available:
|
||||||
st.warning(f"No models available for {model_type}")
|
st.warning(f"No models available for {model_type}")
|
||||||
|
|
||||||
|
|
||||||
def get_selected_index(models, model_id, default=0):
|
|
||||||
"""Returns the index of the selected model in the list of models"""
|
|
||||||
if not model_id or not models:
|
|
||||||
return default
|
|
||||||
for i, model in enumerate(models):
|
|
||||||
if model.id == model_id:
|
|
||||||
return i
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
with model_defaults_tab:
|
with model_defaults_tab:
|
||||||
default_models = DefaultModels().model_dump()
|
|
||||||
all_models = Model.get_all()
|
|
||||||
text_generation_models = [model for model in all_models if model.type == "language"]
|
text_generation_models = [model for model in all_models if model.type == "language"]
|
||||||
|
|
||||||
text_to_speech_models = [
|
text_to_speech_models = [
|
||||||
|
|
@ -143,93 +177,80 @@ with model_defaults_tab:
|
||||||
"In this section, you can select the default models to be used on the various content operations done by Open Notebook. Some of these can be overriden in the different modules."
|
"In this section, you can select the default models to be used on the various content operations done by Open Notebook. Some of these can be overriden in the different modules."
|
||||||
)
|
)
|
||||||
defs = {}
|
defs = {}
|
||||||
defs["default_chat_model"] = st.selectbox(
|
defs["default_chat_model"] = model_selector(
|
||||||
"Default Chat Model",
|
"Default Chat Model",
|
||||||
text_generation_models,
|
"default_chat_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_chat_model"),
|
||||||
help="This model will be used for chat.",
|
help="This model will be used for chat.",
|
||||||
index=get_selected_index(
|
model_type="language",
|
||||||
text_generation_models, default_models.get("default_chat_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
st.divider()
|
st.divider()
|
||||||
defs["default_transformation_model"] = st.selectbox(
|
defs["default_transformation_model"] = model_selector(
|
||||||
"Default Transformation Model",
|
"Default Transformation Model",
|
||||||
text_generation_models,
|
"default_transformation_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_transformation_model"),
|
||||||
help="This model will be used for text transformations such as summaries, insights, etc.",
|
help="This model will be used for text transformations such as summaries, insights, etc.",
|
||||||
index=get_selected_index(
|
model_type="language",
|
||||||
text_generation_models, default_models.get("default_transformation_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
st.caption("You can use a cheap model here like gpt-4o-mini, llama3, etc.")
|
||||||
st.divider()
|
st.divider()
|
||||||
defs["default_tools_model"] = st.selectbox(
|
defs["default_tools_model"] = model_selector(
|
||||||
"Default Tools Model",
|
"Default Tools Model",
|
||||||
text_generation_models,
|
"default_tools_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_tools_model"),
|
||||||
help="This model will be used for calling tools. Currently, it's best to use Open AI and Anthropic for this.",
|
help="This model will be used for calling tools. Currently, it's best to use Open AI and Anthropic for this.",
|
||||||
index=get_selected_index(
|
model_type="language",
|
||||||
text_generation_models, default_models.get("default_tools_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
st.caption("Recommended to use a capable model here, like gpt-4o, claude, etc.")
|
||||||
st.divider()
|
st.divider()
|
||||||
defs["large_context_model"] = st.selectbox(
|
defs["large_context_model"] = model_selector(
|
||||||
"Large Context Model",
|
"Large Context Model",
|
||||||
text_generation_models,
|
"large_context_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("large_context_model"),
|
||||||
help="This model will be used for larger context generation -- recommended: Gemini",
|
help="This model will be used for larger context generation -- recommended: Gemini",
|
||||||
index=get_selected_index(
|
model_type="language",
|
||||||
text_generation_models, default_models.get("large_context_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
st.caption("Recommended to use Gemini models for larger context processing")
|
st.caption("Recommended to use Gemini models for larger context processing")
|
||||||
st.divider()
|
st.divider()
|
||||||
defs["default_text_to_speech_model"] = st.selectbox(
|
defs["default_text_to_speech_model"] = model_selector(
|
||||||
"Default Text to Speech Model",
|
"Default Text to Speech Model",
|
||||||
text_to_speech_models,
|
"default_text_to_speech_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_text_to_speech_model"),
|
||||||
help="This is the default model for converting text to speech (podcasts, etc)",
|
help="This is the default model for converting text to speech (podcasts, etc)",
|
||||||
index=get_selected_index(
|
model_type="text_to_speech",
|
||||||
text_to_speech_models, default_models.get("default_text_to_speech_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
st.caption("You can override this model on different podcasts")
|
st.caption("You can override this model on different podcasts")
|
||||||
st.divider()
|
st.divider()
|
||||||
defs["default_speech_to_text_model"] = st.selectbox(
|
defs["default_speech_to_text_model"] = model_selector(
|
||||||
"Default Speech to Text Model",
|
"Default Speech to Text Model",
|
||||||
speech_to_text_models,
|
"default_speech_to_text_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_speech_to_text_model"),
|
||||||
help="This is the default model for converting speech to text (audio transcriptions, etc)",
|
help="This is the default model for converting speech to text (audio transcriptions, etc)",
|
||||||
index=get_selected_index(
|
model_type="speech_to_text",
|
||||||
speech_to_text_models, default_models.get("default_speech_to_text_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
# defs["default_vision_model"] = st.selectbox(
|
# defs["default_vision_model"] = (
|
||||||
# "Default Vision Model",
|
# model_selector(
|
||||||
# vision_models,
|
# "Default Speech to Text Model",
|
||||||
# format_func=lambda x: x.name,
|
# "default_vision_model",
|
||||||
# help="This is the default model for vision tasks (image recognition, PDF recognition, etc)",
|
# selected_id=default_models.get("default_vision_model"),
|
||||||
# index=get_selected_index(
|
# help="This is the default model for vision tasks",
|
||||||
# vision_models, default_models.get("default_vision_model")
|
# model_type="vision",
|
||||||
# ),
|
# ),
|
||||||
# )
|
# )
|
||||||
# st.divider()
|
|
||||||
|
|
||||||
defs["default_embedding_model"] = st.selectbox(
|
defs["default_embedding_model"] = model_selector(
|
||||||
"Default Embedding Model",
|
"Default Speech to Text Model",
|
||||||
embedding_models,
|
"default_embedding_model",
|
||||||
format_func=lambda x: x.name,
|
selected_id=default_models.get("default_embedding_model"),
|
||||||
help="This is the default model for embeddings (semantic search, etc)",
|
help="This is the default model for embeddings (semantic search, etc)",
|
||||||
index=get_selected_index(
|
model_type="embedding",
|
||||||
embedding_models, default_models.get("default_embedding_model")
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
st.caption(
|
st.caption(
|
||||||
"Caution: you cannot change the embedding model once there is embeddings or they will need to be regenerated"
|
"Caution: you cannot change the embedding model once there is embeddings or they will need to be regenerated"
|
||||||
)
|
)
|
||||||
|
|
||||||
# if st.button("Save Defaults", key="save_defaults"):
|
|
||||||
for k, v in defs.items():
|
for k, v in defs.items():
|
||||||
if v:
|
if v:
|
||||||
defs[k] = v.id
|
defs[k] = v.id
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from open_notebook.domain.models import Model
|
|
||||||
from open_notebook.graphs.multipattern import graph as pattern_graph
|
from open_notebook.graphs.multipattern import graph as pattern_graph
|
||||||
|
from pages.components.model_selector import model_selector
|
||||||
from pages.stream_app.utils import setup_page
|
from pages.stream_app.utils import setup_page
|
||||||
|
|
||||||
setup_page("🛝 Playground")
|
setup_page("🛝 Playground")
|
||||||
|
|
@ -22,12 +22,13 @@ transformation = st.selectbox(
|
||||||
with st.expander("Details"):
|
with st.expander("Details"):
|
||||||
st.json(transformation)
|
st.json(transformation)
|
||||||
|
|
||||||
models = Model.get_models_by_type("language")
|
model = model_selector(
|
||||||
model = st.selectbox(
|
|
||||||
"Pick a pattern model",
|
"Pick a pattern model",
|
||||||
models,
|
key="model",
|
||||||
format_func=lambda x: x.name,
|
help="This is the model that will be used to run the transformation",
|
||||||
|
model_type="language",
|
||||||
)
|
)
|
||||||
|
|
||||||
input_text = st.text_area("Enter some text", height=200)
|
input_text = st.text_area("Enter some text", height=200)
|
||||||
|
|
||||||
if st.button("Run"):
|
if st.button("Run"):
|
||||||
|
|
|
||||||
35
pages/components/model_selector.py
Normal file
35
pages/components/model_selector.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
import streamlit as st
|
||||||
|
|
||||||
|
from open_notebook.domain.models import Model
|
||||||
|
|
||||||
|
|
||||||
|
def model_selector(
|
||||||
|
label,
|
||||||
|
key,
|
||||||
|
selected_id=None,
|
||||||
|
help=None,
|
||||||
|
model_type: Literal[
|
||||||
|
"language", "embedding", "speech_to_text", "text_to_speech"
|
||||||
|
] = "language",
|
||||||
|
) -> Model:
|
||||||
|
models = Model.get_models_by_type(model_type)
|
||||||
|
models.sort(key=lambda x: (x.provider, x.name))
|
||||||
|
try:
|
||||||
|
index = (
|
||||||
|
next((i for i, m in enumerate(models) if m.id == selected_id), 0)
|
||||||
|
if selected_id
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
index = 0
|
||||||
|
|
||||||
|
return st.selectbox(
|
||||||
|
label,
|
||||||
|
models,
|
||||||
|
format_func=lambda x: f"{x.provider} - {x.name}",
|
||||||
|
help=help,
|
||||||
|
index=index,
|
||||||
|
key=key,
|
||||||
|
)
|
||||||
|
|
@ -116,34 +116,18 @@ def check_migration():
|
||||||
|
|
||||||
def check_models():
|
def check_models():
|
||||||
default_models = model_manager.defaults
|
default_models = model_manager.defaults
|
||||||
if (
|
if not all(
|
||||||
not default_models.default_chat_model
|
[
|
||||||
or not default_models.default_transformation_model
|
default_models.default_chat_model,
|
||||||
|
default_models.default_transformation_model,
|
||||||
|
default_models.default_embedding_model,
|
||||||
|
default_models.default_speech_to_text_model,
|
||||||
|
default_models.large_context_model,
|
||||||
|
]
|
||||||
):
|
):
|
||||||
st.warning(
|
st.warning(
|
||||||
"You don't have default chat and transformation models selected. Please, select them on the settings page."
|
"You are missing some default models and the app might not work as expected. Please, select them on the settings page."
|
||||||
)
|
)
|
||||||
st.stop()
|
|
||||||
elif not default_models.default_embedding_model:
|
|
||||||
st.warning(
|
|
||||||
"You don't have a default embedding model selected. Vector search will not be possible and your assistant will be less able to answer your queries. Please, select one on the settings page."
|
|
||||||
)
|
|
||||||
st.stop()
|
|
||||||
elif not default_models.default_speech_to_text_model:
|
|
||||||
st.warning(
|
|
||||||
"You don't have a default speech to text model selected. Your assistant will not be able to transcribe audio. Please, select one on the settings page."
|
|
||||||
)
|
|
||||||
st.stop()
|
|
||||||
elif not default_models.default_text_to_speech_model:
|
|
||||||
st.warning(
|
|
||||||
"You don't have a default text to speech model selected. Your assistant will not be able to generate audio and podcasts. Please, select one on the settings page."
|
|
||||||
)
|
|
||||||
st.stop()
|
|
||||||
elif not default_models.large_context_model:
|
|
||||||
st.warning(
|
|
||||||
"You don't have a large context model selected. Your assistant will not be able to process large documents. Please, select one on the settings page."
|
|
||||||
)
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
|
|
||||||
def handle_error(func):
|
def handle_error(func):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue