khoj 1.24.2.dev4__py3-none-any.whl → 1.24.2.dev6__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.
Files changed (85) hide show
  1. khoj/database/adapters/__init__.py +125 -13
  2. khoj/database/migrations/0065_remove_agent_avatar_remove_agent_public_and_more.py +49 -0
  3. khoj/database/migrations/0066_remove_agent_tools_agent_input_tools_and_more.py +69 -0
  4. khoj/database/migrations/0067_alter_agent_style_icon.py +50 -0
  5. khoj/database/models/__init__.py +60 -18
  6. khoj/interface/compiled/404/index.html +1 -1
  7. khoj/interface/compiled/_next/static/chunks/1269-2e52d48e7d0e5c61.js +1 -0
  8. khoj/interface/compiled/_next/static/chunks/1603-67a89278e2c5dbe6.js +1 -0
  9. khoj/interface/compiled/_next/static/chunks/2697-f27cc03429fc6482.js +1 -0
  10. khoj/interface/compiled/_next/static/chunks/3110-ef2cacd1b8d79ad8.js +1 -0
  11. khoj/interface/compiled/_next/static/chunks/4086-2c74808ba38a5a0f.js +1 -0
  12. khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +1 -0
  13. khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +1 -0
  14. khoj/interface/compiled/_next/static/chunks/9178-7dcdc4fb8cc8cc24.js +1 -0
  15. khoj/interface/compiled/_next/static/chunks/9417-1d158bf46d3a0dc9.js +1 -0
  16. khoj/interface/compiled/_next/static/chunks/9479-563e4d61f91d5a7c.js +1 -0
  17. khoj/interface/compiled/_next/static/chunks/app/agents/page-d2fe596a672ca395.js +1 -0
  18. khoj/interface/compiled/_next/static/chunks/app/automations/page-5480731341f34450.js +1 -0
  19. khoj/interface/compiled/_next/static/chunks/app/chat/page-702057ccbcf27881.js +1 -0
  20. khoj/interface/compiled/_next/static/chunks/app/factchecker/page-e7b34316ec6f44de.js +1 -0
  21. khoj/interface/compiled/_next/static/chunks/app/page-421d13f70c505dd9.js +1 -0
  22. khoj/interface/compiled/_next/static/chunks/app/search/page-d56541c746fded7d.js +1 -0
  23. khoj/interface/compiled/_next/static/chunks/app/settings/page-e044a999468a7c5d.js +1 -0
  24. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-fbbd66a4d4633438.js +1 -0
  25. khoj/interface/compiled/_next/static/chunks/{webpack-8cb92a712ecd16e2.js → webpack-9257083ec6042fc1.js} +1 -1
  26. khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +1 -0
  27. khoj/interface/compiled/_next/static/css/60fc94dfe42ddfe9.css +1 -0
  28. khoj/interface/compiled/_next/static/css/e294b18216ce4cd6.css +25 -0
  29. khoj/interface/compiled/agents/index.html +1 -1
  30. khoj/interface/compiled/agents/index.txt +2 -2
  31. khoj/interface/compiled/automations/index.html +1 -1
  32. khoj/interface/compiled/automations/index.txt +2 -2
  33. khoj/interface/compiled/chat/index.html +1 -1
  34. khoj/interface/compiled/chat/index.txt +2 -2
  35. khoj/interface/compiled/factchecker/index.html +1 -1
  36. khoj/interface/compiled/factchecker/index.txt +2 -2
  37. khoj/interface/compiled/index.html +1 -1
  38. khoj/interface/compiled/index.txt +2 -2
  39. khoj/interface/compiled/search/index.html +1 -1
  40. khoj/interface/compiled/search/index.txt +2 -2
  41. khoj/interface/compiled/settings/index.html +1 -1
  42. khoj/interface/compiled/settings/index.txt +2 -2
  43. khoj/interface/compiled/share/chat/index.html +1 -1
  44. khoj/interface/compiled/share/chat/index.txt +2 -2
  45. khoj/processor/conversation/anthropic/anthropic_chat.py +2 -0
  46. khoj/processor/conversation/google/gemini_chat.py +2 -0
  47. khoj/processor/conversation/offline/chat_model.py +3 -1
  48. khoj/processor/conversation/openai/gpt.py +2 -0
  49. khoj/processor/conversation/prompts.py +56 -5
  50. khoj/processor/image/generate.py +3 -1
  51. khoj/processor/tools/online_search.py +9 -7
  52. khoj/routers/api.py +21 -4
  53. khoj/routers/api_agents.py +224 -4
  54. khoj/routers/api_chat.py +24 -11
  55. khoj/routers/helpers.py +100 -13
  56. khoj/search_type/text_search.py +4 -1
  57. khoj/utils/helpers.py +15 -2
  58. {khoj-1.24.2.dev4.dist-info → khoj-1.24.2.dev6.dist-info}/METADATA +1 -8
  59. {khoj-1.24.2.dev4.dist-info → khoj-1.24.2.dev6.dist-info}/RECORD +64 -61
  60. khoj/interface/compiled/_next/static/chunks/1603-3e2e1528e3b6ea1d.js +0 -1
  61. khoj/interface/compiled/_next/static/chunks/2697-a29cb9191a9e339c.js +0 -1
  62. khoj/interface/compiled/_next/static/chunks/6648-ee109f4ea33a74e2.js +0 -1
  63. khoj/interface/compiled/_next/static/chunks/7071-b4711cecca6619a8.js +0 -1
  64. khoj/interface/compiled/_next/static/chunks/743-1a64254447cda71f.js +0 -1
  65. khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +0 -1
  66. khoj/interface/compiled/_next/static/chunks/9162-0be016519a18568b.js +0 -1
  67. khoj/interface/compiled/_next/static/chunks/9178-7e815211edcb3657.js +0 -1
  68. khoj/interface/compiled/_next/static/chunks/9417-5d14ac74aaab2c66.js +0 -1
  69. khoj/interface/compiled/_next/static/chunks/9984-e410179c6fac7cf1.js +0 -1
  70. khoj/interface/compiled/_next/static/chunks/app/agents/page-d302911777a3e027.js +0 -1
  71. khoj/interface/compiled/_next/static/chunks/app/automations/page-0a5de8c254c29a1c.js +0 -1
  72. khoj/interface/compiled/_next/static/chunks/app/chat/page-d96bf6a84bb05290.js +0 -1
  73. khoj/interface/compiled/_next/static/chunks/app/factchecker/page-32e61af29e6b431d.js +0 -1
  74. khoj/interface/compiled/_next/static/chunks/app/page-96cab08c985716f4.js +0 -1
  75. khoj/interface/compiled/_next/static/chunks/app/search/page-b3193d46c65571c5.js +0 -1
  76. khoj/interface/compiled/_next/static/chunks/app/settings/page-0db9b708366606ec.js +0 -1
  77. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-f06ac16cfe5b5a16.js +0 -1
  78. khoj/interface/compiled/_next/static/css/24f141a6e37cd204.css +0 -25
  79. khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +0 -1
  80. khoj/interface/compiled/_next/static/css/b1094827d745306b.css +0 -1
  81. /khoj/interface/compiled/_next/static/{9Px2MOmCuHSTa2RFA3GJa → mRXreP-unBb35nvpcsyA9}/_buildManifest.js +0 -0
  82. /khoj/interface/compiled/_next/static/{9Px2MOmCuHSTa2RFA3GJa → mRXreP-unBb35nvpcsyA9}/_ssgManifest.js +0 -0
  83. {khoj-1.24.2.dev4.dist-info → khoj-1.24.2.dev6.dist-info}/WHEEL +0 -0
  84. {khoj-1.24.2.dev4.dist-info → khoj-1.24.2.dev6.dist-info}/entry_points.txt +0 -0
  85. {khoj-1.24.2.dev4.dist-info → khoj-1.24.2.dev6.dist-info}/licenses/LICENSE +0 -0
@@ -10,6 +10,7 @@ from enum import Enum
10
10
  from typing import Callable, Iterable, List, Optional, Type
11
11
 
12
12
  import cron_descriptor
13
+ import django
13
14
  from apscheduler.job import Job
14
15
  from asgiref.sync import sync_to_async
15
16
  from django.contrib.sessions.backends.db import SessionStore
@@ -551,26 +552,62 @@ class ClientApplicationAdapters:
551
552
 
552
553
  class AgentAdapters:
553
554
  DEFAULT_AGENT_NAME = "Khoj"
554
- DEFAULT_AGENT_AVATAR = "https://assets.khoj.dev/lamp-128.png"
555
555
  DEFAULT_AGENT_SLUG = "khoj"
556
556
 
557
+ @staticmethod
558
+ async def aget_readonly_agent_by_slug(agent_slug: str, user: KhojUser):
559
+ return await Agent.objects.filter(
560
+ (Q(slug__iexact=agent_slug.lower()))
561
+ & (
562
+ Q(privacy_level=Agent.PrivacyLevel.PUBLIC)
563
+ | Q(privacy_level=Agent.PrivacyLevel.PROTECTED)
564
+ | Q(creator=user)
565
+ )
566
+ ).afirst()
567
+
568
+ @staticmethod
569
+ async def adelete_agent_by_slug(agent_slug: str, user: KhojUser):
570
+ agent = await AgentAdapters.aget_agent_by_slug(agent_slug, user)
571
+ if agent:
572
+ await agent.adelete()
573
+ return True
574
+ return False
575
+
557
576
  @staticmethod
558
577
  async def aget_agent_by_slug(agent_slug: str, user: KhojUser):
559
578
  return await Agent.objects.filter(
560
- (Q(slug__iexact=agent_slug.lower())) & (Q(public=True) | Q(creator=user))
579
+ (Q(slug__iexact=agent_slug.lower())) & (Q(privacy_level=Agent.PrivacyLevel.PUBLIC) | Q(creator=user))
580
+ ).afirst()
581
+
582
+ @staticmethod
583
+ async def aget_agent_by_name(agent_name: str, user: KhojUser):
584
+ return await Agent.objects.filter(
585
+ (Q(name__iexact=agent_name.lower())) & (Q(privacy_level=Agent.PrivacyLevel.PUBLIC) | Q(creator=user))
561
586
  ).afirst()
562
587
 
563
588
  @staticmethod
564
589
  def get_agent_by_slug(slug: str, user: KhojUser = None):
565
590
  if user:
566
- return Agent.objects.filter((Q(slug__iexact=slug.lower())) & (Q(public=True) | Q(creator=user))).first()
567
- return Agent.objects.filter(slug__iexact=slug.lower(), public=True).first()
591
+ return Agent.objects.filter(
592
+ (Q(slug__iexact=slug.lower())) & (Q(privacy_level=Agent.PrivacyLevel.PUBLIC) | Q(creator=user))
593
+ ).first()
594
+ return Agent.objects.filter(slug__iexact=slug.lower(), privacy_level=Agent.PrivacyLevel.PUBLIC).first()
568
595
 
569
596
  @staticmethod
570
597
  def get_all_accessible_agents(user: KhojUser = None):
598
+ public_query = Q(privacy_level=Agent.PrivacyLevel.PUBLIC)
571
599
  if user:
572
- return Agent.objects.filter(Q(public=True) | Q(creator=user)).distinct().order_by("created_at")
573
- return Agent.objects.filter(public=True).order_by("created_at")
600
+ return (
601
+ Agent.objects.filter(public_query | Q(creator=user))
602
+ .distinct()
603
+ .order_by("created_at")
604
+ .prefetch_related("creator", "chat_model", "fileobject_set")
605
+ )
606
+ return (
607
+ Agent.objects.filter(public_query)
608
+ .order_by("created_at")
609
+ .prefetch_related("creator", "chat_model", "fileobject_set")
610
+ )
574
611
 
575
612
  @staticmethod
576
613
  async def aget_all_accessible_agents(user: KhojUser = None) -> List[Agent]:
@@ -609,12 +646,11 @@ class AgentAdapters:
609
646
  # The default agent is public and managed by the admin. It's handled a little differently than other agents.
610
647
  agent = Agent.objects.create(
611
648
  name=AgentAdapters.DEFAULT_AGENT_NAME,
612
- public=True,
649
+ privacy_level=Agent.PrivacyLevel.PUBLIC,
613
650
  managed_by_admin=True,
614
651
  chat_model=default_conversation_config,
615
652
  personality=default_personality,
616
653
  tools=["*"],
617
- avatar=AgentAdapters.DEFAULT_AGENT_AVATAR,
618
654
  slug=AgentAdapters.DEFAULT_AGENT_SLUG,
619
655
  )
620
656
  Conversation.objects.filter(agent=None).update(agent=agent)
@@ -625,6 +661,68 @@ class AgentAdapters:
625
661
  async def aget_default_agent():
626
662
  return await Agent.objects.filter(name=AgentAdapters.DEFAULT_AGENT_NAME).afirst()
627
663
 
664
+ @staticmethod
665
+ async def aupdate_agent(
666
+ user: KhojUser,
667
+ name: str,
668
+ personality: str,
669
+ privacy_level: str,
670
+ icon: str,
671
+ color: str,
672
+ chat_model: str,
673
+ files: List[str],
674
+ input_tools: List[str],
675
+ output_modes: List[str],
676
+ ):
677
+ chat_model_option = await ChatModelOptions.objects.filter(chat_model=chat_model).afirst()
678
+
679
+ agent, created = await Agent.objects.filter(name=name, creator=user).aupdate_or_create(
680
+ defaults={
681
+ "name": name,
682
+ "creator": user,
683
+ "personality": personality,
684
+ "privacy_level": privacy_level,
685
+ "style_icon": icon,
686
+ "style_color": color,
687
+ "chat_model": chat_model_option,
688
+ "input_tools": input_tools,
689
+ "output_modes": output_modes,
690
+ }
691
+ )
692
+
693
+ # Delete all existing files and entries
694
+ await FileObject.objects.filter(agent=agent).adelete()
695
+ await Entry.objects.filter(agent=agent).adelete()
696
+
697
+ for file in files:
698
+ reference_file = await FileObject.objects.filter(file_name=file, user=agent.creator).afirst()
699
+ if reference_file:
700
+ await FileObject.objects.acreate(file_name=file, agent=agent, raw_text=reference_file.raw_text)
701
+
702
+ # Duplicate all entries associated with the file
703
+ entries: List[Entry] = []
704
+ async for entry in Entry.objects.filter(file_path=file, user=agent.creator).aiterator():
705
+ entries.append(
706
+ Entry(
707
+ agent=agent,
708
+ embeddings=entry.embeddings,
709
+ raw=entry.raw,
710
+ compiled=entry.compiled,
711
+ heading=entry.heading,
712
+ file_source=entry.file_source,
713
+ file_type=entry.file_type,
714
+ file_path=entry.file_path,
715
+ file_name=entry.file_name,
716
+ url=entry.url,
717
+ hashed_value=entry.hashed_value,
718
+ )
719
+ )
720
+
721
+ # Bulk create entries
722
+ await Entry.objects.abulk_create(entries)
723
+
724
+ return agent
725
+
628
726
 
629
727
  class PublicConversationAdapters:
630
728
  @staticmethod
@@ -1196,6 +1294,10 @@ class EntryAdapters:
1196
1294
  def user_has_entries(user: KhojUser):
1197
1295
  return Entry.objects.filter(user=user).exists()
1198
1296
 
1297
+ @staticmethod
1298
+ def agent_has_entries(agent: Agent):
1299
+ return Entry.objects.filter(agent=agent).exists()
1300
+
1199
1301
  @staticmethod
1200
1302
  async def auser_has_entries(user: KhojUser):
1201
1303
  return await Entry.objects.filter(user=user).aexists()
@@ -1229,15 +1331,19 @@ class EntryAdapters:
1229
1331
  return total_size / 1024 / 1024
1230
1332
 
1231
1333
  @staticmethod
1232
- def apply_filters(user: KhojUser, query: str, file_type_filter: str = None):
1334
+ def apply_filters(user: KhojUser, query: str, file_type_filter: str = None, agent: Agent = None):
1233
1335
  q_filter_terms = Q()
1234
1336
 
1235
1337
  word_filters = EntryAdapters.word_filter.get_filter_terms(query)
1236
1338
  file_filters = EntryAdapters.file_filter.get_filter_terms(query)
1237
1339
  date_filters = EntryAdapters.date_filter.get_query_date_range(query)
1238
1340
 
1341
+ user_or_agent = Q(user=user)
1342
+ if agent != None:
1343
+ user_or_agent |= Q(agent=agent)
1344
+
1239
1345
  if len(word_filters) == 0 and len(file_filters) == 0 and len(date_filters) == 0:
1240
- return Entry.objects.filter(user=user)
1346
+ return Entry.objects.filter(user_or_agent)
1241
1347
 
1242
1348
  for term in word_filters:
1243
1349
  if term.startswith("+"):
@@ -1273,7 +1379,7 @@ class EntryAdapters:
1273
1379
  formatted_max_date = date.fromtimestamp(max_date).strftime("%Y-%m-%d")
1274
1380
  q_filter_terms &= Q(embeddings_dates__date__lte=formatted_max_date)
1275
1381
 
1276
- relevant_entries = Entry.objects.filter(user=user).filter(q_filter_terms)
1382
+ relevant_entries = Entry.objects.filter(user_or_agent).filter(q_filter_terms)
1277
1383
  if file_type_filter:
1278
1384
  relevant_entries = relevant_entries.filter(file_type=file_type_filter)
1279
1385
  return relevant_entries
@@ -1286,9 +1392,15 @@ class EntryAdapters:
1286
1392
  file_type_filter: str = None,
1287
1393
  raw_query: str = None,
1288
1394
  max_distance: float = math.inf,
1395
+ agent: Agent = None,
1289
1396
  ):
1290
- relevant_entries = EntryAdapters.apply_filters(user, raw_query, file_type_filter)
1291
- relevant_entries = relevant_entries.filter(user=user).annotate(
1397
+ user_or_agent = Q(user=user)
1398
+
1399
+ if agent != None:
1400
+ user_or_agent |= Q(agent=agent)
1401
+
1402
+ relevant_entries = EntryAdapters.apply_filters(user, raw_query, file_type_filter, agent)
1403
+ relevant_entries = relevant_entries.filter(user_or_agent).annotate(
1292
1404
  distance=CosineDistance("embeddings", embeddings)
1293
1405
  )
1294
1406
  relevant_entries = relevant_entries.filter(distance__lte=max_distance)
@@ -0,0 +1,49 @@
1
+ # Generated by Django 5.0.8 on 2024-09-18 02:54
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", "0064_remove_conversation_temp_id_alter_conversation_id"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="agent",
15
+ name="avatar",
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name="agent",
19
+ name="public",
20
+ ),
21
+ migrations.AddField(
22
+ model_name="agent",
23
+ name="privacy_level",
24
+ field=models.CharField(
25
+ choices=[("public", "Public"), ("private", "Private"), ("protected", "Protected")],
26
+ default="private",
27
+ max_length=30,
28
+ ),
29
+ ),
30
+ migrations.AddField(
31
+ model_name="entry",
32
+ name="agent",
33
+ field=models.ForeignKey(
34
+ blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to="database.agent"
35
+ ),
36
+ ),
37
+ migrations.AddField(
38
+ model_name="fileobject",
39
+ name="agent",
40
+ field=models.ForeignKey(
41
+ blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to="database.agent"
42
+ ),
43
+ ),
44
+ migrations.AlterField(
45
+ model_name="agent",
46
+ name="slug",
47
+ field=models.CharField(max_length=200, unique=True),
48
+ ),
49
+ ]
@@ -0,0 +1,69 @@
1
+ # Generated by Django 5.0.8 on 2024-10-01 00:42
2
+
3
+ import django.contrib.postgres.fields
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+ dependencies = [
9
+ ("database", "0065_remove_agent_avatar_remove_agent_public_and_more"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="agent",
15
+ name="tools",
16
+ ),
17
+ migrations.AddField(
18
+ model_name="agent",
19
+ name="input_tools",
20
+ field=django.contrib.postgres.fields.ArrayField(
21
+ base_field=models.CharField(
22
+ choices=[
23
+ ("general", "General"),
24
+ ("online", "Online"),
25
+ ("notes", "Notes"),
26
+ ("summarize", "Summarize"),
27
+ ("webpage", "Webpage"),
28
+ ],
29
+ max_length=200,
30
+ ),
31
+ default=list,
32
+ size=None,
33
+ ),
34
+ ),
35
+ migrations.AddField(
36
+ model_name="agent",
37
+ name="output_modes",
38
+ field=django.contrib.postgres.fields.ArrayField(
39
+ base_field=models.CharField(choices=[("text", "Text"), ("image", "Image")], max_length=200),
40
+ default=list,
41
+ size=None,
42
+ ),
43
+ ),
44
+ migrations.AlterField(
45
+ model_name="agent",
46
+ name="style_icon",
47
+ field=models.CharField(
48
+ choices=[
49
+ ("Lightbulb", "Lightbulb"),
50
+ ("Health", "Health"),
51
+ ("Robot", "Robot"),
52
+ ("Aperture", "Aperture"),
53
+ ("GraduationCap", "Graduation Cap"),
54
+ ("Jeep", "Jeep"),
55
+ ("Island", "Island"),
56
+ ("MathOperations", "Math Operations"),
57
+ ("Asclepius", "Asclepius"),
58
+ ("Couch", "Couch"),
59
+ ("Code", "Code"),
60
+ ("Atom", "Atom"),
61
+ ("ClockCounterClockwise", "Clock Counter Clockwise"),
62
+ ("PencilLine", "Pencil Line"),
63
+ ("Chalkboard", "Chalkboard"),
64
+ ],
65
+ default="Lightbulb",
66
+ max_length=200,
67
+ ),
68
+ ),
69
+ ]
@@ -0,0 +1,50 @@
1
+ # Generated by Django 5.0.8 on 2024-10-01 18:42
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+ dependencies = [
8
+ ("database", "0066_remove_agent_tools_agent_input_tools_and_more"),
9
+ ]
10
+
11
+ operations = [
12
+ migrations.AlterField(
13
+ model_name="agent",
14
+ name="style_icon",
15
+ field=models.CharField(
16
+ choices=[
17
+ ("Lightbulb", "Lightbulb"),
18
+ ("Health", "Health"),
19
+ ("Robot", "Robot"),
20
+ ("Aperture", "Aperture"),
21
+ ("GraduationCap", "Graduation Cap"),
22
+ ("Jeep", "Jeep"),
23
+ ("Island", "Island"),
24
+ ("MathOperations", "Math Operations"),
25
+ ("Asclepius", "Asclepius"),
26
+ ("Couch", "Couch"),
27
+ ("Code", "Code"),
28
+ ("Atom", "Atom"),
29
+ ("ClockCounterClockwise", "Clock Counter Clockwise"),
30
+ ("PencilLine", "Pencil Line"),
31
+ ("Chalkboard", "Chalkboard"),
32
+ ("Cigarette", "Cigarette"),
33
+ ("CraneTower", "Crane Tower"),
34
+ ("Heart", "Heart"),
35
+ ("Leaf", "Leaf"),
36
+ ("NewspaperClipping", "Newspaper Clipping"),
37
+ ("OrangeSlice", "Orange Slice"),
38
+ ("SmileyMelting", "Smiley Melting"),
39
+ ("YinYang", "Yin Yang"),
40
+ ("SneakerMove", "Sneaker Move"),
41
+ ("Student", "Student"),
42
+ ("Oven", "Oven"),
43
+ ("Gavel", "Gavel"),
44
+ ("Broadcast", "Broadcast"),
45
+ ],
46
+ default="Lightbulb",
47
+ max_length=200,
48
+ ),
49
+ ),
50
+ ]
@@ -3,6 +3,7 @@ import uuid
3
3
  from random import choice
4
4
 
5
5
  from django.contrib.auth.models import AbstractUser
6
+ from django.contrib.postgres.fields import ArrayField
6
7
  from django.core.exceptions import ValidationError
7
8
  from django.db import models
8
9
  from django.db.models.signals import pre_save
@@ -10,6 +11,8 @@ from django.dispatch import receiver
10
11
  from pgvector.django import VectorField
11
12
  from phonenumber_field.modelfields import PhoneNumberField
12
13
 
14
+ from khoj.utils.helpers import ConversationCommand
15
+
13
16
 
14
17
  class BaseModel(models.Model):
15
18
  created_at = models.DateTimeField(auto_now_add=True)
@@ -125,7 +128,7 @@ class Agent(BaseModel):
125
128
  EMERALD = "emerald"
126
129
 
127
130
  class StyleIconTypes(models.TextChoices):
128
- LIGHBULB = "Lightbulb"
131
+ LIGHTBULB = "Lightbulb"
129
132
  HEALTH = "Health"
130
133
  ROBOT = "Robot"
131
134
  APERTURE = "Aperture"
@@ -140,20 +143,64 @@ class Agent(BaseModel):
140
143
  CLOCK_COUNTER_CLOCKWISE = "ClockCounterClockwise"
141
144
  PENCIL_LINE = "PencilLine"
142
145
  CHALKBOARD = "Chalkboard"
146
+ CIGARETTE = "Cigarette"
147
+ CRANE_TOWER = "CraneTower"
148
+ HEART = "Heart"
149
+ LEAF = "Leaf"
150
+ NEWSPAPER_CLIPPING = "NewspaperClipping"
151
+ ORANGE_SLICE = "OrangeSlice"
152
+ SMILEY_MELTING = "SmileyMelting"
153
+ YIN_YANG = "YinYang"
154
+ SNEAKER_MOVE = "SneakerMove"
155
+ STUDENT = "Student"
156
+ OVEN = "Oven"
157
+ GAVEL = "Gavel"
158
+ BROADCAST = "Broadcast"
159
+
160
+ class PrivacyLevel(models.TextChoices):
161
+ PUBLIC = "public"
162
+ PRIVATE = "private"
163
+ PROTECTED = "protected"
164
+
165
+ class InputToolOptions(models.TextChoices):
166
+ # These map to various ConversationCommand types
167
+ GENERAL = "general"
168
+ ONLINE = "online"
169
+ NOTES = "notes"
170
+ SUMMARIZE = "summarize"
171
+ WEBPAGE = "webpage"
172
+
173
+ class OutputModeOptions(models.TextChoices):
174
+ # These map to various ConversationCommand types
175
+ TEXT = "text"
176
+ IMAGE = "image"
143
177
 
144
178
  creator = models.ForeignKey(
145
179
  KhojUser, on_delete=models.CASCADE, default=None, null=True, blank=True
146
180
  ) # Creator will only be null when the agents are managed by admin
147
181
  name = models.CharField(max_length=200)
148
182
  personality = models.TextField()
149
- avatar = models.URLField(max_length=400, default=None, null=True, blank=True)
150
- tools = models.JSONField(default=list) # List of tools the agent has access to, like online search or notes search
151
- public = models.BooleanField(default=False)
183
+ input_tools = ArrayField(models.CharField(max_length=200, choices=InputToolOptions.choices), default=list)
184
+ output_modes = ArrayField(models.CharField(max_length=200, choices=OutputModeOptions.choices), default=list)
152
185
  managed_by_admin = models.BooleanField(default=False)
153
186
  chat_model = models.ForeignKey(ChatModelOptions, on_delete=models.CASCADE)
154
- slug = models.CharField(max_length=200)
187
+ slug = models.CharField(max_length=200, unique=True)
155
188
  style_color = models.CharField(max_length=200, choices=StyleColorTypes.choices, default=StyleColorTypes.BLUE)
156
- style_icon = models.CharField(max_length=200, choices=StyleIconTypes.choices, default=StyleIconTypes.LIGHBULB)
189
+ style_icon = models.CharField(max_length=200, choices=StyleIconTypes.choices, default=StyleIconTypes.LIGHTBULB)
190
+ privacy_level = models.CharField(max_length=30, choices=PrivacyLevel.choices, default=PrivacyLevel.PRIVATE)
191
+
192
+ def save(self, *args, **kwargs):
193
+ is_new = self._state.adding
194
+
195
+ if self.creator is None:
196
+ self.managed_by_admin = True
197
+
198
+ if is_new:
199
+ random_sequence = "".join(choice("0123456789") for i in range(6))
200
+ slug = f"{self.name.lower().replace(' ', '-')}-{random_sequence}"
201
+ self.slug = slug
202
+
203
+ super().save(*args, **kwargs)
157
204
 
158
205
 
159
206
  class ProcessLock(BaseModel):
@@ -173,22 +220,11 @@ class ProcessLock(BaseModel):
173
220
  def verify_agent(sender, instance, **kwargs):
174
221
  # check if this is a new instance
175
222
  if instance._state.adding:
176
- if Agent.objects.filter(name=instance.name, public=True).exists():
223
+ if Agent.objects.filter(name=instance.name, privacy_level=Agent.PrivacyLevel.PUBLIC).exists():
177
224
  raise ValidationError(f"A public Agent with the name {instance.name} already exists.")
178
225
  if Agent.objects.filter(name=instance.name, creator=instance.creator).exists():
179
226
  raise ValidationError(f"A private Agent with the name {instance.name} already exists.")
180
227
 
181
- slug = instance.name.lower().replace(" ", "-")
182
- observed_random_numbers = set()
183
- while Agent.objects.filter(slug=slug).exists():
184
- try:
185
- random_number = choice([i for i in range(0, 1000) if i not in observed_random_numbers])
186
- except IndexError:
187
- raise ValidationError("Unable to generate a unique slug for the Agent. Please try again later.")
188
- observed_random_numbers.add(random_number)
189
- slug = f"{slug}-{random_number}"
190
- instance.slug = slug
191
-
192
228
 
193
229
  class NotionConfig(BaseModel):
194
230
  token = models.CharField(max_length=200)
@@ -406,6 +442,7 @@ class Entry(BaseModel):
406
442
  GITHUB = "github"
407
443
 
408
444
  user = models.ForeignKey(KhojUser, on_delete=models.CASCADE, default=None, null=True, blank=True)
445
+ agent = models.ForeignKey(Agent, on_delete=models.CASCADE, default=None, null=True, blank=True)
409
446
  embeddings = VectorField(dimensions=None)
410
447
  raw = models.TextField()
411
448
  compiled = models.TextField()
@@ -418,12 +455,17 @@ class Entry(BaseModel):
418
455
  hashed_value = models.CharField(max_length=100)
419
456
  corpus_id = models.UUIDField(default=uuid.uuid4, editable=False)
420
457
 
458
+ def save(self, *args, **kwargs):
459
+ if self.user and self.agent:
460
+ raise ValidationError("An Entry cannot be associated with both a user and an agent.")
461
+
421
462
 
422
463
  class FileObject(BaseModel):
423
464
  # Same as Entry but raw will be a much larger string
424
465
  file_name = models.CharField(max_length=400, default=None, null=True, blank=True)
425
466
  raw_text = models.TextField()
426
467
  user = models.ForeignKey(KhojUser, on_delete=models.CASCADE, default=None, null=True, blank=True)
468
+ agent = models.ForeignKey(Agent, on_delete=models.CASCADE, default=None, null=True, blank=True)
427
469
 
428
470
 
429
471
  class EntryDates(BaseModel):
@@ -1 +1 @@
1
- <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/0e790e04fd40ad16-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/1538cedb321e3a97.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/24f141a6e37cd204.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-8cb92a712ecd16e2.js"/><script src="/_next/static/chunks/fd9d1056-2b978342deb60015.js" async=""></script><script src="/_next/static/chunks/7023-a5bf5744d19b3bd3.js" async=""></script><script src="/_next/static/chunks/main-app-6d6ee3495efe03d4.js" async=""></script><meta name="robots" content="noindex"/><meta http-equiv="Content-Security-Policy" content="default-src &#x27;self&#x27; https://assets.khoj.dev; media-src * blob:; script-src &#x27;self&#x27; https://assets.khoj.dev &#x27;unsafe-inline&#x27; &#x27;unsafe-eval&#x27;; connect-src &#x27;self&#x27; blob: https://ipapi.co/json ws://localhost:42110; style-src &#x27;self&#x27; https://assets.khoj.dev &#x27;unsafe-inline&#x27; https://fonts.googleapis.com; img-src &#x27;self&#x27; data: blob: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com; font-src &#x27;self&#x27; https://assets.khoj.dev https://fonts.gstatic.com; child-src &#x27;none&#x27;; object-src &#x27;none&#x27;;"/><title>404: This page could not be found.</title><title>Khoj AI - Home</title><meta name="description" content="Your Second Brain."/><link rel="manifest" href="/static/khoj.webmanifest" crossorigin="use-credentials"/><meta property="og:title" content="Khoj AI - Home"/><meta property="og:description" content="Your Second Brain."/><meta property="og:url" content="https://app.khoj.dev/"/><meta property="og:site_name" content="Khoj AI"/><meta property="og:image" content="https://assets.khoj.dev/khoj_lantern_256x256.png"/><meta property="og:image:width" content="256"/><meta property="og:image:height" content="256"/><meta property="og:image" content="https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="Khoj AI - Home"/><meta name="twitter:description" content="Your Second Brain."/><meta name="twitter:image" content="https://assets.khoj.dev/khoj_lantern_256x256.png"/><meta name="twitter:image:width" content="256"/><meta name="twitter:image:height" content="256"/><meta name="twitter:image" content="https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png"/><meta name="twitter:image:width" content="1200"/><meta name="twitter:image:height" content="630"/><link rel="icon" href="/static/assets/icons/khoj_lantern.ico"/><link rel="apple-touch-icon" href="/static/assets/icons/khoj_lantern_256x256.png"/><meta name="next-size-adjust"/><script src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" noModule=""></script></head><body class="__className_90df87"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-8cb92a712ecd16e2.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/media/0e790e04fd40ad16-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/_next/static/css/1538cedb321e3a97.css\",\"style\"]\n3:HL[\"/_next/static/css/24f141a6e37cd204.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"4:I[95751,[],\"\"]\n6:I[39275,[],\"\"]\n7:I[61343,[],\"\"]\nd:I[76130,[],\"\"]\n8:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n9:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\na:{\"display\":\"inline-block\"}\nb:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\ne:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L4\",null,{\"buildId\":\"9Px2MOmCuHSTa2RFA3GJa\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\",\"\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L5\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/1538cedb321e3a97.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/24f141a6e37cd204.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"meta\",null,{\"httpEquiv\":\"Content-Security-Policy\",\"content\":\"default-src 'self' https://assets.khoj.dev; media-src * blob:; script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval'; connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110; style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com; font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com; child-src 'none'; object-src 'none';\"}],[\"$\",\"body\",null,{\"className\":\"__className_90df87\",\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$8\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$9\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$a\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$b\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$Lc\"],\"globalErrorComponent\":\"$d\",\"missingSlots\":\"$We\"}]\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"link\",\"4\",{\"rel\":\"manifest\",\"href\":\"/static/khoj.webmanifest\",\"crossOrigin\":\"use-credentials\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:title\",\"content\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:url\",\"content\":\"https://app.khoj.dev/\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:site_name\",\"content\":\"Khoj AI\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:image:width\",\"content\":\"256\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:image:height\",\"content\":\"256\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"256\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"256\"}],[\"$\",\"meta\",\"22\",{\"name\":\"twitter:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png\"}],[\"$\",\"meta\",\"23\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"24\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"25\",{\"rel\":\"icon\",\"href\":\"/static/assets/icons/khoj_lantern.ico\"}],[\"$\",\"link\",\"26\",{\"rel\":\"apple-touch-icon\",\"href\":\"/static/assets/icons/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"27\",{\"name\":\"next-size-adjust\"}]]\n"])</script><script>self.__next_f.push([1,"5:null\n"])</script></body></html>
1
+ <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/0e790e04fd40ad16-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/1538cedb321e3a97.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/e294b18216ce4cd6.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-9257083ec6042fc1.js"/><script src="/_next/static/chunks/fd9d1056-2b978342deb60015.js" async=""></script><script src="/_next/static/chunks/7023-a5bf5744d19b3bd3.js" async=""></script><script src="/_next/static/chunks/main-app-6d6ee3495efe03d4.js" async=""></script><meta name="robots" content="noindex"/><meta http-equiv="Content-Security-Policy" content="default-src &#x27;self&#x27; https://assets.khoj.dev; media-src * blob:; script-src &#x27;self&#x27; https://assets.khoj.dev &#x27;unsafe-inline&#x27; &#x27;unsafe-eval&#x27;; connect-src &#x27;self&#x27; blob: https://ipapi.co/json ws://localhost:42110; style-src &#x27;self&#x27; https://assets.khoj.dev &#x27;unsafe-inline&#x27; https://fonts.googleapis.com; img-src &#x27;self&#x27; data: blob: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com; font-src &#x27;self&#x27; https://assets.khoj.dev https://fonts.gstatic.com; child-src &#x27;none&#x27;; object-src &#x27;none&#x27;;"/><title>404: This page could not be found.</title><title>Khoj AI - Home</title><meta name="description" content="Your Second Brain."/><link rel="manifest" href="/static/khoj.webmanifest" crossorigin="use-credentials"/><meta property="og:title" content="Khoj AI - Home"/><meta property="og:description" content="Your Second Brain."/><meta property="og:url" content="https://app.khoj.dev/"/><meta property="og:site_name" content="Khoj AI"/><meta property="og:image" content="https://assets.khoj.dev/khoj_lantern_256x256.png"/><meta property="og:image:width" content="256"/><meta property="og:image:height" content="256"/><meta property="og:image" content="https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png"/><meta property="og:image:width" content="1200"/><meta property="og:image:height" content="630"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="Khoj AI - Home"/><meta name="twitter:description" content="Your Second Brain."/><meta name="twitter:image" content="https://assets.khoj.dev/khoj_lantern_256x256.png"/><meta name="twitter:image:width" content="256"/><meta name="twitter:image:height" content="256"/><meta name="twitter:image" content="https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png"/><meta name="twitter:image:width" content="1200"/><meta name="twitter:image:height" content="630"/><link rel="icon" href="/static/assets/icons/khoj_lantern.ico"/><link rel="apple-touch-icon" href="/static/assets/icons/khoj_lantern_256x256.png"/><meta name="next-size-adjust"/><script src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" noModule=""></script></head><body class="__className_90df87"><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}</style><h1 class="next-error-h1" style="display:inline-block;margin:0 20px 0 0;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block"><h2 style="font-size:14px;font-weight:400;line-height:49px;margin:0">This page could not be found.</h2></div></div></div><script src="/_next/static/chunks/webpack-9257083ec6042fc1.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/media/0e790e04fd40ad16-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/_next/static/css/1538cedb321e3a97.css\",\"style\"]\n3:HL[\"/_next/static/css/e294b18216ce4cd6.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"4:I[95751,[],\"\"]\n6:I[39275,[],\"\"]\n7:I[61343,[],\"\"]\nd:I[76130,[],\"\"]\n8:{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"}\n9:{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"}\na:{\"display\":\"inline-block\"}\nb:{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0}\ne:[]\n"])</script><script>self.__next_f.push([1,"0:[\"$\",\"$L4\",null,{\"buildId\":\"mRXreP-unBb35nvpcsyA9\",\"assetPrefix\":\"\",\"urlParts\":[\"\",\"_not-found\",\"\"],\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L5\",[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],null],null],null]},[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]],null]},[[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/1538cedb321e3a97.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/e294b18216ce4cd6.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"meta\",null,{\"httpEquiv\":\"Content-Security-Policy\",\"content\":\"default-src 'self' https://assets.khoj.dev; media-src * blob:; script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval'; connect-src 'self' blob: https://ipapi.co/json ws://localhost:42110; style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com; font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com; child-src 'none'; object-src 'none';\"}],[\"$\",\"body\",null,{\"className\":\"__className_90df87\",\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":\"$8\",\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":\"$9\",\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":\"$a\",\"children\":[\"$\",\"h2\",null,{\"style\":\"$b\",\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]}]]}]],null],null],\"couldBeIntercepted\":false,\"initialHead\":[[\"$\",\"meta\",null,{\"name\":\"robots\",\"content\":\"noindex\"}],\"$Lc\"],\"globalErrorComponent\":\"$d\",\"missingSlots\":\"$We\"}]\n"])</script><script>self.__next_f.push([1,"c:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"link\",\"4\",{\"rel\":\"manifest\",\"href\":\"/static/khoj.webmanifest\",\"crossOrigin\":\"use-credentials\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:title\",\"content\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:url\",\"content\":\"https://app.khoj.dev/\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:site_name\",\"content\":\"Khoj AI\"}],[\"$\",\"meta\",\"9\",{\"property\":\"og:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"10\",{\"property\":\"og:image:width\",\"content\":\"256\"}],[\"$\",\"meta\",\"11\",{\"property\":\"og:image:height\",\"content\":\"256\"}],[\"$\",\"meta\",\"12\",{\"property\":\"og:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png\"}],[\"$\",\"meta\",\"13\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"14\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"15\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"16\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"17\",{\"name\":\"twitter:title\",\"content\":\"Khoj AI - Home\"}],[\"$\",\"meta\",\"18\",{\"name\":\"twitter:description\",\"content\":\"Your Second Brain.\"}],[\"$\",\"meta\",\"19\",{\"name\":\"twitter:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"20\",{\"name\":\"twitter:image:width\",\"content\":\"256\"}],[\"$\",\"meta\",\"21\",{\"name\":\"twitter:image:height\",\"content\":\"256\"}],[\"$\",\"meta\",\"22\",{\"name\":\"twitter:image\",\"content\":\"https://assets.khoj.dev/khoj_lantern_logomarktype_1200x630.png\"}],[\"$\",\"meta\",\"23\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"24\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"25\",{\"rel\":\"icon\",\"href\":\"/static/assets/icons/khoj_lantern.ico\"}],[\"$\",\"link\",\"26\",{\"rel\":\"apple-touch-icon\",\"href\":\"/static/assets/icons/khoj_lantern_256x256.png\"}],[\"$\",\"meta\",\"27\",{\"name\":\"next-size-adjust\"}]]\n"])</script><script>self.__next_f.push([1,"5:null\n"])</script></body></html>