khoj 1.30.11.dev64__py3-none-any.whl → 1.32.3.dev34__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- khoj/configure.py +4 -2
- khoj/database/adapters/__init__.py +67 -58
- khoj/database/admin.py +9 -9
- khoj/database/migrations/0077_chatmodel_alter_agent_chat_model_and_more.py +62 -0
- khoj/database/migrations/0078_khojuser_email_verification_code_expiry.py +17 -0
- khoj/database/models/__init__.py +9 -8
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/182-8cd8b17d40e6e989.js +20 -0
- khoj/interface/compiled/_next/static/chunks/1915-605f698f2573cfd4.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2117-9886e6a0232dc093.js +2 -0
- khoj/interface/compiled/_next/static/chunks/2581-455000f8aeb08fc3.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3175-b2e522f8ca392f7e.js +3 -0
- khoj/interface/compiled/_next/static/chunks/3727.dcea8f2193111552.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3789-a09e37a819171a9d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4124-0baa32400521e909.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4357-03ea130575287c27.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5243-f7f0a2a6e1ac5d28.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5427-3e7360c8e6ac9728.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{1279-4cb23143aa2c0228.js → 5473-b1cf56dedac6577a.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/5477-c5d7eabee28a789a.js +1 -0
- khoj/interface/compiled/_next/static/chunks/6065-64db9ad305ba0bcd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8667-d3e5bc726e4ff4e3.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9259-27d1ff42af9a43e0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/94ca1967.1d9b42d929a1ee8c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{1210.ef7a0f9a7e43da1d.js → 9597.83583248dfbf6e73.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/964ecbae.51d6faf8801d15e6.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9665-1ab5c8c667b74dca.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/_not-found/{page-cfba071f5a657256.js → page-a834eddae3e235df.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-e00fb81dca656a10.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-ab5ebe4efba9b582.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/{layout-7f1b79a2c67af0b4.js → layout-1fe1537449f43496.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-37d56a7bbfd307df.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-33934fc2d6ae6838.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/page-a0b61f10b0bf6dd5.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/layout-30e7fda7262713ce.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/page-33a3375b1414d1bd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/layout-c02531d586972d7d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/page-bbbfda90fa03c5be.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/layout-d09d6510a45cd4bd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/page-430db6215e48aea2.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-e8e5db7830bf3f47.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-02dc1f2e2a41e522.js +1 -0
- khoj/interface/compiled/_next/static/chunks/d3ac728e-44ebd2a0c99b12a0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{fd9d1056-2e6c8140e79afc3b.js → fd9d1056-4482b99a36fd1673.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/main-app-de1f09df97a3cfc7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/main-db4bfac6b0a8d00b.js +1 -0
- khoj/interface/compiled/_next/static/chunks/pages/{_app-f870474a17b7f2fd.js → _app-3c9ca398d360b709.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/pages/{_error-c66a4e8afc46f17b.js → _error-cf5ca766ac8f493f.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- khoj/interface/compiled/_next/static/chunks/webpack-b0a1b08bb62bdc15.js +1 -0
- khoj/interface/compiled/_next/static/css/0f04760e76bba6c1.css +25 -0
- khoj/interface/compiled/_next/static/css/37a73b87f02df402.css +1 -0
- khoj/interface/compiled/_next/static/css/8e6a3ca11a60b189.css +1 -0
- khoj/interface/compiled/_next/static/css/9c164d9727dd8092.css +1 -0
- khoj/interface/compiled/_next/static/css/c3acbadc30537d04.css +1 -0
- khoj/interface/compiled/_next/static/css/dac88c17aaee5fcf.css +1 -0
- khoj/interface/compiled/_next/static/css/df4b47a2d0d85eae.css +1 -0
- khoj/interface/compiled/_next/static/css/e546bf5cc4914244.css +1 -0
- khoj/interface/compiled/_next/static/mqcIHpVqVWkmBuN0npYHA/_buildManifest.js +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +6 -6
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +7 -7
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +6 -6
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +6 -6
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +6 -6
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +8 -8
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +6 -6
- khoj/interface/email/magic_link.html +36 -13
- khoj/main.py +1 -1
- khoj/migrations/migrate_server_pg.py +7 -7
- khoj/processor/conversation/anthropic/anthropic_chat.py +5 -7
- khoj/processor/conversation/google/gemini_chat.py +5 -7
- khoj/processor/conversation/google/utils.py +0 -1
- khoj/processor/conversation/offline/chat_model.py +15 -14
- khoj/processor/conversation/openai/gpt.py +7 -9
- khoj/processor/conversation/openai/utils.py +31 -17
- khoj/processor/conversation/prompts.py +65 -49
- khoj/processor/conversation/utils.py +46 -44
- khoj/processor/tools/online_search.py +49 -2
- khoj/routers/api.py +22 -27
- khoj/routers/api_agents.py +4 -4
- khoj/routers/api_chat.py +33 -13
- khoj/routers/api_model.py +4 -4
- khoj/routers/auth.py +108 -7
- khoj/routers/email.py +10 -14
- khoj/routers/helpers.py +187 -143
- khoj/routers/web_client.py +1 -1
- khoj/utils/constants.py +1 -1
- khoj/utils/helpers.py +5 -3
- khoj/utils/initialization.py +28 -26
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/METADATA +7 -7
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/RECORD +102 -99
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/WHEEL +1 -1
- khoj/interface/compiled/_next/static/67DcUiU9MqkM1fhksWunh/_buildManifest.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1459.690bf20e7d7b7090.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1603-13cef426e0e650ec.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1970-1b63ac1497b03a10.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2646-92ba433951d02d52.js +0 -20
- khoj/interface/compiled/_next/static/chunks/3072-be830e4f8412b9d2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3463-081c031e873b7966.js +0 -3
- khoj/interface/compiled/_next/static/chunks/3690-51312931ba1eae30.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3717-b46079dbe9f55694.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4504-62ac13e7d94c52f9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4602-460621c3241e0d13.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4752-554a3db270186ce3.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5512-7cc62049bbe60e11.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5538-0ea2d3944ca051e1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7023-e8de2bded4df6539.js +0 -2
- khoj/interface/compiled/_next/static/chunks/7592-a09c39a38e60634b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8423-1dda16bc56236523.js +0 -1
- khoj/interface/compiled/_next/static/chunks/94ca1967.5584df65931cfe83.js +0 -1
- khoj/interface/compiled/_next/static/chunks/964ecbae.ea4eab2a3a835ffe.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-1878cc328ea380bd.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-8eead7920b0ff92a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-b5800b5286306140.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-9219a85f3477e722.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-d7d2ab93e519f0b2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/layout-6310c57b674dd6f5.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/page-3c32ad5472f75965.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-2ca475462c0b2176.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-faa998c71eb7ca8e.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/layout-f285795bc3154b8c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-cbe7f56b1f87d77a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-592e8c470f2c2084.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-cd5757199539bbf2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/d3ac728e-a9e3522eef9b6b28.js +0 -1
- khoj/interface/compiled/_next/static/chunks/main-1ea5c2e0fdef4626.js +0 -1
- khoj/interface/compiled/_next/static/chunks/main-app-6d6ee3495efe03d4.js +0 -1
- khoj/interface/compiled/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/webpack-616f0694bfe6f6c1.js +0 -1
- khoj/interface/compiled/_next/static/css/1f293605f2871853.css +0 -1
- khoj/interface/compiled/_next/static/css/3c34171b174cc381.css +0 -25
- khoj/interface/compiled/_next/static/css/3cf13271869a4aeb.css +0 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
- khoj/interface/compiled/_next/static/css/5a400c87d295e68a.css +0 -1
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.css +0 -1
- khoj/interface/compiled/_next/static/css/9c4221ae0779cc04.css +0 -1
- /khoj/interface/compiled/_next/static/{67DcUiU9MqkM1fhksWunh → mqcIHpVqVWkmBuN0npYHA}/_ssgManifest.js +0 -0
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/entry_points.txt +0 -0
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/licenses/LICENSE +0 -0
khoj/configure.py
CHANGED
@@ -24,6 +24,7 @@ from starlette.concurrency import run_in_threadpool
|
|
24
24
|
from starlette.middleware import Middleware
|
25
25
|
from starlette.middleware.authentication import AuthenticationMiddleware
|
26
26
|
from starlette.middleware.base import BaseHTTPMiddleware
|
27
|
+
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
|
27
28
|
from starlette.middleware.sessions import SessionMiddleware
|
28
29
|
from starlette.requests import HTTPConnection
|
29
30
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
@@ -43,7 +44,6 @@ from khoj.database.adapters import (
|
|
43
44
|
from khoj.database.models import ClientApplication, KhojUser, ProcessLock, Subscription
|
44
45
|
from khoj.processor.embeddings import CrossEncoderModel, EmbeddingsModel
|
45
46
|
from khoj.routers.api_content import configure_content, configure_search
|
46
|
-
from khoj.routers.helpers import update_telemetry_state
|
47
47
|
from khoj.routers.twilio import is_twilio_enabled
|
48
48
|
from khoj.utils import constants, state
|
49
49
|
from khoj.utils.config import SearchType
|
@@ -343,7 +343,7 @@ def configure_routes(app):
|
|
343
343
|
logger.info("📞 Enabled Twilio")
|
344
344
|
|
345
345
|
|
346
|
-
def configure_middleware(app):
|
346
|
+
def configure_middleware(app, ssl_enabled: bool = False):
|
347
347
|
class NextJsMiddleware(Middleware):
|
348
348
|
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
349
349
|
if scope["type"] == "http" and scope["path"].startswith("/_next"):
|
@@ -354,6 +354,8 @@ def configure_middleware(app):
|
|
354
354
|
super().__init__(app)
|
355
355
|
self.app = app
|
356
356
|
|
357
|
+
if ssl_enabled:
|
358
|
+
app.add_middleware(HTTPSRedirectMiddleware)
|
357
359
|
app.add_middleware(AsyncCloseConnectionsMiddleware)
|
358
360
|
app.add_middleware(AuthenticationMiddleware, backend=UserAuthenticationBackend())
|
359
361
|
app.add_middleware(NextJsMiddleware)
|
@@ -36,7 +36,7 @@ from torch import Tensor
|
|
36
36
|
from khoj.database.models import (
|
37
37
|
Agent,
|
38
38
|
AiModelApi,
|
39
|
-
|
39
|
+
ChatModel,
|
40
40
|
ClientApplication,
|
41
41
|
Conversation,
|
42
42
|
Entry,
|
@@ -238,7 +238,9 @@ async def aget_or_create_user_by_email(email: str) -> tuple[KhojUser, bool]:
|
|
238
238
|
await user.asave()
|
239
239
|
|
240
240
|
if user:
|
241
|
-
|
241
|
+
# Generate a secure 6-digit numeric code
|
242
|
+
user.email_verification_code = f"{secrets.randbelow(1000000):06}"
|
243
|
+
user.email_verification_code_expiry = datetime.now(tz=timezone.utc) + timedelta(minutes=5)
|
242
244
|
await user.asave()
|
243
245
|
|
244
246
|
user_subscription = await Subscription.objects.filter(user=user).afirst()
|
@@ -267,16 +269,19 @@ async def astart_trial_subscription(user: KhojUser) -> Subscription:
|
|
267
269
|
return subscription
|
268
270
|
|
269
271
|
|
270
|
-
async def aget_user_validated_by_email_verification_code(code: str) -> KhojUser:
|
271
|
-
user = await KhojUser.objects.filter(email_verification_code=code).afirst()
|
272
|
+
async def aget_user_validated_by_email_verification_code(code: str, email: str) -> tuple[Optional[KhojUser], bool]:
|
273
|
+
user = await KhojUser.objects.filter(email_verification_code=code, email=email).afirst()
|
272
274
|
if not user:
|
273
|
-
return None
|
275
|
+
return None, False
|
276
|
+
|
277
|
+
if user.email_verification_code_expiry < datetime.now(tz=timezone.utc):
|
278
|
+
return user, True
|
274
279
|
|
275
280
|
user.email_verification_code = None
|
276
281
|
user.verified_email = True
|
277
282
|
await user.asave()
|
278
283
|
|
279
|
-
return user
|
284
|
+
return user, False
|
280
285
|
|
281
286
|
|
282
287
|
async def create_user_by_google_token(token: dict) -> KhojUser:
|
@@ -432,10 +437,14 @@ def is_user_subscribed(user: KhojUser) -> bool:
|
|
432
437
|
return subscribed
|
433
438
|
|
434
439
|
|
435
|
-
async def
|
440
|
+
async def aget_user_by_email(email: str) -> KhojUser:
|
436
441
|
return await KhojUser.objects.filter(email=email).afirst()
|
437
442
|
|
438
443
|
|
444
|
+
def get_user_by_email(email: str) -> KhojUser:
|
445
|
+
return KhojUser.objects.filter(email=email).first()
|
446
|
+
|
447
|
+
|
439
448
|
async def aget_user_by_uuid(uuid: str) -> KhojUser:
|
440
449
|
return await KhojUser.objects.filter(uuid=uuid).afirst()
|
441
450
|
|
@@ -736,8 +745,8 @@ class AgentAdapters:
|
|
736
745
|
|
737
746
|
@staticmethod
|
738
747
|
def create_default_agent(user: KhojUser):
|
739
|
-
|
740
|
-
if
|
748
|
+
default_chat_model = ConversationAdapters.get_default_chat_model(user)
|
749
|
+
if default_chat_model is None:
|
741
750
|
logger.info("No default conversation config found, skipping default agent creation")
|
742
751
|
return None
|
743
752
|
default_personality = prompts.personality.format(current_date="placeholder", day_of_week="placeholder")
|
@@ -746,7 +755,7 @@ class AgentAdapters:
|
|
746
755
|
|
747
756
|
if agent:
|
748
757
|
agent.personality = default_personality
|
749
|
-
agent.chat_model =
|
758
|
+
agent.chat_model = default_chat_model
|
750
759
|
agent.slug = AgentAdapters.DEFAULT_AGENT_SLUG
|
751
760
|
agent.name = AgentAdapters.DEFAULT_AGENT_NAME
|
752
761
|
agent.privacy_level = Agent.PrivacyLevel.PUBLIC
|
@@ -760,7 +769,7 @@ class AgentAdapters:
|
|
760
769
|
name=AgentAdapters.DEFAULT_AGENT_NAME,
|
761
770
|
privacy_level=Agent.PrivacyLevel.PUBLIC,
|
762
771
|
managed_by_admin=True,
|
763
|
-
chat_model=
|
772
|
+
chat_model=default_chat_model,
|
764
773
|
personality=default_personality,
|
765
774
|
slug=AgentAdapters.DEFAULT_AGENT_SLUG,
|
766
775
|
)
|
@@ -787,7 +796,7 @@ class AgentAdapters:
|
|
787
796
|
output_modes: List[str],
|
788
797
|
slug: Optional[str] = None,
|
789
798
|
):
|
790
|
-
chat_model_option = await
|
799
|
+
chat_model_option = await ChatModel.objects.filter(name=chat_model).afirst()
|
791
800
|
|
792
801
|
# Slug will be None for new agents, which will trigger a new agent creation with a generated, immutable slug
|
793
802
|
agent, created = await Agent.objects.filter(slug=slug, creator=user).aupdate_or_create(
|
@@ -857,7 +866,7 @@ class ConversationAdapters:
|
|
857
866
|
agent=conversation.agent,
|
858
867
|
conversation_log=conversation.conversation_log,
|
859
868
|
slug=conversation.slug,
|
860
|
-
title=conversation.title,
|
869
|
+
title=conversation.title if conversation.title else conversation.slug,
|
861
870
|
)
|
862
871
|
|
863
872
|
@staticmethod
|
@@ -972,29 +981,29 @@ class ConversationAdapters:
|
|
972
981
|
|
973
982
|
@staticmethod
|
974
983
|
@require_valid_user
|
975
|
-
def
|
976
|
-
return
|
984
|
+
def has_any_chat_model(user: KhojUser):
|
985
|
+
return ChatModel.objects.filter(user=user).exists()
|
977
986
|
|
978
987
|
@staticmethod
|
979
|
-
def
|
980
|
-
return
|
988
|
+
def get_all_chat_models():
|
989
|
+
return ChatModel.objects.all()
|
981
990
|
|
982
991
|
@staticmethod
|
983
|
-
async def
|
984
|
-
return await sync_to_async(list)(
|
992
|
+
async def aget_all_chat_models():
|
993
|
+
return await sync_to_async(list)(ChatModel.objects.prefetch_related("ai_model_api").all())
|
985
994
|
|
986
995
|
@staticmethod
|
987
996
|
def get_vision_enabled_config():
|
988
|
-
|
989
|
-
for config in
|
997
|
+
chat_models = ConversationAdapters.get_all_chat_models()
|
998
|
+
for config in chat_models:
|
990
999
|
if config.vision_enabled:
|
991
1000
|
return config
|
992
1001
|
return None
|
993
1002
|
|
994
1003
|
@staticmethod
|
995
1004
|
async def aget_vision_enabled_config():
|
996
|
-
|
997
|
-
for config in
|
1005
|
+
chat_models = await ConversationAdapters.aget_all_chat_models()
|
1006
|
+
for config in chat_models:
|
998
1007
|
if config.vision_enabled:
|
999
1008
|
return config
|
1000
1009
|
return None
|
@@ -1010,7 +1019,7 @@ class ConversationAdapters:
|
|
1010
1019
|
@staticmethod
|
1011
1020
|
@arequire_valid_user
|
1012
1021
|
async def aset_user_conversation_processor(user: KhojUser, conversation_processor_config_id: int):
|
1013
|
-
config = await
|
1022
|
+
config = await ChatModel.objects.filter(id=conversation_processor_config_id).afirst()
|
1014
1023
|
if not config:
|
1015
1024
|
return None
|
1016
1025
|
new_config = await UserConversationConfig.objects.aupdate_or_create(user=user, defaults={"setting": config})
|
@@ -1026,24 +1035,24 @@ class ConversationAdapters:
|
|
1026
1035
|
return new_config
|
1027
1036
|
|
1028
1037
|
@staticmethod
|
1029
|
-
def
|
1038
|
+
def get_chat_model(user: KhojUser):
|
1030
1039
|
subscribed = is_user_subscribed(user)
|
1031
1040
|
if not subscribed:
|
1032
|
-
return ConversationAdapters.
|
1041
|
+
return ConversationAdapters.get_default_chat_model(user)
|
1033
1042
|
config = UserConversationConfig.objects.filter(user=user).first()
|
1034
1043
|
if config:
|
1035
1044
|
return config.setting
|
1036
|
-
return ConversationAdapters.
|
1045
|
+
return ConversationAdapters.get_advanced_chat_model(user)
|
1037
1046
|
|
1038
1047
|
@staticmethod
|
1039
|
-
async def
|
1048
|
+
async def aget_chat_model(user: KhojUser):
|
1040
1049
|
subscribed = await ais_user_subscribed(user)
|
1041
1050
|
if not subscribed:
|
1042
|
-
return await ConversationAdapters.
|
1051
|
+
return await ConversationAdapters.aget_default_chat_model(user)
|
1043
1052
|
config = await UserConversationConfig.objects.filter(user=user).prefetch_related("setting").afirst()
|
1044
1053
|
if config:
|
1045
1054
|
return config.setting
|
1046
|
-
return ConversationAdapters.
|
1055
|
+
return ConversationAdapters.aget_advanced_chat_model(user)
|
1047
1056
|
|
1048
1057
|
@staticmethod
|
1049
1058
|
async def aget_voice_model_config(user: KhojUser) -> Optional[VoiceModelOption]:
|
@@ -1064,7 +1073,7 @@ class ConversationAdapters:
|
|
1064
1073
|
return VoiceModelOption.objects.first()
|
1065
1074
|
|
1066
1075
|
@staticmethod
|
1067
|
-
def
|
1076
|
+
def get_default_chat_model(user: KhojUser = None):
|
1068
1077
|
"""Get default conversation config. Prefer chat model by server admin > user > first created chat model"""
|
1069
1078
|
# Get the server chat settings
|
1070
1079
|
server_chat_settings = ServerChatSettings.objects.first()
|
@@ -1084,10 +1093,10 @@ class ConversationAdapters:
|
|
1084
1093
|
return user_chat_settings.setting
|
1085
1094
|
|
1086
1095
|
# Get the first chat model if even the user chat settings are not set
|
1087
|
-
return
|
1096
|
+
return ChatModel.objects.filter().first()
|
1088
1097
|
|
1089
1098
|
@staticmethod
|
1090
|
-
async def
|
1099
|
+
async def aget_default_chat_model(user: KhojUser = None):
|
1091
1100
|
"""Get default conversation config. Prefer chat model by server admin > user > first created chat model"""
|
1092
1101
|
# Get the server chat settings
|
1093
1102
|
server_chat_settings: ServerChatSettings = (
|
@@ -1117,17 +1126,17 @@ class ConversationAdapters:
|
|
1117
1126
|
return user_chat_settings.setting
|
1118
1127
|
|
1119
1128
|
# Get the first chat model if even the user chat settings are not set
|
1120
|
-
return await
|
1129
|
+
return await ChatModel.objects.filter().prefetch_related("ai_model_api").afirst()
|
1121
1130
|
|
1122
1131
|
@staticmethod
|
1123
|
-
def
|
1132
|
+
def get_advanced_chat_model(user: KhojUser):
|
1124
1133
|
server_chat_settings = ServerChatSettings.objects.first()
|
1125
1134
|
if server_chat_settings is not None and server_chat_settings.chat_advanced is not None:
|
1126
1135
|
return server_chat_settings.chat_advanced
|
1127
|
-
return ConversationAdapters.
|
1136
|
+
return ConversationAdapters.get_default_chat_model(user)
|
1128
1137
|
|
1129
1138
|
@staticmethod
|
1130
|
-
async def
|
1139
|
+
async def aget_advanced_chat_model(user: KhojUser = None):
|
1131
1140
|
server_chat_settings: ServerChatSettings = (
|
1132
1141
|
await ServerChatSettings.objects.filter()
|
1133
1142
|
.prefetch_related("chat_advanced", "chat_advanced__ai_model_api")
|
@@ -1135,7 +1144,7 @@ class ConversationAdapters:
|
|
1135
1144
|
)
|
1136
1145
|
if server_chat_settings is not None and server_chat_settings.chat_advanced is not None:
|
1137
1146
|
return server_chat_settings.chat_advanced
|
1138
|
-
return await ConversationAdapters.
|
1147
|
+
return await ConversationAdapters.aget_default_chat_model(user)
|
1139
1148
|
|
1140
1149
|
@staticmethod
|
1141
1150
|
async def aget_server_webscraper():
|
@@ -1247,16 +1256,16 @@ class ConversationAdapters:
|
|
1247
1256
|
|
1248
1257
|
@staticmethod
|
1249
1258
|
def get_conversation_processor_options():
|
1250
|
-
return
|
1259
|
+
return ChatModel.objects.all()
|
1251
1260
|
|
1252
1261
|
@staticmethod
|
1253
|
-
def
|
1262
|
+
def set_user_chat_model(user: KhojUser, chat_model: ChatModel):
|
1254
1263
|
user_conversation_config, _ = UserConversationConfig.objects.get_or_create(user=user)
|
1255
|
-
user_conversation_config.setting =
|
1264
|
+
user_conversation_config.setting = chat_model
|
1256
1265
|
user_conversation_config.save()
|
1257
1266
|
|
1258
1267
|
@staticmethod
|
1259
|
-
async def
|
1268
|
+
async def aget_user_chat_model(user: KhojUser):
|
1260
1269
|
config = (
|
1261
1270
|
await UserConversationConfig.objects.filter(user=user).prefetch_related("setting__ai_model_api").afirst()
|
1262
1271
|
)
|
@@ -1288,33 +1297,33 @@ class ConversationAdapters:
|
|
1288
1297
|
return random.sample(all_questions, max_results)
|
1289
1298
|
|
1290
1299
|
@staticmethod
|
1291
|
-
def
|
1300
|
+
def get_valid_chat_model(user: KhojUser, conversation: Conversation):
|
1292
1301
|
agent: Agent = conversation.agent if AgentAdapters.get_default_agent() != conversation.agent else None
|
1293
1302
|
if agent and agent.chat_model:
|
1294
|
-
|
1303
|
+
chat_model = conversation.agent.chat_model
|
1295
1304
|
else:
|
1296
|
-
|
1305
|
+
chat_model = ConversationAdapters.get_chat_model(user)
|
1297
1306
|
|
1298
|
-
if
|
1299
|
-
|
1307
|
+
if chat_model is None:
|
1308
|
+
chat_model = ConversationAdapters.get_default_chat_model()
|
1300
1309
|
|
1301
|
-
if
|
1310
|
+
if chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
1302
1311
|
if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
|
1303
|
-
|
1304
|
-
max_tokens =
|
1305
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
1312
|
+
chat_model_name = chat_model.name
|
1313
|
+
max_tokens = chat_model.max_prompt_size
|
1314
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
1306
1315
|
|
1307
|
-
return
|
1316
|
+
return chat_model
|
1308
1317
|
|
1309
1318
|
if (
|
1310
|
-
|
1319
|
+
chat_model.model_type
|
1311
1320
|
in [
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1321
|
+
ChatModel.ModelType.ANTHROPIC,
|
1322
|
+
ChatModel.ModelType.OPENAI,
|
1323
|
+
ChatModel.ModelType.GOOGLE,
|
1315
1324
|
]
|
1316
|
-
) and
|
1317
|
-
return
|
1325
|
+
) and chat_model.ai_model_api:
|
1326
|
+
return chat_model
|
1318
1327
|
|
1319
1328
|
else:
|
1320
1329
|
raise ValueError("Invalid conversation config - either configure offline chat or openai chat")
|
khoj/database/admin.py
CHANGED
@@ -16,7 +16,7 @@ from unfold import admin as unfold_admin
|
|
16
16
|
from khoj.database.models import (
|
17
17
|
Agent,
|
18
18
|
AiModelApi,
|
19
|
-
|
19
|
+
ChatModel,
|
20
20
|
ClientApplication,
|
21
21
|
Conversation,
|
22
22
|
Entry,
|
@@ -147,7 +147,7 @@ class KhojUserAdmin(UserAdmin, unfold_admin.ModelAdmin):
|
|
147
147
|
if user.email:
|
148
148
|
host = request.get_host()
|
149
149
|
unique_id = user.email_verification_code
|
150
|
-
login_url = f"{host}/auth/magic?code={unique_id}"
|
150
|
+
login_url = f"{host}/auth/magic?code={unique_id}&email={user.email}"
|
151
151
|
messages.info(request, f"Email login URL for {user.email}: {login_url}")
|
152
152
|
|
153
153
|
get_email_login_url.short_description = "Get email login URL" # type: ignore
|
@@ -212,15 +212,15 @@ class KhojUserSubscription(unfold_admin.ModelAdmin):
|
|
212
212
|
list_filter = ("type",)
|
213
213
|
|
214
214
|
|
215
|
-
@admin.register(
|
216
|
-
class
|
215
|
+
@admin.register(ChatModel)
|
216
|
+
class ChatModelAdmin(unfold_admin.ModelAdmin):
|
217
217
|
list_display = (
|
218
218
|
"id",
|
219
|
-
"
|
219
|
+
"name",
|
220
220
|
"ai_model_api",
|
221
221
|
"max_prompt_size",
|
222
222
|
)
|
223
|
-
search_fields = ("id", "
|
223
|
+
search_fields = ("id", "name", "ai_model_api__name")
|
224
224
|
|
225
225
|
|
226
226
|
@admin.register(TextToImageModelConfig)
|
@@ -385,7 +385,7 @@ class UserConversationConfigAdmin(unfold_admin.ModelAdmin):
|
|
385
385
|
"get_chat_model",
|
386
386
|
"get_subscription_type",
|
387
387
|
)
|
388
|
-
search_fields = ("id", "user__email", "
|
388
|
+
search_fields = ("id", "user__email", "setting__name", "user__subscription__type")
|
389
389
|
ordering = ("-updated_at",)
|
390
390
|
|
391
391
|
def get_user_email(self, obj):
|
@@ -395,10 +395,10 @@ class UserConversationConfigAdmin(unfold_admin.ModelAdmin):
|
|
395
395
|
get_user_email.admin_order_field = "user__email" # type: ignore
|
396
396
|
|
397
397
|
def get_chat_model(self, obj):
|
398
|
-
return obj.setting.
|
398
|
+
return obj.setting.name if obj.setting else None
|
399
399
|
|
400
400
|
get_chat_model.short_description = "Chat Model" # type: ignore
|
401
|
-
get_chat_model.admin_order_field = "
|
401
|
+
get_chat_model.admin_order_field = "setting__name" # type: ignore
|
402
402
|
|
403
403
|
def get_subscription_type(self, obj):
|
404
404
|
if hasattr(obj.user, "subscription"):
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Generated by Django 5.0.9 on 2024-12-09 04:21
|
2
|
+
|
3
|
+
import django.db.models.deletion
|
4
|
+
from django.db import migrations, models
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
dependencies = [
|
9
|
+
("database", "0076_rename_openaiprocessorconversationconfig_aimodelapi_and_more"),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.RenameModel(
|
14
|
+
old_name="ChatModelOptions",
|
15
|
+
new_name="ChatModel",
|
16
|
+
),
|
17
|
+
migrations.RenameField(
|
18
|
+
model_name="chatmodel",
|
19
|
+
old_name="chat_model",
|
20
|
+
new_name="name",
|
21
|
+
),
|
22
|
+
migrations.AlterField(
|
23
|
+
model_name="agent",
|
24
|
+
name="chat_model",
|
25
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="database.chatmodel"),
|
26
|
+
),
|
27
|
+
migrations.AlterField(
|
28
|
+
model_name="serverchatsettings",
|
29
|
+
name="chat_advanced",
|
30
|
+
field=models.ForeignKey(
|
31
|
+
blank=True,
|
32
|
+
default=None,
|
33
|
+
null=True,
|
34
|
+
on_delete=django.db.models.deletion.CASCADE,
|
35
|
+
related_name="chat_advanced",
|
36
|
+
to="database.chatmodel",
|
37
|
+
),
|
38
|
+
),
|
39
|
+
migrations.AlterField(
|
40
|
+
model_name="serverchatsettings",
|
41
|
+
name="chat_default",
|
42
|
+
field=models.ForeignKey(
|
43
|
+
blank=True,
|
44
|
+
default=None,
|
45
|
+
null=True,
|
46
|
+
on_delete=django.db.models.deletion.CASCADE,
|
47
|
+
related_name="chat_default",
|
48
|
+
to="database.chatmodel",
|
49
|
+
),
|
50
|
+
),
|
51
|
+
migrations.AlterField(
|
52
|
+
model_name="userconversationconfig",
|
53
|
+
name="setting",
|
54
|
+
field=models.ForeignKey(
|
55
|
+
blank=True,
|
56
|
+
default=None,
|
57
|
+
null=True,
|
58
|
+
on_delete=django.db.models.deletion.CASCADE,
|
59
|
+
to="database.chatmodel",
|
60
|
+
),
|
61
|
+
),
|
62
|
+
]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Generated by Django 5.0.9 on 2024-12-14 18:58
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
dependencies = [
|
8
|
+
("database", "0077_chatmodel_alter_agent_chat_model_and_more"),
|
9
|
+
]
|
10
|
+
|
11
|
+
operations = [
|
12
|
+
migrations.AddField(
|
13
|
+
model_name="khojuser",
|
14
|
+
name="email_verification_code_expiry",
|
15
|
+
field=models.DateTimeField(blank=True, default=None, null=True),
|
16
|
+
),
|
17
|
+
]
|
khoj/database/models/__init__.py
CHANGED
@@ -138,6 +138,7 @@ class KhojUser(AbstractUser):
|
|
138
138
|
verified_phone_number = models.BooleanField(default=False)
|
139
139
|
verified_email = models.BooleanField(default=False)
|
140
140
|
email_verification_code = models.CharField(max_length=200, null=True, default=None, blank=True)
|
141
|
+
email_verification_code_expiry = models.DateTimeField(null=True, default=None, blank=True)
|
141
142
|
|
142
143
|
def save(self, *args, **kwargs):
|
143
144
|
if not self.uuid:
|
@@ -193,7 +194,7 @@ class AiModelApi(DbBaseModel):
|
|
193
194
|
return self.name
|
194
195
|
|
195
196
|
|
196
|
-
class
|
197
|
+
class ChatModel(DbBaseModel):
|
197
198
|
class ModelType(models.TextChoices):
|
198
199
|
OPENAI = "openai"
|
199
200
|
OFFLINE = "offline"
|
@@ -203,13 +204,13 @@ class ChatModelOptions(DbBaseModel):
|
|
203
204
|
max_prompt_size = models.IntegerField(default=None, null=True, blank=True)
|
204
205
|
subscribed_max_prompt_size = models.IntegerField(default=None, null=True, blank=True)
|
205
206
|
tokenizer = models.CharField(max_length=200, default=None, null=True, blank=True)
|
206
|
-
|
207
|
+
name = models.CharField(max_length=200, default="bartowski/Meta-Llama-3.1-8B-Instruct-GGUF")
|
207
208
|
model_type = models.CharField(max_length=200, choices=ModelType.choices, default=ModelType.OFFLINE)
|
208
209
|
vision_enabled = models.BooleanField(default=False)
|
209
210
|
ai_model_api = models.ForeignKey(AiModelApi, on_delete=models.CASCADE, default=None, null=True, blank=True)
|
210
211
|
|
211
212
|
def __str__(self):
|
212
|
-
return self.
|
213
|
+
return self.name
|
213
214
|
|
214
215
|
|
215
216
|
class VoiceModelOption(DbBaseModel):
|
@@ -297,7 +298,7 @@ class Agent(DbBaseModel):
|
|
297
298
|
models.CharField(max_length=200, choices=OutputModeOptions.choices), default=list, null=True, blank=True
|
298
299
|
)
|
299
300
|
managed_by_admin = models.BooleanField(default=False)
|
300
|
-
chat_model = models.ForeignKey(
|
301
|
+
chat_model = models.ForeignKey(ChatModel, on_delete=models.CASCADE)
|
301
302
|
slug = models.CharField(max_length=200, unique=True)
|
302
303
|
style_color = models.CharField(max_length=200, choices=StyleColorTypes.choices, default=StyleColorTypes.BLUE)
|
303
304
|
style_icon = models.CharField(max_length=200, choices=StyleIconTypes.choices, default=StyleIconTypes.LIGHTBULB)
|
@@ -438,10 +439,10 @@ class WebScraper(DbBaseModel):
|
|
438
439
|
|
439
440
|
class ServerChatSettings(DbBaseModel):
|
440
441
|
chat_default = models.ForeignKey(
|
441
|
-
|
442
|
+
ChatModel, on_delete=models.CASCADE, default=None, null=True, blank=True, related_name="chat_default"
|
442
443
|
)
|
443
444
|
chat_advanced = models.ForeignKey(
|
444
|
-
|
445
|
+
ChatModel, on_delete=models.CASCADE, default=None, null=True, blank=True, related_name="chat_advanced"
|
445
446
|
)
|
446
447
|
web_scraper = models.ForeignKey(
|
447
448
|
WebScraper, on_delete=models.CASCADE, default=None, null=True, blank=True, related_name="web_scraper"
|
@@ -563,7 +564,7 @@ class SpeechToTextModelOptions(DbBaseModel):
|
|
563
564
|
|
564
565
|
class UserConversationConfig(DbBaseModel):
|
565
566
|
user = models.OneToOneField(KhojUser, on_delete=models.CASCADE)
|
566
|
-
setting = models.ForeignKey(
|
567
|
+
setting = models.ForeignKey(ChatModel, on_delete=models.CASCADE, default=None, null=True, blank=True)
|
567
568
|
|
568
569
|
|
569
570
|
class UserVoiceModelConfig(DbBaseModel):
|
@@ -638,7 +639,7 @@ def verify_public_conversation(sender, instance, **kwargs):
|
|
638
639
|
|
639
640
|
# check if this is a new instance
|
640
641
|
if instance._state.adding:
|
641
|
-
slug = re.sub(r"\W+", "-", instance.slug.lower())[:50]
|
642
|
+
slug = re.sub(r"\W+", "-", instance.slug.lower())[:50] if instance.slug else uuid.uuid4().hex
|
642
643
|
observed_random_id = set()
|
643
644
|
while PublicConversation.objects.filter(slug=slug).exists():
|
644
645
|
try:
|