khoj 2.0.0b13.dev5__py3-none-any.whl → 2.0.0b13.dev23__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 (174) hide show
  1. khoj/app/README.md +1 -1
  2. khoj/app/urls.py +1 -0
  3. khoj/database/adapters/__init__.py +4 -4
  4. khoj/database/management/commands/delete_orphaned_fileobjects.py +0 -1
  5. khoj/database/migrations/0064_remove_conversation_temp_id_alter_conversation_id.py +1 -1
  6. khoj/database/migrations/0075_migrate_generated_assets_and_validate.py +1 -1
  7. khoj/database/models/__init__.py +6 -6
  8. khoj/database/tests.py +0 -2
  9. khoj/interface/compiled/404/index.html +2 -2
  10. khoj/interface/compiled/_next/static/chunks/{9245.a04e92d034540234.js → 1225.ecac11e7421504c4.js} +3 -3
  11. khoj/interface/compiled/_next/static/chunks/1320.ae930ad00affe685.js +5 -0
  12. khoj/interface/compiled/_next/static/chunks/{1327-3b1a41af530fa8ee.js → 1327-e254819a9172cfa7.js} +1 -1
  13. khoj/interface/compiled/_next/static/chunks/1626.15a8acc0d6639ec6.js +1 -0
  14. khoj/interface/compiled/_next/static/chunks/{3489.c523fe96a2eee74f.js → 1940.d082758bd04e08ae.js} +1 -1
  15. khoj/interface/compiled/_next/static/chunks/{2327-ea623ca2d22f78e9.js → 2327-438aaec1657c5ada.js} +1 -1
  16. khoj/interface/compiled/_next/static/chunks/2475.57a0d0fd93d07af0.js +93 -0
  17. khoj/interface/compiled/_next/static/chunks/2481.5ce6524ba0a73f90.js +55 -0
  18. khoj/interface/compiled/_next/static/chunks/297.4c4c823ff6e3255b.js +174 -0
  19. khoj/interface/compiled/_next/static/chunks/{5639-09e2009a2adedf8b.js → 3260-82d2521fab032ff1.js} +68 -23
  20. khoj/interface/compiled/_next/static/chunks/3353.1c6d553216a1acae.js +1 -0
  21. khoj/interface/compiled/_next/static/chunks/3855.f7b8131f78af046e.js +1 -0
  22. khoj/interface/compiled/_next/static/chunks/3973.dc54a39586ab48be.js +1 -0
  23. khoj/interface/compiled/_next/static/chunks/4241.c1cd170f7f37ac59.js +24 -0
  24. khoj/interface/compiled/_next/static/chunks/{4327.8d2a1b8f1ea78208.js → 4327.f3704dc398c67113.js} +19 -19
  25. khoj/interface/compiled/_next/static/chunks/4505.f09454a346269c3f.js +117 -0
  26. khoj/interface/compiled/_next/static/chunks/4801.96a152d49742b644.js +1 -0
  27. khoj/interface/compiled/_next/static/chunks/5427-a95ec748e52abb75.js +1 -0
  28. khoj/interface/compiled/_next/static/chunks/549.2bd27f59a91a9668.js +148 -0
  29. khoj/interface/compiled/_next/static/chunks/5765.71b1e1207b76b03f.js +1 -0
  30. khoj/interface/compiled/_next/static/chunks/584.d7ce3505f169b706.js +1 -0
  31. khoj/interface/compiled/_next/static/chunks/6240.34f7c1fa692edd61.js +24 -0
  32. khoj/interface/compiled/_next/static/chunks/6d3fe5a5-f9f3c16e0bc0cdf9.js +10 -0
  33. khoj/interface/compiled/_next/static/chunks/{7127-0f4a2a77d97fb5fa.js → 7127-97b83757db125ba6.js} +1 -1
  34. khoj/interface/compiled/_next/static/chunks/7200-93ab0072359b8028.js +1 -0
  35. khoj/interface/compiled/_next/static/chunks/{2612.bcf5a623b3da209e.js → 7553.f5ad54b1f6e92c49.js} +2 -2
  36. khoj/interface/compiled/_next/static/chunks/7626-1b630f1654172341.js +1 -0
  37. khoj/interface/compiled/_next/static/chunks/764.dadd316e8e16d191.js +63 -0
  38. khoj/interface/compiled/_next/static/chunks/78.08169ab541abab4f.js +43 -0
  39. khoj/interface/compiled/_next/static/chunks/784.e03acf460df213d1.js +1 -0
  40. khoj/interface/compiled/_next/static/chunks/{9537-d9ab442ce15d1e20.js → 8072-e1440cb482a0940e.js} +1 -1
  41. khoj/interface/compiled/_next/static/chunks/{3265.924139c4146ee344.js → 8086.8d39887215807fcd.js} +1 -1
  42. khoj/interface/compiled/_next/static/chunks/8168.f074ab8c7c16d82d.js +59 -0
  43. khoj/interface/compiled/_next/static/chunks/{8694.2bd9c2f65d8c5847.js → 8223.1705878fa7a09292.js} +1 -1
  44. khoj/interface/compiled/_next/static/chunks/8483.94f6c9e2bee86f50.js +215 -0
  45. khoj/interface/compiled/_next/static/chunks/{8888.ebe0e552b59e7fed.js → 8810.fc0e479de78c7c61.js} +1 -1
  46. khoj/interface/compiled/_next/static/chunks/8828.bc74dc4ce94e78f6.js +1 -0
  47. khoj/interface/compiled/_next/static/chunks/{7303.d0612f812a967a08.js → 8909.14ac3f43d0070cf1.js} +5 -5
  48. khoj/interface/compiled/_next/static/chunks/90542734.b1a1629065ba199b.js +1 -0
  49. khoj/interface/compiled/_next/static/chunks/9167.098534184f03fe92.js +56 -0
  50. khoj/interface/compiled/_next/static/chunks/{4980.63500d68b3bb1222.js → 9537.e934ce37bf314509.js} +5 -5
  51. khoj/interface/compiled/_next/static/chunks/9574.3fe8e26e95bf1c34.js +1 -0
  52. khoj/interface/compiled/_next/static/chunks/9599.ec50b5296c27dae9.js +1 -0
  53. khoj/interface/compiled/_next/static/chunks/9643.b34248df52ffc77c.js +262 -0
  54. khoj/interface/compiled/_next/static/chunks/9747.2fd9065b1435abb1.js +1 -0
  55. khoj/interface/compiled/_next/static/chunks/9922.98f2b2a9959b4ebe.js +1 -0
  56. khoj/interface/compiled/_next/static/chunks/app/agents/layout-e00fb81dca656a10.js +1 -0
  57. khoj/interface/compiled/_next/static/chunks/app/agents/page-e291b49977f43880.js +1 -0
  58. khoj/interface/compiled/_next/static/chunks/app/automations/page-198b26df6e09bbb0.js +1 -0
  59. khoj/interface/compiled/_next/static/chunks/app/chat/layout-33934fc2d6ae6838.js +1 -0
  60. khoj/interface/compiled/_next/static/chunks/app/chat/{page-8e1c4f2af3c9429e.js → page-dfcc1e8e2ad62873.js} +1 -1
  61. khoj/interface/compiled/_next/static/chunks/app/{page-2b3056cba8aa96ce.js → page-1567cac7b79a7c59.js} +1 -1
  62. khoj/interface/compiled/_next/static/chunks/app/settings/{page-8be3b35178abf2ec.js → page-6081362437c82470.js} +1 -1
  63. khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-4a4b0c0f4749c2b2.js → page-e0dcb1762f8c8f88.js} +1 -1
  64. khoj/interface/compiled/_next/static/chunks/webpack-5393aad3d824e0cb.js +1 -0
  65. khoj/interface/compiled/_next/static/css/{2945c4a857922f3b.css → c34713c98384ee87.css} +1 -1
  66. khoj/interface/compiled/agents/index.html +2 -2
  67. khoj/interface/compiled/agents/index.txt +3 -3
  68. khoj/interface/compiled/automations/index.html +2 -2
  69. khoj/interface/compiled/automations/index.txt +4 -4
  70. khoj/interface/compiled/chat/index.html +2 -2
  71. khoj/interface/compiled/chat/index.txt +3 -3
  72. khoj/interface/compiled/index.html +2 -2
  73. khoj/interface/compiled/index.txt +3 -3
  74. khoj/interface/compiled/search/index.html +2 -2
  75. khoj/interface/compiled/search/index.txt +3 -3
  76. khoj/interface/compiled/settings/index.html +2 -2
  77. khoj/interface/compiled/settings/index.txt +5 -5
  78. khoj/interface/compiled/share/chat/index.html +2 -2
  79. khoj/interface/compiled/share/chat/index.txt +3 -3
  80. khoj/main.py +3 -3
  81. khoj/manage.py +1 -0
  82. khoj/processor/content/github/github_to_entries.py +6 -6
  83. khoj/processor/content/images/image_to_entries.py +0 -1
  84. khoj/processor/content/markdown/markdown_to_entries.py +2 -3
  85. khoj/processor/content/notion/notion_to_entries.py +5 -5
  86. khoj/processor/content/org_mode/org_to_entries.py +4 -5
  87. khoj/processor/content/org_mode/orgnode.py +4 -4
  88. khoj/processor/content/plaintext/plaintext_to_entries.py +1 -2
  89. khoj/processor/content/text_to_entries.py +1 -2
  90. khoj/processor/conversation/google/utils.py +3 -3
  91. khoj/processor/conversation/openai/gpt.py +65 -28
  92. khoj/processor/conversation/openai/utils.py +358 -22
  93. khoj/processor/conversation/prompts.py +11 -5
  94. khoj/processor/conversation/utils.py +20 -11
  95. khoj/processor/embeddings.py +0 -2
  96. khoj/processor/image/generate.py +3 -3
  97. khoj/processor/operator/__init__.py +2 -2
  98. khoj/processor/operator/grounding_agent.py +15 -2
  99. khoj/processor/operator/grounding_agent_uitars.py +34 -23
  100. khoj/processor/operator/operator_agent_anthropic.py +29 -4
  101. khoj/processor/operator/operator_agent_base.py +1 -1
  102. khoj/processor/operator/operator_agent_binary.py +4 -4
  103. khoj/processor/operator/operator_agent_openai.py +21 -6
  104. khoj/processor/operator/operator_environment_browser.py +1 -1
  105. khoj/processor/operator/operator_environment_computer.py +1 -1
  106. khoj/processor/speech/text_to_speech.py +0 -1
  107. khoj/processor/tools/online_search.py +1 -1
  108. khoj/processor/tools/run_code.py +1 -1
  109. khoj/routers/api.py +1 -2
  110. khoj/routers/api_agents.py +1 -2
  111. khoj/routers/api_automation.py +1 -1
  112. khoj/routers/api_chat.py +10 -16
  113. khoj/routers/api_model.py +0 -1
  114. khoj/routers/api_subscription.py +1 -1
  115. khoj/routers/email.py +4 -4
  116. khoj/routers/helpers.py +35 -24
  117. khoj/routers/research.py +2 -4
  118. khoj/search_filter/base_filter.py +2 -4
  119. khoj/search_type/text_search.py +1 -2
  120. khoj/utils/constants.py +3 -0
  121. khoj/utils/helpers.py +4 -4
  122. khoj/utils/initialization.py +1 -3
  123. khoj/utils/models.py +2 -4
  124. khoj/utils/rawconfig.py +1 -2
  125. khoj/utils/state.py +1 -1
  126. {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/METADATA +3 -2
  127. {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/RECORD +139 -137
  128. khoj/interface/compiled/_next/static/chunks/1191.b547ec13349b4aed.js +0 -1
  129. khoj/interface/compiled/_next/static/chunks/1588.f0558a0bdffc4761.js +0 -117
  130. khoj/interface/compiled/_next/static/chunks/1918.925cb4a35518d258.js +0 -43
  131. khoj/interface/compiled/_next/static/chunks/2849.dc00ae5ba7219cfc.js +0 -1
  132. khoj/interface/compiled/_next/static/chunks/303.fe76de943e930fbd.js +0 -1
  133. khoj/interface/compiled/_next/static/chunks/4533.586e74b45a2bde25.js +0 -55
  134. khoj/interface/compiled/_next/static/chunks/4551.82ce1476b5516bc2.js +0 -5
  135. khoj/interface/compiled/_next/static/chunks/4748.0edd37cba3ea2809.js +0 -59
  136. khoj/interface/compiled/_next/static/chunks/5210.cd35a1c1ec594a20.js +0 -93
  137. khoj/interface/compiled/_next/static/chunks/5329.f8b3c5b3d16159cd.js +0 -1
  138. khoj/interface/compiled/_next/static/chunks/5427-13d6ffd380fdfab7.js +0 -1
  139. khoj/interface/compiled/_next/static/chunks/558-c14e76cff03f6a60.js +0 -1
  140. khoj/interface/compiled/_next/static/chunks/5830.8876eccb82da9b7d.js +0 -262
  141. khoj/interface/compiled/_next/static/chunks/6230.88a71d8145347b3f.js +0 -1
  142. khoj/interface/compiled/_next/static/chunks/7161.77e0530a40ad5ca8.js +0 -1
  143. khoj/interface/compiled/_next/static/chunks/7200-ac3b2e37ff30e126.js +0 -1
  144. khoj/interface/compiled/_next/static/chunks/7505.c31027a3695bdebb.js +0 -148
  145. khoj/interface/compiled/_next/static/chunks/7760.35649cc21d9585bd.js +0 -56
  146. khoj/interface/compiled/_next/static/chunks/83.48e2db193a940052.js +0 -1
  147. khoj/interface/compiled/_next/static/chunks/8427.844694e06133fb51.js +0 -1
  148. khoj/interface/compiled/_next/static/chunks/8665.4db7e6b2e8933497.js +0 -174
  149. khoj/interface/compiled/_next/static/chunks/872.caf84cc1a39ae59f.js +0 -1
  150. khoj/interface/compiled/_next/static/chunks/8890.6e8a59e4de6978bc.js +0 -215
  151. khoj/interface/compiled/_next/static/chunks/8950.5f2272e0ac923f9e.js +0 -1
  152. khoj/interface/compiled/_next/static/chunks/90542734.2c21f16f18b22411.js +0 -1
  153. khoj/interface/compiled/_next/static/chunks/9202.c703864fcedc8d1f.js +0 -63
  154. khoj/interface/compiled/_next/static/chunks/9320.6aca4885d541aa44.js +0 -24
  155. khoj/interface/compiled/_next/static/chunks/9535.f78cd92d03331e55.js +0 -1
  156. khoj/interface/compiled/_next/static/chunks/9968.b111fc002796da81.js +0 -1
  157. khoj/interface/compiled/_next/static/chunks/app/agents/layout-4e2a134ec26aa606.js +0 -1
  158. khoj/interface/compiled/_next/static/chunks/app/agents/page-9a4610474cd59a71.js +0 -1
  159. khoj/interface/compiled/_next/static/chunks/app/automations/page-f7bb9d777b7745d4.js +0 -1
  160. khoj/interface/compiled/_next/static/chunks/app/chat/layout-ad4d1792ab1a4108.js +0 -1
  161. khoj/interface/compiled/_next/static/chunks/f3e3247b-1758d4651e4457c2.js +0 -10
  162. khoj/interface/compiled/_next/static/chunks/webpack-ee14d29b64c5ab47.js +0 -1
  163. /khoj/interface/compiled/_next/static/{XfWrWDAk5VXeZ88OdP652 → Q7tm150g44Fs4H1CGytNf}/_buildManifest.js +0 -0
  164. /khoj/interface/compiled/_next/static/{XfWrWDAk5VXeZ88OdP652 → Q7tm150g44Fs4H1CGytNf}/_ssgManifest.js +0 -0
  165. /khoj/interface/compiled/_next/static/chunks/{1915-fbfe167c84ad60c5.js → 1915-5c6508f6ebb62a30.js} +0 -0
  166. /khoj/interface/compiled/_next/static/chunks/{2117-e78b6902ad6f75ec.js → 2117-080746c8e170c81a.js} +0 -0
  167. /khoj/interface/compiled/_next/static/chunks/{2939-4d4084c5b888b960.js → 2939-4af3fd24b8ffc9ad.js} +0 -0
  168. /khoj/interface/compiled/_next/static/chunks/{4447-d6cf93724d57e34b.js → 4447-cd95608f8e93e711.js} +0 -0
  169. /khoj/interface/compiled/_next/static/chunks/{8667-4b7790573b08c50d.js → 8667-50b03a89e82e0ba7.js} +0 -0
  170. /khoj/interface/compiled/_next/static/chunks/{9139-ce1ae935dac9c871.js → 9139-8ac4d9feb10f8869.js} +0 -0
  171. /khoj/interface/compiled/_next/static/chunks/app/search/{page-4885df3cd175c957.js → page-3639e50ec3e9acfd.js} +0 -0
  172. {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/WHEEL +0 -0
  173. {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/entry_points.txt +0 -0
  174. {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/licenses/LICENSE +0 -0
khoj/routers/api_chat.py CHANGED
@@ -10,7 +10,6 @@ from functools import partial
10
10
  from typing import Any, Dict, List, Optional
11
11
  from urllib.parse import unquote
12
12
 
13
- from asgiref.sync import sync_to_async
14
13
  from fastapi import (
15
14
  APIRouter,
16
15
  Depends,
@@ -32,10 +31,10 @@ from khoj.database.adapters import (
32
31
  PublicConversationAdapters,
33
32
  aget_user_name,
34
33
  )
35
- from khoj.database.models import Agent, ChatMessageModel, KhojUser
34
+ from khoj.database.models import Agent, KhojUser
36
35
  from khoj.processor.conversation import prompts
37
36
  from khoj.processor.conversation.openai.utils import is_local_api
38
- from khoj.processor.conversation.prompts import help_message, no_entries_found
37
+ from khoj.processor.conversation.prompts import no_entries_found
39
38
  from khoj.processor.conversation.utils import (
40
39
  OperatorRun,
41
40
  ResponseWithThought,
@@ -65,11 +64,8 @@ from khoj.routers.helpers import (
65
64
  acreate_title_from_history,
66
65
  agenerate_chat_response,
67
66
  aget_data_sources_and_output_format,
68
- construct_automation_created_message,
69
- create_automation,
70
67
  gather_raw_query_files,
71
68
  generate_mermaidjs_diagram,
72
- generate_summary_from_files,
73
69
  get_conversation_command,
74
70
  get_message_from_queue,
75
71
  is_query_empty,
@@ -89,13 +85,11 @@ from khoj.utils.helpers import (
89
85
  convert_image_to_webp,
90
86
  get_country_code_from_timezone,
91
87
  get_country_name_from_timezone,
92
- get_device,
93
88
  is_env_var_true,
94
89
  is_none_or_empty,
95
90
  is_operator_enabled,
96
91
  )
97
92
  from khoj.utils.rawconfig import (
98
- ChatRequestBody,
99
93
  FileAttachment,
100
94
  FileFilterRequest,
101
95
  FilesFilterRequest,
@@ -689,7 +683,6 @@ async def event_generator(
689
683
  region = body.region
690
684
  country = body.country or get_country_name_from_timezone(body.timezone)
691
685
  country_code = body.country_code or get_country_code_from_timezone(body.timezone)
692
- timezone = body.timezone
693
686
  raw_images = body.images
694
687
  raw_query_files = body.files
695
688
 
@@ -853,7 +846,8 @@ async def event_generator(
853
846
  if (
854
847
  len(train_of_thought) > 0
855
848
  and train_of_thought[-1]["type"] == ChatEvent.THOUGHT.value
856
- and type(train_of_thought[-1]["data"]) == type(data) == str
849
+ and isinstance(train_of_thought[-1]["data"], str)
850
+ and isinstance(data, str)
857
851
  ):
858
852
  train_of_thought[-1]["data"] += data
859
853
  else:
@@ -1075,11 +1069,11 @@ async def event_generator(
1075
1069
 
1076
1070
  # researched_results = await extract_relevant_info(q, researched_results, agent)
1077
1071
  if state.verbose > 1:
1078
- logger.debug(f'Researched Results: {"".join(r.summarizedResult or "" for r in research_results)}')
1072
+ logger.debug(f"Researched Results: {''.join(r.summarizedResult or '' for r in research_results)}")
1079
1073
 
1080
1074
  # Gather Context
1081
1075
  ## Extract Document References
1082
- if not ConversationCommand.Research in conversation_commands:
1076
+ if ConversationCommand.Research not in conversation_commands:
1083
1077
  try:
1084
1078
  async for result in search_documents(
1085
1079
  q,
@@ -1218,7 +1212,7 @@ async def event_generator(
1218
1212
  else:
1219
1213
  code_results = result
1220
1214
  except ValueError as e:
1221
- program_execution_context.append(f"Failed to run code")
1215
+ program_execution_context.append("Failed to run code")
1222
1216
  logger.warning(
1223
1217
  f"Failed to use code tool: {e}. Attempting to respond without code results",
1224
1218
  exc_info=True,
@@ -1297,7 +1291,7 @@ async def event_generator(
1297
1291
  inferred_queries.append(improved_image_prompt)
1298
1292
  if generated_image is None or status_code != 200:
1299
1293
  program_execution_context.append(f"Failed to generate image with {improved_image_prompt}")
1300
- async for result in send_event(ChatEvent.STATUS, f"Failed to generate image"):
1294
+ async for result in send_event(ChatEvent.STATUS, "Failed to generate image"):
1301
1295
  yield result
1302
1296
  else:
1303
1297
  generated_images.append(generated_image)
@@ -1315,7 +1309,7 @@ async def event_generator(
1315
1309
  yield result
1316
1310
 
1317
1311
  if ConversationCommand.Diagram in conversation_commands:
1318
- async for result in send_event(ChatEvent.STATUS, f"Creating diagram"):
1312
+ async for result in send_event(ChatEvent.STATUS, "Creating diagram"):
1319
1313
  yield result
1320
1314
 
1321
1315
  inferred_queries = []
@@ -1372,7 +1366,7 @@ async def event_generator(
1372
1366
  return
1373
1367
 
1374
1368
  ## Generate Text Output
1375
- async for result in send_event(ChatEvent.STATUS, f"**Generating a well-informed response**"):
1369
+ async for result in send_event(ChatEvent.STATUS, "**Generating a well-informed response**"):
1376
1370
  yield result
1377
1371
 
1378
1372
  llm_response, chat_metadata = await agenerate_chat_response(
khoj/routers/api_model.py CHANGED
@@ -3,7 +3,6 @@ import logging
3
3
  from typing import Dict, Optional, Union
4
4
 
5
5
  from fastapi import APIRouter, Request
6
- from fastapi.requests import Request
7
6
  from fastapi.responses import Response
8
7
  from starlette.authentication import has_required_scope, requires
9
8
 
@@ -117,7 +117,7 @@ async def subscribe(request: Request):
117
117
  )
118
118
  logger.log(logging.INFO, f"🥳 New User Created: {user.user.uuid}")
119
119
 
120
- logger.info(f'Stripe subscription {event["type"]} for {customer_email}')
120
+ logger.info(f"Stripe subscription {event['type']} for {customer_email}")
121
121
  return {"success": success}
122
122
 
123
123
 
khoj/routers/email.py CHANGED
@@ -44,7 +44,7 @@ async def send_magic_link_email(email, unique_id, host):
44
44
  {
45
45
  "sender": os.environ.get("RESEND_EMAIL", "noreply@khoj.dev"),
46
46
  "to": email,
47
- "subject": f"Your login code to Khoj",
47
+ "subject": "Your login code to Khoj",
48
48
  "html": html_content,
49
49
  }
50
50
  )
@@ -98,11 +98,11 @@ async def send_query_feedback(uquery, kquery, sentiment, user_email):
98
98
  user_email=user_email if not is_none_or_empty(user_email) else "N/A",
99
99
  )
100
100
  # send feedback to fixed account
101
- r = resend.Emails.send(
101
+ resend.Emails.send(
102
102
  {
103
103
  "sender": os.environ.get("RESEND_EMAIL", "noreply@khoj.dev"),
104
104
  "to": "team@khoj.dev",
105
- "subject": f"User Feedback",
105
+ "subject": "User Feedback",
106
106
  "html": html_content,
107
107
  }
108
108
  )
@@ -127,7 +127,7 @@ def send_task_email(name, email, query, result, subject, is_image=False):
127
127
 
128
128
  r = resend.Emails.send(
129
129
  {
130
- "sender": f'Khoj <{os.environ.get("RESEND_EMAIL", "khoj@khoj.dev")}>',
130
+ "sender": f"Khoj <{os.environ.get('RESEND_EMAIL', 'khoj@khoj.dev')}>",
131
131
  "to": email,
132
132
  "subject": f"✨ {subject}",
133
133
  "html": html_content,
khoj/routers/helpers.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import asyncio
2
2
  import base64
3
- import concurrent.futures
4
3
  import fnmatch
5
4
  import hashlib
6
5
  import json
@@ -47,14 +46,12 @@ from khoj.database.adapters import (
47
46
  EntryAdapters,
48
47
  FileObjectAdapters,
49
48
  aget_user_by_email,
50
- ais_user_subscribed,
51
49
  create_khoj_token,
52
50
  get_default_search_model,
53
51
  get_khoj_tokens,
54
52
  get_user_name,
55
53
  get_user_notion_config,
56
54
  get_user_subscription_state,
57
- is_user_subscribed,
58
55
  run_with_process_lock,
59
56
  )
60
57
  from khoj.database.models import (
@@ -160,7 +157,7 @@ def validate_chat_model(user: KhojUser):
160
157
 
161
158
  async def is_ready_to_chat(user: KhojUser):
162
159
  user_chat_model = await ConversationAdapters.aget_user_chat_model(user)
163
- if user_chat_model == None:
160
+ if user_chat_model is None:
164
161
  user_chat_model = await ConversationAdapters.aget_default_chat_model(user)
165
162
 
166
163
  if (
@@ -581,7 +578,7 @@ async def generate_online_subqueries(
581
578
  )
582
579
  return {q}
583
580
  return response
584
- except Exception as e:
581
+ except Exception:
585
582
  logger.error(f"Invalid response for constructing online subqueries: {response}. Returning original query: {q}")
586
583
  return {q}
587
584
 
@@ -1172,8 +1169,8 @@ async def search_documents(
1172
1169
  agent_has_entries = await sync_to_async(EntryAdapters.agent_has_entries)(agent=agent)
1173
1170
 
1174
1171
  if (
1175
- not ConversationCommand.Notes in conversation_commands
1176
- and not ConversationCommand.Default in conversation_commands
1172
+ ConversationCommand.Notes not in conversation_commands
1173
+ and ConversationCommand.Default not in conversation_commands
1177
1174
  and not agent_has_entries
1178
1175
  ):
1179
1176
  yield compiled_references, inferred_queries, q
@@ -1267,6 +1264,7 @@ async def extract_questions(
1267
1264
  location_data: LocationData = None,
1268
1265
  query_images: Optional[List[str]] = None,
1269
1266
  query_files: str = None,
1267
+ max_queries: int = 5,
1270
1268
  tracer: dict = {},
1271
1269
  ):
1272
1270
  """
@@ -1296,14 +1294,20 @@ async def extract_questions(
1296
1294
  location=location,
1297
1295
  username=username,
1298
1296
  personality_context=personality_context,
1297
+ max_queries=max_queries,
1299
1298
  )
1300
1299
 
1301
1300
  prompt = prompts.extract_questions_user_message.format(text=query, chat_history=chat_history_str)
1302
1301
 
1303
1302
  class DocumentQueries(BaseModel):
1304
- """Choose searches to run on user documents."""
1303
+ """Choose semantic search queries to run on user documents."""
1305
1304
 
1306
- queries: List[str] = Field(..., min_items=1, description="List of search queries to run on user documents.")
1305
+ queries: List[str] = Field(
1306
+ ...,
1307
+ min_length=1,
1308
+ max_length=max_queries,
1309
+ description="List of semantic search queries to run on user documents.",
1310
+ )
1307
1311
 
1308
1312
  raw_response = await send_message_to_model_wrapper(
1309
1313
  system_message=system_prompt,
@@ -1325,8 +1329,8 @@ async def extract_questions(
1325
1329
  logger.error(f"Invalid response for constructing subqueries: {response}")
1326
1330
  return [query]
1327
1331
  return queries
1328
- except:
1329
- logger.warning(f"LLM returned invalid JSON. Falling back to using user message as search query.")
1332
+ except Exception:
1333
+ logger.warning("LLM returned invalid JSON. Falling back to using user message as search query.")
1330
1334
  return [query]
1331
1335
 
1332
1336
 
@@ -1351,7 +1355,7 @@ async def execute_search(
1351
1355
  return results
1352
1356
 
1353
1357
  if q is None or q == "":
1354
- logger.warning(f"No query param (q) passed in API call to initiate search")
1358
+ logger.warning("No query param (q) passed in API call to initiate search")
1355
1359
  return results
1356
1360
 
1357
1361
  # initialize variables
@@ -1364,7 +1368,7 @@ async def execute_search(
1364
1368
  if user:
1365
1369
  query_cache_key = f"{user_query}-{n}-{t}-{r}-{max_distance}-{dedupe}"
1366
1370
  if query_cache_key in state.query_cache[user.uuid]:
1367
- logger.debug(f"Return response from query cache")
1371
+ logger.debug("Return response from query cache")
1368
1372
  return state.query_cache[user.uuid][query_cache_key]
1369
1373
 
1370
1374
  # Encode query with filter terms removed
@@ -1875,8 +1879,8 @@ class ApiUserRateLimiter:
1875
1879
 
1876
1880
  user: KhojUser = websocket.scope["user"].object
1877
1881
  subscribed = has_required_scope(websocket, ["premium"])
1878
- current_window = "today" if self.window == 60 * 60 * 24 else f"now"
1879
- next_window = "tomorrow" if self.window == 60 * 60 * 24 else f"in a bit"
1882
+ current_window = "today" if self.window == 60 * 60 * 24 else "now"
1883
+ next_window = "tomorrow" if self.window == 60 * 60 * 24 else "in a bit"
1880
1884
  common_message_prefix = f"I'm glad you're enjoying interacting with me! You've unfortunately exceeded your usage limit for {current_window}."
1881
1885
 
1882
1886
  # Remove requests outside of the time window
@@ -2219,7 +2223,7 @@ def should_notify(original_query: str, executed_query: str, ai_response: str, us
2219
2223
  should_notify_result = response["decision"] == "Yes"
2220
2224
  reason = response.get("reason", "unknown")
2221
2225
  logger.info(
2222
- f'Decided to {"not " if not should_notify_result else ""}notify user of automation response because of reason: {reason}.'
2226
+ f"Decided to {'not ' if not should_notify_result else ''}notify user of automation response because of reason: {reason}."
2223
2227
  )
2224
2228
  return should_notify_result
2225
2229
  except Exception as e:
@@ -2313,7 +2317,7 @@ def scheduled_chat(
2313
2317
  response_map = raw_response.json()
2314
2318
  ai_response = response_map.get("response") or response_map.get("image")
2315
2319
  is_image = False
2316
- if type(ai_response) == dict:
2320
+ if isinstance(ai_response, dict):
2317
2321
  is_image = ai_response.get("image") is not None
2318
2322
  else:
2319
2323
  ai_response = raw_response.text
@@ -2460,12 +2464,12 @@ async def aschedule_automation(
2460
2464
 
2461
2465
  def construct_automation_created_message(automation: Job, crontime: str, query_to_run: str, subject: str):
2462
2466
  # Display next run time in user timezone instead of UTC
2463
- schedule = f'{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime("%Z")}'
2467
+ schedule = f"{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime('%Z')}"
2464
2468
  next_run_time = automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z")
2465
2469
  # Remove /automated_task prefix from inferred_query
2466
2470
  unprefixed_query_to_run = re.sub(r"^\/automated_task\s*", "", query_to_run)
2467
2471
  # Create the automation response
2468
- automation_icon_url = f"/static/assets/icons/automation.svg"
2472
+ automation_icon_url = "/static/assets/icons/automation.svg"
2469
2473
  return f"""
2470
2474
  ### ![]({automation_icon_url}) Created Automation
2471
2475
  - Subject: **{subject}**
@@ -2713,13 +2717,13 @@ def configure_content(
2713
2717
  t: Optional[state.SearchType] = state.SearchType.All,
2714
2718
  ) -> bool:
2715
2719
  success = True
2716
- if t == None:
2720
+ if t is None:
2717
2721
  t = state.SearchType.All
2718
2722
 
2719
2723
  if t is not None and t in [type.value for type in state.SearchType]:
2720
2724
  t = state.SearchType(t)
2721
2725
 
2722
- if t is not None and not t.value in [type.value for type in state.SearchType]:
2726
+ if t is not None and t.value not in [type.value for type in state.SearchType]:
2723
2727
  logger.warning(f"🚨 Invalid search type: {t}")
2724
2728
  return False
2725
2729
 
@@ -2988,7 +2992,7 @@ async def grep_files(
2988
2992
  query += f" {' and '.join(context_info)}"
2989
2993
  if line_count > max_results:
2990
2994
  if lines_before or lines_after:
2991
- query += f" for"
2995
+ query += " for"
2992
2996
  query += f" first {max_results} results"
2993
2997
  return query
2994
2998
 
@@ -2998,7 +3002,7 @@ async def grep_files(
2998
3002
  lines_after = lines_after or 0
2999
3003
 
3000
3004
  try:
3001
- regex = re.compile(regex_pattern, re.IGNORECASE)
3005
+ regex = re.compile(regex_pattern, re.IGNORECASE | re.MULTILINE)
3002
3006
  except re.error as e:
3003
3007
  yield {
3004
3008
  "query": _generate_query(0, 0, path_prefix, regex_pattern, lines_before, lines_after),
@@ -3008,7 +3012,14 @@ async def grep_files(
3008
3012
  return
3009
3013
 
3010
3014
  try:
3011
- file_matches = await FileObjectAdapters.aget_file_objects_by_regex(user, regex_pattern, path_prefix)
3015
+ # Make db pushdown filters more permissive by removing line anchors
3016
+ # The precise line-anchored matching will be done in Python stage
3017
+ db_pattern = regex_pattern
3018
+ db_pattern = re.sub(r"\(\?\w*\)", "", db_pattern) # Remove inline flags like (?i), (?m), (?im)
3019
+ db_pattern = re.sub(r"^\^", "", db_pattern) # Remove ^ at regex pattern start
3020
+ db_pattern = re.sub(r"\$$", "", db_pattern) # Remove $ at regex pattern end
3021
+
3022
+ file_matches = await FileObjectAdapters.aget_file_objects_by_regex(user, db_pattern, path_prefix)
3012
3023
 
3013
3024
  line_matches = []
3014
3025
  for file_object in file_matches:
khoj/routers/research.py CHANGED
@@ -15,7 +15,6 @@ from khoj.processor.conversation.utils import (
15
15
  ResearchIteration,
16
16
  ToolCall,
17
17
  construct_iteration_history,
18
- construct_structured_message,
19
18
  construct_tool_chat_history,
20
19
  load_complex_json,
21
20
  )
@@ -24,7 +23,6 @@ from khoj.processor.tools.online_search import read_webpages_content, search_onl
24
23
  from khoj.processor.tools.run_code import run_code
25
24
  from khoj.routers.helpers import (
26
25
  ChatEvent,
27
- generate_summary_from_files,
28
26
  get_message_from_queue,
29
27
  grep_files,
30
28
  list_files,
@@ -184,7 +182,7 @@ async def apick_next_tool(
184
182
  # TODO: Handle multiple tool calls.
185
183
  response_text = response.text
186
184
  parsed_response = [ToolCall(**item) for item in load_complex_json(response_text)][0]
187
- except Exception as e:
185
+ except Exception:
188
186
  # Otherwise assume the model has decided to end the research run and respond to the user.
189
187
  parsed_response = ToolCall(name=ConversationCommand.Text, args={"response": response_text}, id=None)
190
188
 
@@ -199,7 +197,7 @@ async def apick_next_tool(
199
197
  if i.warning is None and isinstance(i.query, ToolCall)
200
198
  }
201
199
  if (parsed_response.name, dict_to_tuple(parsed_response.args)) in previous_tool_query_combinations:
202
- warning = f"Repeated tool, query combination detected. Skipping iteration. Try something different."
200
+ warning = "Repeated tool, query combination detected. Skipping iteration. Try something different."
203
201
  # Only send client status updates if we'll execute this iteration and model has thoughts to share.
204
202
  elif send_status_func and not is_none_or_empty(response.thought):
205
203
  async for event in send_status_func(response.thought):
@@ -4,12 +4,10 @@ from typing import List
4
4
 
5
5
  class BaseFilter(ABC):
6
6
  @abstractmethod
7
- def get_filter_terms(self, query: str) -> List[str]:
8
- ...
7
+ def get_filter_terms(self, query: str) -> List[str]: ...
9
8
 
10
9
  def can_filter(self, raw_query: str) -> bool:
11
10
  return len(self.get_filter_terms(raw_query)) > 0
12
11
 
13
12
  @abstractmethod
14
- def defilter(self, query: str) -> str:
15
- ...
13
+ def defilter(self, query: str) -> str: ...
@@ -9,9 +9,8 @@ from asgiref.sync import sync_to_async
9
9
  from sentence_transformers import util
10
10
 
11
11
  from khoj.database.adapters import EntryAdapters, get_default_search_model
12
- from khoj.database.models import Agent
12
+ from khoj.database.models import Agent, KhojUser
13
13
  from khoj.database.models import Entry as DbEntry
14
- from khoj.database.models import KhojUser
15
14
  from khoj.processor.content.text_to_entries import TextToEntries
16
15
  from khoj.utils import state
17
16
  from khoj.utils.helpers import get_absolute_path, timer
khoj/utils/constants.py CHANGED
@@ -40,6 +40,9 @@ model_to_cost: Dict[str, Dict[str, float]] = {
40
40
  "o3": {"input": 2.0, "output": 8.00},
41
41
  "o3-pro": {"input": 20.0, "output": 80.00},
42
42
  "o4-mini": {"input": 1.10, "output": 4.40},
43
+ "gpt-5-2025-08-07": {"input": 1.25, "output": 10.00, "cache_read": 0.125},
44
+ "gpt-5-mini-2025-08-07": {"input": 0.25, "output": 2.00, "cache_read": 0.025},
45
+ "gpt-5-nano-2025-08-07": {"input": 0.05, "output": 0.40, "cache_read": 0.005},
43
46
  # Gemini Pricing: https://ai.google.dev/pricing
44
47
  "gemini-1.5-flash": {"input": 0.075, "output": 0.30},
45
48
  "gemini-1.5-flash-002": {"input": 0.075, "output": 0.30},
khoj/utils/helpers.py CHANGED
@@ -77,7 +77,7 @@ class AsyncIteratorWrapper:
77
77
 
78
78
 
79
79
  def is_none_or_empty(item):
80
- return item == None or (hasattr(item, "__iter__") and len(item) == 0) or item == ""
80
+ return item is None or (hasattr(item, "__iter__") and len(item) == 0) or item == ""
81
81
 
82
82
 
83
83
  def to_snake_case_from_dash(item: str):
@@ -97,7 +97,7 @@ def get_from_dict(dictionary, *args):
97
97
  Returns: dictionary[args[0]][args[1]]... or None if any keys missing"""
98
98
  current = dictionary
99
99
  for arg in args:
100
- if not hasattr(current, "__iter__") or not arg in current:
100
+ if not hasattr(current, "__iter__") or arg not in current:
101
101
  return None
102
102
  current = current[arg]
103
103
  return current
@@ -751,7 +751,7 @@ def is_valid_url(url: str) -> bool:
751
751
  try:
752
752
  result = urlparse(url.strip())
753
753
  return all([result.scheme, result.netloc])
754
- except:
754
+ except Exception:
755
755
  return False
756
756
 
757
757
 
@@ -759,7 +759,7 @@ def is_internet_connected():
759
759
  try:
760
760
  response = requests.head("https://www.google.com")
761
761
  return response.status_code == 200
762
- except:
762
+ except Exception:
763
763
  return False
764
764
 
765
765
 
@@ -60,9 +60,7 @@ def initialization(interactive: bool = True):
60
60
  ]
61
61
  default_chat_models = known_available_models + other_available_models
62
62
  except Exception as e:
63
- logger.warning(
64
- f"⚠️ Failed to fetch {provider} chat models. Fallback to default models. Error: {str(e)}"
65
- )
63
+ logger.warning(f"⚠️ Failed to fetch {provider} chat models. Fallback to default models. Error: {str(e)}")
66
64
 
67
65
  # Set up OpenAI's online chat models
68
66
  openai_configured, openai_provider = _setup_chat_model_provider(
khoj/utils/models.py CHANGED
@@ -8,12 +8,10 @@ from tqdm import trange
8
8
 
9
9
  class BaseEncoder(ABC):
10
10
  @abstractmethod
11
- def __init__(self, model_name: str, device: torch.device = None, **kwargs):
12
- ...
11
+ def __init__(self, model_name: str, device: torch.device = None, **kwargs): ...
13
12
 
14
13
  @abstractmethod
15
- def encode(self, entries: List[str], device: torch.device = None, **kwargs) -> torch.Tensor:
16
- ...
14
+ def encode(self, entries: List[str], device: torch.device = None, **kwargs) -> torch.Tensor: ...
17
15
 
18
16
 
19
17
  class OpenAI(BaseEncoder):
khoj/utils/rawconfig.py CHANGED
@@ -1,8 +1,7 @@
1
1
  # System Packages
2
2
  import json
3
3
  import uuid
4
- from pathlib import Path
5
- from typing import Dict, List, Optional
4
+ from typing import List, Optional
6
5
 
7
6
  from pydantic import BaseModel
8
7
 
khoj/utils/state.py CHANGED
@@ -2,7 +2,7 @@ import os
2
2
  import threading
3
3
  from collections import defaultdict
4
4
  from pathlib import Path
5
- from typing import Any, Dict, List
5
+ from typing import Dict, List
6
6
 
7
7
  from apscheduler.schedulers.background import BackgroundScheduler
8
8
  from openai import OpenAI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: khoj
3
- Version: 2.0.0b13.dev5
3
+ Version: 2.0.0b13.dev23
4
4
  Summary: Your Second Brain
5
5
  Project-URL: Homepage, https://khoj.dev
6
6
  Project-URL: Documentation, https://docs.khoj.dev
@@ -27,6 +27,7 @@ Requires-Dist: anyio~=4.8.0
27
27
  Requires-Dist: apscheduler~=3.10.0
28
28
  Requires-Dist: authlib==1.2.1
29
29
  Requires-Dist: beautifulsoup4~=4.12.3
30
+ Requires-Dist: click<8.2.2
30
31
  Requires-Dist: cron-descriptor==1.4.3
31
32
  Requires-Dist: dateparser>=1.1.1
32
33
  Requires-Dist: defusedxml==0.7.1
@@ -78,7 +79,6 @@ Requires-Dist: tzdata==2023.3
78
79
  Requires-Dist: uvicorn==0.30.6
79
80
  Requires-Dist: websockets==13.0
80
81
  Provides-Extra: dev
81
- Requires-Dist: black>=23.1.0; extra == 'dev'
82
82
  Requires-Dist: boto3>=1.34.57; extra == 'dev'
83
83
  Requires-Dist: datasets; extra == 'dev'
84
84
  Requires-Dist: factory-boy>=3.2.1; extra == 'dev'
@@ -93,6 +93,7 @@ Requires-Dist: pytest-asyncio==0.21.1; extra == 'dev'
93
93
  Requires-Dist: pytest-django==4.5.2; extra == 'dev'
94
94
  Requires-Dist: pytest-xdist[psutil]; extra == 'dev'
95
95
  Requires-Dist: pytest>=7.1.2; extra == 'dev'
96
+ Requires-Dist: ruff>=0.12.0; extra == 'dev'
96
97
  Requires-Dist: stripe==7.3.0; extra == 'dev'
97
98
  Requires-Dist: twilio==8.11; extra == 'dev'
98
99
  Provides-Extra: local