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.
- khoj/app/README.md +1 -1
- khoj/app/urls.py +1 -0
- khoj/database/adapters/__init__.py +4 -4
- khoj/database/management/commands/delete_orphaned_fileobjects.py +0 -1
- khoj/database/migrations/0064_remove_conversation_temp_id_alter_conversation_id.py +1 -1
- khoj/database/migrations/0075_migrate_generated_assets_and_validate.py +1 -1
- khoj/database/models/__init__.py +6 -6
- khoj/database/tests.py +0 -2
- khoj/interface/compiled/404/index.html +2 -2
- khoj/interface/compiled/_next/static/chunks/{9245.a04e92d034540234.js → 1225.ecac11e7421504c4.js} +3 -3
- khoj/interface/compiled/_next/static/chunks/1320.ae930ad00affe685.js +5 -0
- khoj/interface/compiled/_next/static/chunks/{1327-3b1a41af530fa8ee.js → 1327-e254819a9172cfa7.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/1626.15a8acc0d6639ec6.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{3489.c523fe96a2eee74f.js → 1940.d082758bd04e08ae.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{2327-ea623ca2d22f78e9.js → 2327-438aaec1657c5ada.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/2475.57a0d0fd93d07af0.js +93 -0
- khoj/interface/compiled/_next/static/chunks/2481.5ce6524ba0a73f90.js +55 -0
- khoj/interface/compiled/_next/static/chunks/297.4c4c823ff6e3255b.js +174 -0
- khoj/interface/compiled/_next/static/chunks/{5639-09e2009a2adedf8b.js → 3260-82d2521fab032ff1.js} +68 -23
- khoj/interface/compiled/_next/static/chunks/3353.1c6d553216a1acae.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3855.f7b8131f78af046e.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3973.dc54a39586ab48be.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4241.c1cd170f7f37ac59.js +24 -0
- khoj/interface/compiled/_next/static/chunks/{4327.8d2a1b8f1ea78208.js → 4327.f3704dc398c67113.js} +19 -19
- khoj/interface/compiled/_next/static/chunks/4505.f09454a346269c3f.js +117 -0
- khoj/interface/compiled/_next/static/chunks/4801.96a152d49742b644.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5427-a95ec748e52abb75.js +1 -0
- khoj/interface/compiled/_next/static/chunks/549.2bd27f59a91a9668.js +148 -0
- khoj/interface/compiled/_next/static/chunks/5765.71b1e1207b76b03f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/584.d7ce3505f169b706.js +1 -0
- khoj/interface/compiled/_next/static/chunks/6240.34f7c1fa692edd61.js +24 -0
- khoj/interface/compiled/_next/static/chunks/6d3fe5a5-f9f3c16e0bc0cdf9.js +10 -0
- khoj/interface/compiled/_next/static/chunks/{7127-0f4a2a77d97fb5fa.js → 7127-97b83757db125ba6.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/7200-93ab0072359b8028.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{2612.bcf5a623b3da209e.js → 7553.f5ad54b1f6e92c49.js} +2 -2
- khoj/interface/compiled/_next/static/chunks/7626-1b630f1654172341.js +1 -0
- khoj/interface/compiled/_next/static/chunks/764.dadd316e8e16d191.js +63 -0
- khoj/interface/compiled/_next/static/chunks/78.08169ab541abab4f.js +43 -0
- khoj/interface/compiled/_next/static/chunks/784.e03acf460df213d1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{9537-d9ab442ce15d1e20.js → 8072-e1440cb482a0940e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{3265.924139c4146ee344.js → 8086.8d39887215807fcd.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/8168.f074ab8c7c16d82d.js +59 -0
- khoj/interface/compiled/_next/static/chunks/{8694.2bd9c2f65d8c5847.js → 8223.1705878fa7a09292.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/8483.94f6c9e2bee86f50.js +215 -0
- khoj/interface/compiled/_next/static/chunks/{8888.ebe0e552b59e7fed.js → 8810.fc0e479de78c7c61.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/8828.bc74dc4ce94e78f6.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{7303.d0612f812a967a08.js → 8909.14ac3f43d0070cf1.js} +5 -5
- khoj/interface/compiled/_next/static/chunks/90542734.b1a1629065ba199b.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9167.098534184f03fe92.js +56 -0
- khoj/interface/compiled/_next/static/chunks/{4980.63500d68b3bb1222.js → 9537.e934ce37bf314509.js} +5 -5
- khoj/interface/compiled/_next/static/chunks/9574.3fe8e26e95bf1c34.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9599.ec50b5296c27dae9.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9643.b34248df52ffc77c.js +262 -0
- khoj/interface/compiled/_next/static/chunks/9747.2fd9065b1435abb1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9922.98f2b2a9959b4ebe.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-e00fb81dca656a10.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-e291b49977f43880.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/page-198b26df6e09bbb0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-33934fc2d6ae6838.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/{page-8e1c4f2af3c9429e.js → page-dfcc1e8e2ad62873.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-2b3056cba8aa96ce.js → page-1567cac7b79a7c59.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-8be3b35178abf2ec.js → page-6081362437c82470.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-4a4b0c0f4749c2b2.js → page-e0dcb1762f8c8f88.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/webpack-5393aad3d824e0cb.js +1 -0
- khoj/interface/compiled/_next/static/css/{2945c4a857922f3b.css → c34713c98384ee87.css} +1 -1
- khoj/interface/compiled/agents/index.html +2 -2
- khoj/interface/compiled/agents/index.txt +3 -3
- khoj/interface/compiled/automations/index.html +2 -2
- khoj/interface/compiled/automations/index.txt +4 -4
- khoj/interface/compiled/chat/index.html +2 -2
- khoj/interface/compiled/chat/index.txt +3 -3
- khoj/interface/compiled/index.html +2 -2
- khoj/interface/compiled/index.txt +3 -3
- khoj/interface/compiled/search/index.html +2 -2
- khoj/interface/compiled/search/index.txt +3 -3
- khoj/interface/compiled/settings/index.html +2 -2
- khoj/interface/compiled/settings/index.txt +5 -5
- khoj/interface/compiled/share/chat/index.html +2 -2
- khoj/interface/compiled/share/chat/index.txt +3 -3
- khoj/main.py +3 -3
- khoj/manage.py +1 -0
- khoj/processor/content/github/github_to_entries.py +6 -6
- khoj/processor/content/images/image_to_entries.py +0 -1
- khoj/processor/content/markdown/markdown_to_entries.py +2 -3
- khoj/processor/content/notion/notion_to_entries.py +5 -5
- khoj/processor/content/org_mode/org_to_entries.py +4 -5
- khoj/processor/content/org_mode/orgnode.py +4 -4
- khoj/processor/content/plaintext/plaintext_to_entries.py +1 -2
- khoj/processor/content/text_to_entries.py +1 -2
- khoj/processor/conversation/google/utils.py +3 -3
- khoj/processor/conversation/openai/gpt.py +65 -28
- khoj/processor/conversation/openai/utils.py +358 -22
- khoj/processor/conversation/prompts.py +11 -5
- khoj/processor/conversation/utils.py +20 -11
- khoj/processor/embeddings.py +0 -2
- khoj/processor/image/generate.py +3 -3
- khoj/processor/operator/__init__.py +2 -2
- khoj/processor/operator/grounding_agent.py +15 -2
- khoj/processor/operator/grounding_agent_uitars.py +34 -23
- khoj/processor/operator/operator_agent_anthropic.py +29 -4
- khoj/processor/operator/operator_agent_base.py +1 -1
- khoj/processor/operator/operator_agent_binary.py +4 -4
- khoj/processor/operator/operator_agent_openai.py +21 -6
- khoj/processor/operator/operator_environment_browser.py +1 -1
- khoj/processor/operator/operator_environment_computer.py +1 -1
- khoj/processor/speech/text_to_speech.py +0 -1
- khoj/processor/tools/online_search.py +1 -1
- khoj/processor/tools/run_code.py +1 -1
- khoj/routers/api.py +1 -2
- khoj/routers/api_agents.py +1 -2
- khoj/routers/api_automation.py +1 -1
- khoj/routers/api_chat.py +10 -16
- khoj/routers/api_model.py +0 -1
- khoj/routers/api_subscription.py +1 -1
- khoj/routers/email.py +4 -4
- khoj/routers/helpers.py +35 -24
- khoj/routers/research.py +2 -4
- khoj/search_filter/base_filter.py +2 -4
- khoj/search_type/text_search.py +1 -2
- khoj/utils/constants.py +3 -0
- khoj/utils/helpers.py +4 -4
- khoj/utils/initialization.py +1 -3
- khoj/utils/models.py +2 -4
- khoj/utils/rawconfig.py +1 -2
- khoj/utils/state.py +1 -1
- {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/METADATA +3 -2
- {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/RECORD +139 -137
- khoj/interface/compiled/_next/static/chunks/1191.b547ec13349b4aed.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1588.f0558a0bdffc4761.js +0 -117
- khoj/interface/compiled/_next/static/chunks/1918.925cb4a35518d258.js +0 -43
- khoj/interface/compiled/_next/static/chunks/2849.dc00ae5ba7219cfc.js +0 -1
- khoj/interface/compiled/_next/static/chunks/303.fe76de943e930fbd.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4533.586e74b45a2bde25.js +0 -55
- khoj/interface/compiled/_next/static/chunks/4551.82ce1476b5516bc2.js +0 -5
- khoj/interface/compiled/_next/static/chunks/4748.0edd37cba3ea2809.js +0 -59
- khoj/interface/compiled/_next/static/chunks/5210.cd35a1c1ec594a20.js +0 -93
- khoj/interface/compiled/_next/static/chunks/5329.f8b3c5b3d16159cd.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5427-13d6ffd380fdfab7.js +0 -1
- khoj/interface/compiled/_next/static/chunks/558-c14e76cff03f6a60.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5830.8876eccb82da9b7d.js +0 -262
- khoj/interface/compiled/_next/static/chunks/6230.88a71d8145347b3f.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7161.77e0530a40ad5ca8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7200-ac3b2e37ff30e126.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7505.c31027a3695bdebb.js +0 -148
- khoj/interface/compiled/_next/static/chunks/7760.35649cc21d9585bd.js +0 -56
- khoj/interface/compiled/_next/static/chunks/83.48e2db193a940052.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8427.844694e06133fb51.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8665.4db7e6b2e8933497.js +0 -174
- khoj/interface/compiled/_next/static/chunks/872.caf84cc1a39ae59f.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8890.6e8a59e4de6978bc.js +0 -215
- khoj/interface/compiled/_next/static/chunks/8950.5f2272e0ac923f9e.js +0 -1
- khoj/interface/compiled/_next/static/chunks/90542734.2c21f16f18b22411.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9202.c703864fcedc8d1f.js +0 -63
- khoj/interface/compiled/_next/static/chunks/9320.6aca4885d541aa44.js +0 -24
- khoj/interface/compiled/_next/static/chunks/9535.f78cd92d03331e55.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9968.b111fc002796da81.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-4e2a134ec26aa606.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-9a4610474cd59a71.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-f7bb9d777b7745d4.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-ad4d1792ab1a4108.js +0 -1
- khoj/interface/compiled/_next/static/chunks/f3e3247b-1758d4651e4457c2.js +0 -10
- khoj/interface/compiled/_next/static/chunks/webpack-ee14d29b64c5ab47.js +0 -1
- /khoj/interface/compiled/_next/static/{XfWrWDAk5VXeZ88OdP652 → Q7tm150g44Fs4H1CGytNf}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{XfWrWDAk5VXeZ88OdP652 → Q7tm150g44Fs4H1CGytNf}/_ssgManifest.js +0 -0
- /khoj/interface/compiled/_next/static/chunks/{1915-fbfe167c84ad60c5.js → 1915-5c6508f6ebb62a30.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{2117-e78b6902ad6f75ec.js → 2117-080746c8e170c81a.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{2939-4d4084c5b888b960.js → 2939-4af3fd24b8ffc9ad.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{4447-d6cf93724d57e34b.js → 4447-cd95608f8e93e711.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{8667-4b7790573b08c50d.js → 8667-50b03a89e82e0ba7.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{9139-ce1ae935dac9c871.js → 9139-8ac4d9feb10f8869.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/app/search/{page-4885df3cd175c957.js → page-3639e50ec3e9acfd.js} +0 -0
- {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/WHEEL +0 -0
- {khoj-2.0.0b13.dev5.dist-info → khoj-2.0.0b13.dev23.dist-info}/entry_points.txt +0 -0
- {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,
|
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
|
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
|
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
|
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
|
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(
|
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,
|
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,
|
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,
|
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
khoj/routers/api_subscription.py
CHANGED
@@ -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
|
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":
|
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
|
-
|
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":
|
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
|
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
|
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
|
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
|
-
|
1176
|
-
and
|
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
|
1303
|
+
"""Choose semantic search queries to run on user documents."""
|
1305
1304
|
|
1306
|
-
queries: List[str] = Field(
|
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(
|
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(
|
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(
|
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
|
1879
|
-
next_window = "tomorrow" if self.window == 60 * 60 * 24 else
|
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
|
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
|
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
|
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 =
|
2472
|
+
automation_icon_url = "/static/assets/icons/automation.svg"
|
2469
2473
|
return f"""
|
2470
2474
|
###  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
|
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
|
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 +=
|
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
|
-
|
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
|
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 =
|
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: ...
|
khoj/search_type/text_search.py
CHANGED
@@ -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
|
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
|
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
|
|
khoj/utils/initialization.py
CHANGED
@@ -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
khoj/utils/state.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: khoj
|
3
|
-
Version: 2.0.0b13.
|
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
|