diff --git a/src/khoj/database/adapters/__init__.py b/src/khoj/database/adapters/__init__.py index 9ea2e168..ffd24caf 100644 --- a/src/khoj/database/adapters/__init__.py +++ b/src/khoj/database/adapters/__init__.py @@ -1207,6 +1207,14 @@ class ConversationAdapters: return await ChatModel.objects.filter(name=chat_model_name, ai_model_api__name=ai_model_api_name).afirst() return await ChatModel.objects.filter(name=chat_model_name).prefetch_related("ai_model_api").afirst() + @staticmethod + async def aget_chat_model_by_friendly_name(chat_model_name: str, ai_model_api_name: str = None): + if ai_model_api_name: + return await ChatModel.objects.filter( + friendly_name=chat_model_name, ai_model_api__name=ai_model_api_name + ).afirst() + return await ChatModel.objects.filter(friendly_name=chat_model_name).prefetch_related("ai_model_api").afirst() + @staticmethod async def aget_voice_model_config(user: KhojUser) -> Optional[VoiceModelOption]: voice_model_config = await UserVoiceModelConfig.objects.filter(user=user).prefetch_related("setting").afirst() diff --git a/src/khoj/database/admin.py b/src/khoj/database/admin.py index 7297ce11..4d4bc5a0 100644 --- a/src/khoj/database/admin.py +++ b/src/khoj/database/admin.py @@ -232,6 +232,7 @@ class KhojUserSubscription(unfold_admin.ModelAdmin): class ChatModelAdmin(unfold_admin.ModelAdmin): list_display = ( "id", + "friendly_name", "name", "ai_model_api", "max_prompt_size", @@ -243,6 +244,7 @@ class ChatModelAdmin(unfold_admin.ModelAdmin): class TextToImageModelOptionsAdmin(unfold_admin.ModelAdmin): list_display = ( "id", + "friendly_name", "model_name", "model_type", ) diff --git a/src/khoj/database/migrations/0091_chatmodel_friendly_name_and_more.py b/src/khoj/database/migrations/0091_chatmodel_friendly_name_and_more.py new file mode 100644 index 00000000..26a866ba --- /dev/null +++ b/src/khoj/database/migrations/0091_chatmodel_friendly_name_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 5.1.10 on 2025-06-28 06:50 + +from django.db import migrations, models + + +def initialize_friendly_names(apps, schema_editor): + """Initialize friendly_name fields with values from the name/model_name fields""" + ChatModel = apps.get_model("database", "ChatModel") + SpeechToTextModelOptions = apps.get_model("database", "SpeechToTextModelOptions") + TextToImageModelConfig = apps.get_model("database", "TextToImageModelConfig") + + # Initialize ChatModel friendly_name with name field + ChatModel.objects.update(friendly_name=models.F("name")) + + # Initialize SpeechToTextModelOptions friendly_name with model_name field + SpeechToTextModelOptions.objects.update(friendly_name=models.F("model_name")) + + # Initialize TextToImageModelConfig friendly_name with model_name field + TextToImageModelConfig.objects.update(friendly_name=models.F("model_name")) + + +class Migration(migrations.Migration): + dependencies = [ + ("database", "0090_alter_khojuser_uuid"), + ] + + operations = [ + migrations.AddField( + model_name="chatmodel", + name="friendly_name", + field=models.CharField(blank=True, default=None, max_length=200, null=True), + ), + migrations.AddField( + model_name="speechtotextmodeloptions", + name="friendly_name", + field=models.CharField(blank=True, default=None, max_length=200, null=True), + ), + migrations.AddField( + model_name="texttoimagemodelconfig", + name="friendly_name", + field=models.CharField(blank=True, default=None, max_length=200, null=True), + ), + migrations.RunPython(initialize_friendly_names, reverse_code=migrations.RunPython.noop), + ] diff --git a/src/khoj/database/models/__init__.py b/src/khoj/database/models/__init__.py index 2aafadc2..4a952de4 100644 --- a/src/khoj/database/models/__init__.py +++ b/src/khoj/database/models/__init__.py @@ -214,6 +214,7 @@ class ChatModel(DbBaseModel): subscribed_max_prompt_size = models.IntegerField(default=None, null=True, blank=True) tokenizer = models.CharField(max_length=200, default=None, null=True, blank=True) name = models.CharField(max_length=200, default="bartowski/Meta-Llama-3.1-8B-Instruct-GGUF") + friendly_name = models.CharField(max_length=200, default=None, null=True, blank=True) model_type = models.CharField(max_length=200, choices=ModelType.choices, default=ModelType.OFFLINE) price_tier = models.CharField(max_length=20, choices=PriceTier.choices, default=PriceTier.FREE) vision_enabled = models.BooleanField(default=False) @@ -222,7 +223,7 @@ class ChatModel(DbBaseModel): strengths = models.TextField(default=None, null=True, blank=True) def __str__(self): - return self.name + return self.friendly_name class VoiceModelOption(DbBaseModel): @@ -554,6 +555,7 @@ class TextToImageModelConfig(DbBaseModel): GOOGLE = "google" model_name = models.CharField(max_length=200, default="dall-e-3") + friendly_name = models.CharField(max_length=200, default=None, null=True, blank=True) model_type = models.CharField(max_length=200, choices=ModelType.choices, default=ModelType.OPENAI) price_tier = models.CharField(max_length=20, choices=PriceTier.choices, default=PriceTier.FREE) api_key = models.CharField(max_length=200, default=None, null=True, blank=True) @@ -592,6 +594,7 @@ class SpeechToTextModelOptions(DbBaseModel): OFFLINE = "offline" model_name = models.CharField(max_length=200, default="base") + friendly_name = models.CharField(max_length=200, default=None, null=True, blank=True) model_type = models.CharField(max_length=200, choices=ModelType.choices, default=ModelType.OFFLINE) price_tier = models.CharField(max_length=20, choices=PriceTier.choices, default=PriceTier.FREE) ai_model_api = models.ForeignKey(AiModelApi, on_delete=models.CASCADE, default=None, null=True, blank=True) diff --git a/src/khoj/routers/api_agents.py b/src/khoj/routers/api_agents.py index bc9a0adc..53845ae7 100644 --- a/src/khoj/routers/api_agents.py +++ b/src/khoj/routers/api_agents.py @@ -72,7 +72,7 @@ async def all_agents( "color": agent.style_color, "icon": agent.style_icon, "privacy_level": agent.privacy_level, - "chat_model": agent_chat_model.name, + "chat_model": agent_chat_model.friendly_name, "files": file_names, "input_tools": agent.input_tools, "output_modes": agent.output_modes, @@ -134,7 +134,7 @@ async def get_agent_by_conversation( chat_model = await AgentAdapters.aget_agent_chat_model(agent, user) if is_subscribed or chat_model.price_tier == PriceTier.FREE: - agent_chat_model = chat_model.name + agent_chat_model = chat_model.friendly_name else: agent_chat_model = None @@ -219,7 +219,7 @@ async def get_agent( "color": agent.style_color, "icon": agent.style_icon, "privacy_level": agent.privacy_level, - "chat_model": agent.chat_model.name, + "chat_model": agent.chat_model.friendly_name, "files": file_names, "input_tools": agent.input_tools, "output_modes": agent.output_modes, @@ -261,9 +261,9 @@ async def update_hidden_agent( user: KhojUser = request.user.object subscribed = has_required_scope(request, ["premium"]) - chat_model = await ConversationAdapters.aget_chat_model_by_name(body.chat_model) + chat_model = await ConversationAdapters.aget_chat_model_by_friendly_name(body.chat_model) if subscribed or chat_model.price_tier == PriceTier.FREE: - agent_chat_model = body.chat_model + agent_chat_model = chat_model.name else: agent_chat_model = None @@ -292,7 +292,7 @@ async def update_hidden_agent( "name": agent.name, "persona": agent.personality, "creator": agent.creator.username if agent.creator else None, - "chat_model": agent.chat_model.name, + "chat_model": agent.chat_model.friendly_name, "input_tools": agent.input_tools, "output_modes": agent.output_modes, } @@ -311,9 +311,9 @@ async def create_hidden_agent( user: KhojUser = request.user.object subscribed = has_required_scope(request, ["premium"]) - chat_model = await ConversationAdapters.aget_chat_model_by_name(body.chat_model) + chat_model = await ConversationAdapters.aget_chat_model_by_friendly_name(body.chat_model) if subscribed or chat_model.price_tier == PriceTier.FREE: - agent_chat_model = body.chat_model + agent_chat_model = chat_model.name else: agent_chat_model = None @@ -355,7 +355,7 @@ async def create_hidden_agent( "name": agent.name, "persona": agent.personality, "creator": agent.creator.username if agent.creator else None, - "chat_model": agent.chat_model.name, + "chat_model": agent.chat_model.friendly_name, "input_tools": agent.input_tools, "output_modes": agent.output_modes, } @@ -384,9 +384,9 @@ async def create_agent( ) subscribed = has_required_scope(request, ["premium"]) - chat_model = await ConversationAdapters.aget_chat_model_by_name(body.chat_model) + chat_model = await ConversationAdapters.aget_chat_model_by_friendly_name(body.chat_model) if subscribed or chat_model.price_tier == PriceTier.FREE: - agent_chat_model = body.chat_model + agent_chat_model = chat_model.name else: agent_chat_model = None @@ -415,7 +415,7 @@ async def create_agent( "color": agent.style_color, "icon": agent.style_icon, "privacy_level": agent.privacy_level, - "chat_model": agent.chat_model.name, + "chat_model": agent.chat_model.friendly_name, "files": body.files, "input_tools": agent.input_tools, "output_modes": agent.output_modes, @@ -455,9 +455,9 @@ async def update_agent( ) subscribed = has_required_scope(request, ["premium"]) - chat_model = await ConversationAdapters.aget_chat_model_by_name(body.chat_model) + chat_model = await ConversationAdapters.aget_chat_model_by_friendly_name(body.chat_model) if subscribed or chat_model.price_tier == PriceTier.FREE: - agent_chat_model = body.chat_model + agent_chat_model = chat_model.name else: agent_chat_model = None @@ -485,7 +485,7 @@ async def update_agent( "color": agent.style_color, "icon": agent.style_icon, "privacy_level": agent.privacy_level, - "chat_model": agent.chat_model.name, + "chat_model": agent.chat_model.friendly_name, "files": body.files, "input_tools": agent.input_tools, "output_modes": agent.output_modes, diff --git a/src/khoj/routers/api_model.py b/src/khoj/routers/api_model.py index 9ae5a253..1c27f5f3 100644 --- a/src/khoj/routers/api_model.py +++ b/src/khoj/routers/api_model.py @@ -31,7 +31,7 @@ def get_chat_model_options( for chat_model in chat_models: chat_model_options.append( { - "name": chat_model.name, + "name": chat_model.friendly_name, "id": chat_model.id, "strengths": chat_model.strengths, "description": chat_model.description, @@ -54,7 +54,7 @@ def get_user_chat_model( if chat_model is None: chat_model = ConversationAdapters.get_default_chat_model(user) - return Response(status_code=200, content=json.dumps({"id": chat_model.id, "chat_model": chat_model.name})) + return Response(status_code=200, content=json.dumps({"id": chat_model.id, "chat_model": chat_model.friendly_name})) @api_model.post("/chat", status_code=200) diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py index fe6cf1e7..bb75da13 100644 --- a/src/khoj/routers/helpers.py +++ b/src/khoj/routers/helpers.py @@ -2550,7 +2550,7 @@ def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False) for chat_model in chat_models: chat_model_options.append( { - "name": chat_model.name, + "name": chat_model.friendly_name, "id": chat_model.id, "strengths": chat_model.strengths, "description": chat_model.description, @@ -2564,7 +2564,7 @@ def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False) for paint_model in paint_model_options: all_paint_model_options.append( { - "name": paint_model.model_name, + "name": paint_model.friendly_name, "id": paint_model.id, "tier": paint_model.price_tier, }