khoj 2.0.0b12.dev5__py3-none-any.whl → 2.0.0b13.dev19__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 (193) hide show
  1. khoj/app/README.md +1 -1
  2. khoj/app/urls.py +1 -0
  3. khoj/configure.py +21 -54
  4. khoj/database/adapters/__init__.py +6 -15
  5. khoj/database/management/commands/delete_orphaned_fileobjects.py +0 -1
  6. khoj/database/migrations/0064_remove_conversation_temp_id_alter_conversation_id.py +1 -1
  7. khoj/database/migrations/0075_migrate_generated_assets_and_validate.py +1 -1
  8. khoj/database/migrations/0092_alter_chatmodel_model_type_alter_chatmodel_name_and_more.py +36 -0
  9. khoj/database/migrations/0093_remove_localorgconfig_user_and_more.py +36 -0
  10. khoj/database/models/__init__.py +10 -40
  11. khoj/database/tests.py +0 -2
  12. khoj/interface/compiled/404/index.html +2 -2
  13. khoj/interface/compiled/_next/static/chunks/{9245.a04e92d034540234.js → 1225.ecac11e7421504c4.js} +3 -3
  14. khoj/interface/compiled/_next/static/chunks/1320.ae930ad00affe685.js +5 -0
  15. khoj/interface/compiled/_next/static/chunks/{1327-1a9107b9a2a04a98.js → 1327-511bb0a862efce80.js} +1 -1
  16. khoj/interface/compiled/_next/static/chunks/1626.15a8acc0d6639ec6.js +1 -0
  17. khoj/interface/compiled/_next/static/chunks/{3489.c523fe96a2eee74f.js → 1940.d082758bd04e08ae.js} +1 -1
  18. khoj/interface/compiled/_next/static/chunks/{2327-ea623ca2d22f78e9.js → 2327-fe87dd989d71d0eb.js} +1 -1
  19. khoj/interface/compiled/_next/static/chunks/2475.57a0d0fd93d07af0.js +93 -0
  20. khoj/interface/compiled/_next/static/chunks/2481.5ce6524ba0a73f90.js +55 -0
  21. khoj/interface/compiled/_next/static/chunks/297.4c4c823ff6e3255b.js +174 -0
  22. khoj/interface/compiled/_next/static/chunks/{5639-09e2009a2adedf8b.js → 3260-43d3019b92c315bb.js} +68 -23
  23. khoj/interface/compiled/_next/static/chunks/3353.1c6d553216a1acae.js +1 -0
  24. khoj/interface/compiled/_next/static/chunks/3855.f7b8131f78af046e.js +1 -0
  25. khoj/interface/compiled/_next/static/chunks/3973.dc54a39586ab48be.js +1 -0
  26. khoj/interface/compiled/_next/static/chunks/4241.c1cd170f7f37ac59.js +24 -0
  27. khoj/interface/compiled/_next/static/chunks/{4327.8d2a1b8f1ea78208.js → 4327.f3704dc398c67113.js} +19 -19
  28. khoj/interface/compiled/_next/static/chunks/4505.f09454a346269c3f.js +117 -0
  29. khoj/interface/compiled/_next/static/chunks/4801.96a152d49742b644.js +1 -0
  30. khoj/interface/compiled/_next/static/chunks/5427-a95ec748e52abb75.js +1 -0
  31. khoj/interface/compiled/_next/static/chunks/549.2bd27f59a91a9668.js +148 -0
  32. khoj/interface/compiled/_next/static/chunks/5765.71b1e1207b76b03f.js +1 -0
  33. khoj/interface/compiled/_next/static/chunks/584.d7ce3505f169b706.js +1 -0
  34. khoj/interface/compiled/_next/static/chunks/6240.34f7c1fa692edd61.js +24 -0
  35. khoj/interface/compiled/_next/static/chunks/6d3fe5a5-f9f3c16e0bc0cdf9.js +10 -0
  36. khoj/interface/compiled/_next/static/chunks/{7127-0f4a2a77d97fb5fa.js → 7127-97b83757db125ba6.js} +1 -1
  37. khoj/interface/compiled/_next/static/chunks/7200-93ab0072359b8028.js +1 -0
  38. khoj/interface/compiled/_next/static/chunks/{2612.bcf5a623b3da209e.js → 7553.f5ad54b1f6e92c49.js} +2 -2
  39. khoj/interface/compiled/_next/static/chunks/7626-1b630f1654172341.js +1 -0
  40. khoj/interface/compiled/_next/static/chunks/764.dadd316e8e16d191.js +63 -0
  41. khoj/interface/compiled/_next/static/chunks/78.08169ab541abab4f.js +43 -0
  42. khoj/interface/compiled/_next/static/chunks/784.e03acf460df213d1.js +1 -0
  43. khoj/interface/compiled/_next/static/chunks/{9537-d9ab442ce15d1e20.js → 8072-e1440cb482a0940e.js} +1 -1
  44. khoj/interface/compiled/_next/static/chunks/{3265.924139c4146ee344.js → 8086.8d39887215807fcd.js} +1 -1
  45. khoj/interface/compiled/_next/static/chunks/8168.f074ab8c7c16d82d.js +59 -0
  46. khoj/interface/compiled/_next/static/chunks/{8694.2bd9c2f65d8c5847.js → 8223.1705878fa7a09292.js} +1 -1
  47. khoj/interface/compiled/_next/static/chunks/8483.94f6c9e2bee86f50.js +215 -0
  48. khoj/interface/compiled/_next/static/chunks/{8888.ebe0e552b59e7fed.js → 8810.fc0e479de78c7c61.js} +1 -1
  49. khoj/interface/compiled/_next/static/chunks/8828.bc74dc4ce94e78f6.js +1 -0
  50. khoj/interface/compiled/_next/static/chunks/{7303.d0612f812a967a08.js → 8909.14ac3f43d0070cf1.js} +5 -5
  51. khoj/interface/compiled/_next/static/chunks/90542734.b1a1629065ba199b.js +1 -0
  52. khoj/interface/compiled/_next/static/chunks/9167.098534184f03fe92.js +56 -0
  53. khoj/interface/compiled/_next/static/chunks/{4980.63500d68b3bb1222.js → 9537.e934ce37bf314509.js} +5 -5
  54. khoj/interface/compiled/_next/static/chunks/9574.3fe8e26e95bf1c34.js +1 -0
  55. khoj/interface/compiled/_next/static/chunks/9599.ec50b5296c27dae9.js +1 -0
  56. khoj/interface/compiled/_next/static/chunks/9643.b34248df52ffc77c.js +262 -0
  57. khoj/interface/compiled/_next/static/chunks/9747.2fd9065b1435abb1.js +1 -0
  58. khoj/interface/compiled/_next/static/chunks/9922.98f2b2a9959b4ebe.js +1 -0
  59. khoj/interface/compiled/_next/static/chunks/app/agents/layout-4e2a134ec26aa606.js +1 -0
  60. khoj/interface/compiled/_next/static/chunks/app/agents/page-e291b49977f43880.js +1 -0
  61. khoj/interface/compiled/_next/static/chunks/app/automations/page-198b26df6e09bbb0.js +1 -0
  62. khoj/interface/compiled/_next/static/chunks/app/chat/layout-ad4d1792ab1a4108.js +1 -0
  63. khoj/interface/compiled/_next/static/chunks/app/chat/{page-8e1c4f2af3c9429e.js → page-9a75d7369f2a7cd2.js} +1 -1
  64. khoj/interface/compiled/_next/static/chunks/app/{page-2b3056cba8aa96ce.js → page-1567cac7b79a7c59.js} +1 -1
  65. khoj/interface/compiled/_next/static/chunks/app/settings/{page-8be3b35178abf2ec.js → page-6081362437c82470.js} +1 -1
  66. khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-4a4b0c0f4749c2b2.js → page-e0dcb1762f8c8f88.js} +1 -1
  67. khoj/interface/compiled/_next/static/chunks/webpack-d60b0c57a6c38d0f.js +1 -0
  68. khoj/interface/compiled/_next/static/css/{c34713c98384ee87.css → 2945c4a857922f3b.css} +1 -1
  69. khoj/interface/compiled/agents/index.html +2 -2
  70. khoj/interface/compiled/agents/index.txt +3 -3
  71. khoj/interface/compiled/automations/index.html +2 -2
  72. khoj/interface/compiled/automations/index.txt +4 -4
  73. khoj/interface/compiled/chat/index.html +2 -2
  74. khoj/interface/compiled/chat/index.txt +3 -3
  75. khoj/interface/compiled/index.html +2 -2
  76. khoj/interface/compiled/index.txt +3 -3
  77. khoj/interface/compiled/search/index.html +2 -2
  78. khoj/interface/compiled/search/index.txt +3 -3
  79. khoj/interface/compiled/settings/index.html +2 -2
  80. khoj/interface/compiled/settings/index.txt +5 -5
  81. khoj/interface/compiled/share/chat/index.html +2 -2
  82. khoj/interface/compiled/share/chat/index.txt +3 -3
  83. khoj/main.py +7 -9
  84. khoj/manage.py +1 -0
  85. khoj/processor/content/github/github_to_entries.py +6 -7
  86. khoj/processor/content/images/image_to_entries.py +0 -1
  87. khoj/processor/content/markdown/markdown_to_entries.py +2 -3
  88. khoj/processor/content/notion/notion_to_entries.py +5 -6
  89. khoj/processor/content/org_mode/org_to_entries.py +4 -5
  90. khoj/processor/content/org_mode/orgnode.py +4 -4
  91. khoj/processor/content/plaintext/plaintext_to_entries.py +1 -2
  92. khoj/processor/content/text_to_entries.py +1 -3
  93. khoj/processor/conversation/google/utils.py +3 -3
  94. khoj/processor/conversation/openai/utils.py +3 -4
  95. khoj/processor/conversation/prompts.py +0 -32
  96. khoj/processor/conversation/utils.py +25 -38
  97. khoj/processor/embeddings.py +0 -2
  98. khoj/processor/image/generate.py +3 -3
  99. khoj/processor/operator/__init__.py +2 -3
  100. khoj/processor/operator/grounding_agent.py +15 -2
  101. khoj/processor/operator/grounding_agent_uitars.py +34 -23
  102. khoj/processor/operator/operator_agent_anthropic.py +29 -4
  103. khoj/processor/operator/operator_agent_base.py +1 -1
  104. khoj/processor/operator/operator_agent_binary.py +4 -4
  105. khoj/processor/operator/operator_agent_openai.py +21 -6
  106. khoj/processor/operator/operator_environment_browser.py +1 -1
  107. khoj/processor/operator/operator_environment_computer.py +1 -1
  108. khoj/processor/speech/text_to_speech.py +0 -1
  109. khoj/processor/tools/online_search.py +1 -1
  110. khoj/processor/tools/run_code.py +1 -1
  111. khoj/routers/api.py +2 -15
  112. khoj/routers/api_agents.py +1 -2
  113. khoj/routers/api_automation.py +1 -1
  114. khoj/routers/api_chat.py +10 -16
  115. khoj/routers/api_content.py +3 -111
  116. khoj/routers/api_model.py +0 -1
  117. khoj/routers/api_subscription.py +1 -1
  118. khoj/routers/email.py +4 -4
  119. khoj/routers/helpers.py +26 -99
  120. khoj/routers/research.py +2 -4
  121. khoj/search_filter/base_filter.py +2 -4
  122. khoj/search_type/text_search.py +1 -2
  123. khoj/utils/cli.py +5 -53
  124. khoj/utils/config.py +0 -65
  125. khoj/utils/constants.py +0 -7
  126. khoj/utils/helpers.py +5 -13
  127. khoj/utils/initialization.py +7 -48
  128. khoj/utils/models.py +2 -4
  129. khoj/utils/rawconfig.py +1 -69
  130. khoj/utils/state.py +2 -8
  131. khoj/utils/yaml.py +0 -39
  132. {khoj-2.0.0b12.dev5.dist-info → khoj-2.0.0b13.dev19.dist-info}/METADATA +3 -3
  133. {khoj-2.0.0b12.dev5.dist-info → khoj-2.0.0b13.dev19.dist-info}/RECORD +145 -154
  134. khoj/interface/compiled/_next/static/chunks/1191.b547ec13349b4aed.js +0 -1
  135. khoj/interface/compiled/_next/static/chunks/1588.f0558a0bdffc4761.js +0 -117
  136. khoj/interface/compiled/_next/static/chunks/1918.925cb4a35518d258.js +0 -43
  137. khoj/interface/compiled/_next/static/chunks/2849.dc00ae5ba7219cfc.js +0 -1
  138. khoj/interface/compiled/_next/static/chunks/303.fe76de943e930fbd.js +0 -1
  139. khoj/interface/compiled/_next/static/chunks/4533.586e74b45a2bde25.js +0 -55
  140. khoj/interface/compiled/_next/static/chunks/4551.82ce1476b5516bc2.js +0 -5
  141. khoj/interface/compiled/_next/static/chunks/4748.0edd37cba3ea2809.js +0 -59
  142. khoj/interface/compiled/_next/static/chunks/5210.cd35a1c1ec594a20.js +0 -93
  143. khoj/interface/compiled/_next/static/chunks/5329.f8b3c5b3d16159cd.js +0 -1
  144. khoj/interface/compiled/_next/static/chunks/5427-13d6ffd380fdfab7.js +0 -1
  145. khoj/interface/compiled/_next/static/chunks/558-c14e76cff03f6a60.js +0 -1
  146. khoj/interface/compiled/_next/static/chunks/5830.8876eccb82da9b7d.js +0 -262
  147. khoj/interface/compiled/_next/static/chunks/6230.88a71d8145347b3f.js +0 -1
  148. khoj/interface/compiled/_next/static/chunks/7161.77e0530a40ad5ca8.js +0 -1
  149. khoj/interface/compiled/_next/static/chunks/7200-ac3b2e37ff30e126.js +0 -1
  150. khoj/interface/compiled/_next/static/chunks/7505.c31027a3695bdebb.js +0 -148
  151. khoj/interface/compiled/_next/static/chunks/7760.35649cc21d9585bd.js +0 -56
  152. khoj/interface/compiled/_next/static/chunks/83.48e2db193a940052.js +0 -1
  153. khoj/interface/compiled/_next/static/chunks/8427.844694e06133fb51.js +0 -1
  154. khoj/interface/compiled/_next/static/chunks/8665.4db7e6b2e8933497.js +0 -174
  155. khoj/interface/compiled/_next/static/chunks/872.caf84cc1a39ae59f.js +0 -1
  156. khoj/interface/compiled/_next/static/chunks/8890.6e8a59e4de6978bc.js +0 -215
  157. khoj/interface/compiled/_next/static/chunks/8950.5f2272e0ac923f9e.js +0 -1
  158. khoj/interface/compiled/_next/static/chunks/90542734.2c21f16f18b22411.js +0 -1
  159. khoj/interface/compiled/_next/static/chunks/9202.c703864fcedc8d1f.js +0 -63
  160. khoj/interface/compiled/_next/static/chunks/9320.6aca4885d541aa44.js +0 -24
  161. khoj/interface/compiled/_next/static/chunks/9535.f78cd92d03331e55.js +0 -1
  162. khoj/interface/compiled/_next/static/chunks/9968.b111fc002796da81.js +0 -1
  163. khoj/interface/compiled/_next/static/chunks/app/agents/layout-e00fb81dca656a10.js +0 -1
  164. khoj/interface/compiled/_next/static/chunks/app/agents/page-9a4610474cd59a71.js +0 -1
  165. khoj/interface/compiled/_next/static/chunks/app/automations/page-f7bb9d777b7745d4.js +0 -1
  166. khoj/interface/compiled/_next/static/chunks/app/chat/layout-33934fc2d6ae6838.js +0 -1
  167. khoj/interface/compiled/_next/static/chunks/f3e3247b-1758d4651e4457c2.js +0 -10
  168. khoj/interface/compiled/_next/static/chunks/webpack-338a5000c912cc94.js +0 -1
  169. khoj/migrations/__init__.py +0 -0
  170. khoj/migrations/migrate_offline_chat_default_model.py +0 -69
  171. khoj/migrations/migrate_offline_chat_default_model_2.py +0 -71
  172. khoj/migrations/migrate_offline_chat_schema.py +0 -83
  173. khoj/migrations/migrate_offline_model.py +0 -29
  174. khoj/migrations/migrate_processor_config_openai.py +0 -67
  175. khoj/migrations/migrate_server_pg.py +0 -132
  176. khoj/migrations/migrate_version.py +0 -17
  177. khoj/processor/conversation/offline/__init__.py +0 -0
  178. khoj/processor/conversation/offline/chat_model.py +0 -224
  179. khoj/processor/conversation/offline/utils.py +0 -80
  180. khoj/processor/conversation/offline/whisper.py +0 -15
  181. khoj/utils/fs_syncer.py +0 -252
  182. /khoj/interface/compiled/_next/static/{7GoMcE8WpP9fbfYZXv4Nv → N-GdBSXoYe-DuObnbXVRO}/_buildManifest.js +0 -0
  183. /khoj/interface/compiled/_next/static/{7GoMcE8WpP9fbfYZXv4Nv → N-GdBSXoYe-DuObnbXVRO}/_ssgManifest.js +0 -0
  184. /khoj/interface/compiled/_next/static/chunks/{1915-5c6508f6ebb62a30.js → 1915-fbfe167c84ad60c5.js} +0 -0
  185. /khoj/interface/compiled/_next/static/chunks/{2117-080746c8e170c81a.js → 2117-e78b6902ad6f75ec.js} +0 -0
  186. /khoj/interface/compiled/_next/static/chunks/{2939-4af3fd24b8ffc9ad.js → 2939-4d4084c5b888b960.js} +0 -0
  187. /khoj/interface/compiled/_next/static/chunks/{4447-cd95608f8e93e711.js → 4447-d6cf93724d57e34b.js} +0 -0
  188. /khoj/interface/compiled/_next/static/chunks/{8667-50b03a89e82e0ba7.js → 8667-4b7790573b08c50d.js} +0 -0
  189. /khoj/interface/compiled/_next/static/chunks/{9139-8ac4d9feb10f8869.js → 9139-ce1ae935dac9c871.js} +0 -0
  190. /khoj/interface/compiled/_next/static/chunks/app/search/{page-4885df3cd175c957.js → page-3639e50ec3e9acfd.js} +0 -0
  191. {khoj-2.0.0b12.dev5.dist-info → khoj-2.0.0b13.dev19.dist-info}/WHEEL +0 -0
  192. {khoj-2.0.0b12.dev5.dist-info → khoj-2.0.0b13.dev19.dist-info}/entry_points.txt +0 -0
  193. {khoj-2.0.0b12.dev5.dist-info → khoj-2.0.0b13.dev19.dist-info}/licenses/LICENSE +0 -0
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 (
@@ -89,10 +86,6 @@ from khoj.processor.conversation.google.gemini_chat import (
89
86
  converse_gemini,
90
87
  gemini_send_message_to_model,
91
88
  )
92
- from khoj.processor.conversation.offline.chat_model import (
93
- converse_offline,
94
- send_message_to_model_offline,
95
- )
96
89
  from khoj.processor.conversation.openai.gpt import (
97
90
  converse_openai,
98
91
  send_message_to_model,
@@ -117,7 +110,6 @@ from khoj.search_filter.file_filter import FileFilter
117
110
  from khoj.search_filter.word_filter import WordFilter
118
111
  from khoj.search_type import text_search
119
112
  from khoj.utils import state
120
- from khoj.utils.config import OfflineChatProcessorModel
121
113
  from khoj.utils.helpers import (
122
114
  LRU,
123
115
  ConversationCommand,
@@ -165,17 +157,9 @@ def validate_chat_model(user: KhojUser):
165
157
 
166
158
  async def is_ready_to_chat(user: KhojUser):
167
159
  user_chat_model = await ConversationAdapters.aget_user_chat_model(user)
168
- if user_chat_model == None:
160
+ if user_chat_model is None:
169
161
  user_chat_model = await ConversationAdapters.aget_default_chat_model(user)
170
162
 
171
- if user_chat_model and user_chat_model.model_type == ChatModel.ModelType.OFFLINE:
172
- chat_model_name = user_chat_model.name
173
- max_tokens = user_chat_model.max_prompt_size
174
- if state.offline_chat_processor_config is None:
175
- logger.info("Loading Offline Chat Model...")
176
- state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
177
- return True
178
-
179
163
  if (
180
164
  user_chat_model
181
165
  and (
@@ -231,7 +215,6 @@ def update_telemetry_state(
231
215
  telemetry_type=telemetry_type,
232
216
  api=api,
233
217
  client=client,
234
- app_config=state.config.app,
235
218
  disable_telemetry_env=state.telemetry_disabled,
236
219
  properties=user_state,
237
220
  )
@@ -595,7 +578,7 @@ async def generate_online_subqueries(
595
578
  )
596
579
  return {q}
597
580
  return response
598
- except Exception as e:
581
+ except Exception:
599
582
  logger.error(f"Invalid response for constructing online subqueries: {response}. Returning original query: {q}")
600
583
  return {q}
601
584
 
@@ -1186,8 +1169,8 @@ async def search_documents(
1186
1169
  agent_has_entries = await sync_to_async(EntryAdapters.agent_has_entries)(agent=agent)
1187
1170
 
1188
1171
  if (
1189
- not ConversationCommand.Notes in conversation_commands
1190
- and not ConversationCommand.Default in conversation_commands
1172
+ ConversationCommand.Notes not in conversation_commands
1173
+ and ConversationCommand.Default not in conversation_commands
1191
1174
  and not agent_has_entries
1192
1175
  ):
1193
1176
  yield compiled_references, inferred_queries, q
@@ -1339,8 +1322,8 @@ async def extract_questions(
1339
1322
  logger.error(f"Invalid response for constructing subqueries: {response}")
1340
1323
  return [query]
1341
1324
  return queries
1342
- except:
1343
- logger.warning(f"LLM returned invalid JSON. Falling back to using user message as search query.")
1325
+ except Exception:
1326
+ logger.warning("LLM returned invalid JSON. Falling back to using user message as search query.")
1344
1327
  return [query]
1345
1328
 
1346
1329
 
@@ -1365,7 +1348,7 @@ async def execute_search(
1365
1348
  return results
1366
1349
 
1367
1350
  if q is None or q == "":
1368
- logger.warning(f"No query param (q) passed in API call to initiate search")
1351
+ logger.warning("No query param (q) passed in API call to initiate search")
1369
1352
  return results
1370
1353
 
1371
1354
  # initialize variables
@@ -1378,7 +1361,7 @@ async def execute_search(
1378
1361
  if user:
1379
1362
  query_cache_key = f"{user_query}-{n}-{t}-{r}-{max_distance}-{dedupe}"
1380
1363
  if query_cache_key in state.query_cache[user.uuid]:
1381
- logger.debug(f"Return response from query cache")
1364
+ logger.debug("Return response from query cache")
1382
1365
  return state.query_cache[user.uuid][query_cache_key]
1383
1366
 
1384
1367
  # Encode query with filter terms removed
@@ -1470,12 +1453,6 @@ async def send_message_to_model_wrapper(
1470
1453
  vision_available = chat_model.vision_enabled
1471
1454
  api_key = chat_model.ai_model_api.api_key
1472
1455
  api_base_url = chat_model.ai_model_api.api_base_url
1473
- loaded_model = None
1474
-
1475
- if model_type == ChatModel.ModelType.OFFLINE:
1476
- if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
1477
- state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
1478
- loaded_model = state.offline_chat_processor_config.loaded_model
1479
1456
 
1480
1457
  truncated_messages = generate_chatml_messages_with_context(
1481
1458
  user_message=query,
@@ -1483,7 +1460,6 @@ async def send_message_to_model_wrapper(
1483
1460
  system_message=system_message,
1484
1461
  chat_history=chat_history,
1485
1462
  model_name=chat_model_name,
1486
- loaded_model=loaded_model,
1487
1463
  tokenizer_name=tokenizer,
1488
1464
  max_prompt_size=max_tokens,
1489
1465
  vision_enabled=vision_available,
@@ -1492,18 +1468,7 @@ async def send_message_to_model_wrapper(
1492
1468
  query_files=query_files,
1493
1469
  )
1494
1470
 
1495
- if model_type == ChatModel.ModelType.OFFLINE:
1496
- return send_message_to_model_offline(
1497
- messages=truncated_messages,
1498
- loaded_model=loaded_model,
1499
- model_name=chat_model_name,
1500
- max_prompt_size=max_tokens,
1501
- streaming=False,
1502
- response_type=response_type,
1503
- tracer=tracer,
1504
- )
1505
-
1506
- elif model_type == ChatModel.ModelType.OPENAI:
1471
+ if model_type == ChatModel.ModelType.OPENAI:
1507
1472
  return send_message_to_model(
1508
1473
  messages=truncated_messages,
1509
1474
  api_key=api_key,
@@ -1565,19 +1530,12 @@ def send_message_to_model_wrapper_sync(
1565
1530
  vision_available = chat_model.vision_enabled
1566
1531
  api_key = chat_model.ai_model_api.api_key
1567
1532
  api_base_url = chat_model.ai_model_api.api_base_url
1568
- loaded_model = None
1569
-
1570
- if model_type == ChatModel.ModelType.OFFLINE:
1571
- if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
1572
- state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
1573
- loaded_model = state.offline_chat_processor_config.loaded_model
1574
1533
 
1575
1534
  truncated_messages = generate_chatml_messages_with_context(
1576
1535
  user_message=message,
1577
1536
  system_message=system_message,
1578
1537
  chat_history=chat_history,
1579
1538
  model_name=chat_model_name,
1580
- loaded_model=loaded_model,
1581
1539
  max_prompt_size=max_tokens,
1582
1540
  vision_enabled=vision_available,
1583
1541
  model_type=model_type,
@@ -1585,18 +1543,7 @@ def send_message_to_model_wrapper_sync(
1585
1543
  query_files=query_files,
1586
1544
  )
1587
1545
 
1588
- if model_type == ChatModel.ModelType.OFFLINE:
1589
- return send_message_to_model_offline(
1590
- messages=truncated_messages,
1591
- loaded_model=loaded_model,
1592
- model_name=chat_model_name,
1593
- max_prompt_size=max_tokens,
1594
- streaming=False,
1595
- response_type=response_type,
1596
- tracer=tracer,
1597
- )
1598
-
1599
- elif model_type == ChatModel.ModelType.OPENAI:
1546
+ if model_type == ChatModel.ModelType.OPENAI:
1600
1547
  return send_message_to_model(
1601
1548
  messages=truncated_messages,
1602
1549
  api_key=api_key,
@@ -1678,30 +1625,7 @@ async def agenerate_chat_response(
1678
1625
  chat_model = vision_enabled_config
1679
1626
  vision_available = True
1680
1627
 
1681
- if chat_model.model_type == "offline":
1682
- loaded_model = state.offline_chat_processor_config.loaded_model
1683
- chat_response_generator = converse_offline(
1684
- # Query
1685
- user_query=query_to_run,
1686
- # Context
1687
- references=compiled_references,
1688
- online_results=online_results,
1689
- generated_files=raw_generated_files,
1690
- generated_asset_results=generated_asset_results,
1691
- location_data=location_data,
1692
- user_name=user_name,
1693
- query_files=query_files,
1694
- chat_history=chat_history,
1695
- # Model
1696
- loaded_model=loaded_model,
1697
- model_name=chat_model.name,
1698
- max_prompt_size=chat_model.max_prompt_size,
1699
- tokenizer_name=chat_model.tokenizer,
1700
- agent=agent,
1701
- tracer=tracer,
1702
- )
1703
-
1704
- elif chat_model.model_type == ChatModel.ModelType.OPENAI:
1628
+ if chat_model.model_type == ChatModel.ModelType.OPENAI:
1705
1629
  openai_chat_config = chat_model.ai_model_api
1706
1630
  api_key = openai_chat_config.api_key
1707
1631
  chat_model_name = chat_model.name
@@ -1948,8 +1872,8 @@ class ApiUserRateLimiter:
1948
1872
 
1949
1873
  user: KhojUser = websocket.scope["user"].object
1950
1874
  subscribed = has_required_scope(websocket, ["premium"])
1951
- current_window = "today" if self.window == 60 * 60 * 24 else f"now"
1952
- next_window = "tomorrow" if self.window == 60 * 60 * 24 else f"in a bit"
1875
+ current_window = "today" if self.window == 60 * 60 * 24 else "now"
1876
+ next_window = "tomorrow" if self.window == 60 * 60 * 24 else "in a bit"
1953
1877
  common_message_prefix = f"I'm glad you're enjoying interacting with me! You've unfortunately exceeded your usage limit for {current_window}."
1954
1878
 
1955
1879
  # Remove requests outside of the time window
@@ -2292,7 +2216,7 @@ def should_notify(original_query: str, executed_query: str, ai_response: str, us
2292
2216
  should_notify_result = response["decision"] == "Yes"
2293
2217
  reason = response.get("reason", "unknown")
2294
2218
  logger.info(
2295
- f'Decided to {"not " if not should_notify_result else ""}notify user of automation response because of reason: {reason}.'
2219
+ f"Decided to {'not ' if not should_notify_result else ''}notify user of automation response because of reason: {reason}."
2296
2220
  )
2297
2221
  return should_notify_result
2298
2222
  except Exception as e:
@@ -2386,7 +2310,7 @@ def scheduled_chat(
2386
2310
  response_map = raw_response.json()
2387
2311
  ai_response = response_map.get("response") or response_map.get("image")
2388
2312
  is_image = False
2389
- if type(ai_response) == dict:
2313
+ if isinstance(ai_response, dict):
2390
2314
  is_image = ai_response.get("image") is not None
2391
2315
  else:
2392
2316
  ai_response = raw_response.text
@@ -2533,12 +2457,12 @@ async def aschedule_automation(
2533
2457
 
2534
2458
  def construct_automation_created_message(automation: Job, crontime: str, query_to_run: str, subject: str):
2535
2459
  # Display next run time in user timezone instead of UTC
2536
- schedule = f'{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime("%Z")}'
2460
+ schedule = f"{cron_descriptor.get_description(crontime)} {automation.next_run_time.strftime('%Z')}"
2537
2461
  next_run_time = automation.next_run_time.strftime("%Y-%m-%d %I:%M %p %Z")
2538
2462
  # Remove /automated_task prefix from inferred_query
2539
2463
  unprefixed_query_to_run = re.sub(r"^\/automated_task\s*", "", query_to_run)
2540
2464
  # Create the automation response
2541
- automation_icon_url = f"/static/assets/icons/automation.svg"
2465
+ automation_icon_url = "/static/assets/icons/automation.svg"
2542
2466
  return f"""
2543
2467
  ### ![]({automation_icon_url}) Created Automation
2544
2468
  - Subject: **{subject}**
@@ -2786,19 +2710,20 @@ def configure_content(
2786
2710
  t: Optional[state.SearchType] = state.SearchType.All,
2787
2711
  ) -> bool:
2788
2712
  success = True
2789
- if t == None:
2713
+ if t is None:
2790
2714
  t = state.SearchType.All
2791
2715
 
2792
2716
  if t is not None and t in [type.value for type in state.SearchType]:
2793
2717
  t = state.SearchType(t)
2794
2718
 
2795
- if t is not None and not t.value in [type.value for type in state.SearchType]:
2719
+ if t is not None and t.value not in [type.value for type in state.SearchType]:
2796
2720
  logger.warning(f"🚨 Invalid search type: {t}")
2797
2721
  return False
2798
2722
 
2799
2723
  search_type = t.value if t else None
2800
2724
 
2801
- no_documents = all([not files.get(file_type) for file_type in files])
2725
+ # Check if client sent any documents of the supported types
2726
+ no_client_sent_documents = all([not files.get(file_type) for file_type in files])
2802
2727
 
2803
2728
  if files is None:
2804
2729
  logger.warning(f"🚨 No files to process for {search_type} search.")
@@ -2872,7 +2797,8 @@ def configure_content(
2872
2797
  success = False
2873
2798
 
2874
2799
  try:
2875
- if no_documents:
2800
+ # Run server side indexing of user Github docs if no client sent documents
2801
+ if no_client_sent_documents:
2876
2802
  github_config = GithubConfig.objects.filter(user=user).prefetch_related("githubrepoconfig").first()
2877
2803
  if (
2878
2804
  search_type == state.SearchType.All.value or search_type == state.SearchType.Github.value
@@ -2892,7 +2818,8 @@ def configure_content(
2892
2818
  success = False
2893
2819
 
2894
2820
  try:
2895
- if no_documents:
2821
+ # Run server side indexing of user Notion docs if no client sent documents
2822
+ if no_client_sent_documents:
2896
2823
  # Initialize Notion Search
2897
2824
  notion_config = NotionConfig.objects.filter(user=user).first()
2898
2825
  if (
@@ -3058,7 +2985,7 @@ async def grep_files(
3058
2985
  query += f" {' and '.join(context_info)}"
3059
2986
  if line_count > max_results:
3060
2987
  if lines_before or lines_after:
3061
- query += f" for"
2988
+ query += " for"
3062
2989
  query += f" first {max_results} results"
3063
2990
  return query
3064
2991
 
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/cli.py CHANGED
@@ -1,36 +1,19 @@
1
1
  import argparse
2
2
  import logging
3
- import os
4
3
  import pathlib
5
4
  from importlib.metadata import version
6
5
 
7
6
  logger = logging.getLogger(__name__)
8
7
 
9
- from khoj.migrations.migrate_offline_chat_default_model import (
10
- migrate_offline_chat_default_model,
11
- )
12
- from khoj.migrations.migrate_offline_chat_schema import migrate_offline_chat_schema
13
- from khoj.migrations.migrate_offline_model import migrate_offline_model
14
- from khoj.migrations.migrate_processor_config_openai import (
15
- migrate_processor_conversation_schema,
16
- )
17
- from khoj.migrations.migrate_server_pg import migrate_server_pg
18
- from khoj.migrations.migrate_version import migrate_config_to_version
19
- from khoj.utils.helpers import is_env_var_true, resolve_absolute_path
20
- from khoj.utils.yaml import parse_config_from_file
21
-
22
8
 
23
9
  def cli(args=None):
24
10
  # Setup Argument Parser for the Commandline Interface
25
11
  parser = argparse.ArgumentParser(description="Start Khoj; An AI personal assistant for your Digital Brain")
26
12
  parser.add_argument(
27
- "--config-file", default="~/.khoj/khoj.yml", type=pathlib.Path, help="YAML file to configure Khoj"
28
- )
29
- parser.add_argument(
30
- "--regenerate",
31
- action="store_true",
32
- default=False,
33
- help="Regenerate model embeddings from source files. Default: false",
13
+ "--log-file",
14
+ default="~/.khoj/khoj.log",
15
+ type=pathlib.Path,
16
+ help="File path for server logs. Default: ~/.khoj/khoj.log",
34
17
  )
35
18
  parser.add_argument("--verbose", "-v", action="count", default=0, help="Show verbose conversion logs. Default: 0")
36
19
  parser.add_argument("--host", type=str, default="127.0.0.1", help="Host address of the server. Default: 127.0.0.1")
@@ -43,14 +26,11 @@ def cli(args=None):
43
26
  parser.add_argument("--sslcert", type=str, help="Path to SSL certificate file")
44
27
  parser.add_argument("--sslkey", type=str, help="Path to SSL key file")
45
28
  parser.add_argument("--version", "-V", action="store_true", help="Print the installed Khoj version and exit")
46
- parser.add_argument(
47
- "--disable-chat-on-gpu", action="store_true", default=False, help="Disable using GPU for the offline chat model"
48
- )
49
29
  parser.add_argument(
50
30
  "--anonymous-mode",
51
31
  action="store_true",
52
32
  default=False,
53
- help="Run Khoj in anonymous mode. This does not require any login for connecting users.",
33
+ help="Run Khoj in single user mode with no login required. Useful for personal use or testing.",
54
34
  )
55
35
  parser.add_argument(
56
36
  "--non-interactive",
@@ -64,38 +44,10 @@ def cli(args=None):
64
44
  if len(remaining_args) > 0:
65
45
  logger.info(f"⚠️ Ignoring unknown commandline args: {remaining_args}")
66
46
 
67
- # Set default values for arguments
68
- args.chat_on_gpu = not args.disable_chat_on_gpu
69
-
70
47
  args.version_no = version("khoj")
71
48
  if args.version:
72
49
  # Show version of khoj installed and exit
73
50
  print(args.version_no)
74
51
  exit(0)
75
52
 
76
- # Normalize config_file path to absolute path
77
- args.config_file = resolve_absolute_path(args.config_file)
78
-
79
- if not args.config_file.exists():
80
- args.config = None
81
- else:
82
- args = run_migrations(args)
83
- args.config = parse_config_from_file(args.config_file)
84
- if is_env_var_true("KHOJ_TELEMETRY_DISABLE"):
85
- args.config.app.should_log_telemetry = False
86
-
87
- return args
88
-
89
-
90
- def run_migrations(args):
91
- migrations = [
92
- migrate_config_to_version,
93
- migrate_processor_conversation_schema,
94
- migrate_offline_model,
95
- migrate_offline_chat_schema,
96
- migrate_offline_chat_default_model,
97
- migrate_server_pg,
98
- ]
99
- for migration in migrations:
100
- args = migration(args)
101
53
  return args
khoj/utils/config.py CHANGED
@@ -1,22 +1,7 @@
1
1
  # System Packages
2
2
  from __future__ import annotations # to avoid quoting type hints
3
3
 
4
- import logging
5
- from dataclasses import dataclass
6
4
  from enum import Enum
7
- from typing import TYPE_CHECKING, Any, List, Optional, Union
8
-
9
- import torch
10
-
11
- from khoj.processor.conversation.offline.utils import download_model
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- if TYPE_CHECKING:
17
- from sentence_transformers import CrossEncoder
18
-
19
- from khoj.utils.models import BaseEncoder
20
5
 
21
6
 
22
7
  class SearchType(str, Enum):
@@ -29,53 +14,3 @@ class SearchType(str, Enum):
29
14
  Notion = "notion"
30
15
  Plaintext = "plaintext"
31
16
  Docx = "docx"
32
-
33
-
34
- class ProcessorType(str, Enum):
35
- Conversation = "conversation"
36
-
37
-
38
- @dataclass
39
- class TextContent:
40
- enabled: bool
41
-
42
-
43
- @dataclass
44
- class ImageContent:
45
- image_names: List[str]
46
- image_embeddings: torch.Tensor
47
- image_metadata_embeddings: torch.Tensor
48
-
49
-
50
- @dataclass
51
- class TextSearchModel:
52
- bi_encoder: BaseEncoder
53
- cross_encoder: Optional[CrossEncoder] = None
54
- top_k: Optional[int] = 15
55
-
56
-
57
- @dataclass
58
- class ImageSearchModel:
59
- image_encoder: BaseEncoder
60
-
61
-
62
- @dataclass
63
- class SearchModels:
64
- text_search: Optional[TextSearchModel] = None
65
-
66
-
67
- @dataclass
68
- class OfflineChatProcessorConfig:
69
- loaded_model: Union[Any, None] = None
70
-
71
-
72
- class OfflineChatProcessorModel:
73
- def __init__(self, chat_model: str = "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF", max_tokens: int = None):
74
- self.chat_model = chat_model
75
- self.loaded_model = None
76
- try:
77
- self.loaded_model = download_model(self.chat_model, max_tokens=max_tokens)
78
- except ValueError as e:
79
- self.loaded_model = None
80
- logger.error(f"Error while loading offline chat model: {e}", exc_info=True)
81
- raise e
khoj/utils/constants.py CHANGED
@@ -10,13 +10,6 @@ empty_escape_sequences = "\n|\r|\t| "
10
10
  app_env_filepath = "~/.khoj/env"
11
11
  telemetry_server = "https://khoj.beta.haletic.com/v1/telemetry"
12
12
  content_directory = "~/.khoj/content/"
13
- default_offline_chat_models = [
14
- "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
15
- "bartowski/Llama-3.2-3B-Instruct-GGUF",
16
- "bartowski/gemma-2-9b-it-GGUF",
17
- "bartowski/gemma-2-2b-it-GGUF",
18
- "bartowski/Qwen2.5-14B-Instruct-GGUF",
19
- ]
20
13
  default_openai_chat_models = ["gpt-4o-mini", "gpt-4.1", "o3", "o4-mini"]
21
14
  default_gemini_chat_models = ["gemini-2.0-flash", "gemini-2.5-flash-preview-05-20", "gemini-2.5-pro-preview-06-05"]
22
15
  default_anthropic_chat_models = ["claude-sonnet-4-0", "claude-3-5-haiku-latest"]
khoj/utils/helpers.py CHANGED
@@ -47,7 +47,6 @@ if TYPE_CHECKING:
47
47
  from sentence_transformers import CrossEncoder, SentenceTransformer
48
48
 
49
49
  from khoj.utils.models import BaseEncoder
50
- from khoj.utils.rawconfig import AppConfig
51
50
 
52
51
  logger = logging.getLogger(__name__)
53
52
 
@@ -78,7 +77,7 @@ class AsyncIteratorWrapper:
78
77
 
79
78
 
80
79
  def is_none_or_empty(item):
81
- 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 == ""
82
81
 
83
82
 
84
83
  def to_snake_case_from_dash(item: str):
@@ -98,7 +97,7 @@ def get_from_dict(dictionary, *args):
98
97
  Returns: dictionary[args[0]][args[1]]... or None if any keys missing"""
99
98
  current = dictionary
100
99
  for arg in args:
101
- if not hasattr(current, "__iter__") or not arg in current:
100
+ if not hasattr(current, "__iter__") or arg not in current:
102
101
  return None
103
102
  current = current[arg]
104
103
  return current
@@ -267,23 +266,16 @@ def get_server_id():
267
266
  return server_id
268
267
 
269
268
 
270
- def telemetry_disabled(app_config: AppConfig, telemetry_disable_env) -> bool:
271
- if telemetry_disable_env is True:
272
- return True
273
- return not app_config or not app_config.should_log_telemetry
274
-
275
-
276
269
  def log_telemetry(
277
270
  telemetry_type: str,
278
271
  api: str = None,
279
272
  client: Optional[str] = None,
280
- app_config: Optional[AppConfig] = None,
281
273
  disable_telemetry_env: bool = False,
282
274
  properties: dict = None,
283
275
  ):
284
276
  """Log basic app usage telemetry like client, os, api called"""
285
277
  # Do not log usage telemetry, if telemetry is disabled via app config
286
- if telemetry_disabled(app_config, disable_telemetry_env):
278
+ if disable_telemetry_env:
287
279
  return []
288
280
 
289
281
  if properties.get("server_id") is None:
@@ -759,7 +751,7 @@ def is_valid_url(url: str) -> bool:
759
751
  try:
760
752
  result = urlparse(url.strip())
761
753
  return all([result.scheme, result.netloc])
762
- except:
754
+ except Exception:
763
755
  return False
764
756
 
765
757
 
@@ -767,7 +759,7 @@ def is_internet_connected():
767
759
  try:
768
760
  response = requests.head("https://www.google.com")
769
761
  return response.status_code == 200
770
- except:
762
+ except Exception:
771
763
  return False
772
764
 
773
765