khoj 1.24.2.dev2__py3-none-any.whl → 1.24.2.dev16__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/database/adapters/__init__.py +139 -16
- khoj/database/admin.py +2 -0
- khoj/database/migrations/0065_remove_agent_avatar_remove_agent_public_and_more.py +49 -0
- khoj/database/migrations/0066_remove_agent_tools_agent_input_tools_and_more.py +69 -0
- khoj/database/migrations/0067_alter_agent_style_icon.py +50 -0
- khoj/database/models/__init__.py +60 -18
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1269-2e52d48e7d0e5c61.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1603-67a89278e2c5dbe6.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2697-a38d01981ad3bdf8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3110-ef2cacd1b8d79ad8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4086-2c74808ba38a5a0f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9178-899fe9a6b754ecfe.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9417-29502e39c3e7d60c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9479-7eed36fc954ef804.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-df26b497b7356151.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/page-1688dead2f21270d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/page-91abcb71846922b7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-7ab093711c27041c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/page-fada198096eab47f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/page-a7e036689b6507ff.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/page-fa11cafaec7ab39f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-c5d2b9076e5390b2.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{webpack-878fd47921816d3c.js → webpack-f52083d548d804fa.js} +1 -1
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +1 -0
- khoj/interface/compiled/_next/static/css/50d972a8c787730b.css +25 -0
- khoj/interface/compiled/_next/static/css/dfb67a9287720a2b.css +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +2 -2
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +2 -2
- khoj/interface/compiled/factchecker/index.html +1 -1
- khoj/interface/compiled/factchecker/index.txt +2 -2
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +2 -2
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +2 -2
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +2 -2
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/processor/content/notion/notion_to_entries.py +2 -1
- khoj/processor/conversation/anthropic/anthropic_chat.py +2 -0
- khoj/processor/conversation/google/gemini_chat.py +2 -0
- khoj/processor/conversation/offline/chat_model.py +3 -1
- khoj/processor/conversation/openai/gpt.py +2 -0
- khoj/processor/conversation/prompts.py +56 -5
- khoj/processor/image/generate.py +3 -1
- khoj/processor/tools/online_search.py +9 -7
- khoj/routers/api.py +34 -5
- khoj/routers/api_agents.py +232 -4
- khoj/routers/api_chat.py +46 -17
- khoj/routers/api_content.py +14 -0
- khoj/routers/helpers.py +113 -13
- khoj/search_type/text_search.py +4 -1
- khoj/utils/helpers.py +15 -2
- {khoj-1.24.2.dev2.dist-info → khoj-1.24.2.dev16.dist-info}/METADATA +1 -8
- {khoj-1.24.2.dev2.dist-info → khoj-1.24.2.dev16.dist-info}/RECORD +67 -64
- khoj/interface/compiled/_next/static/chunks/1603-3e2e1528e3b6ea1d.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2697-a29cb9191a9e339c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/6648-ee109f4ea33a74e2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7071-b4711cecca6619a8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/743-1a64254447cda71f.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9162-0be016519a18568b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9178-409f672ab573b8fd.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9417-5d14ac74aaab2c66.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9984-e410179c6fac7cf1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-a3db5b3869f83937.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-e68cb1eba3cc41de.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-5b1626fc2882c1f9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-b01f8a9b9107ecbe.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/page-ee9ee504f0d5ace6.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-53c2494182551684.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-2a7e60e3782ed95e.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-9d9faa4a155bbf58.js +0 -1
- khoj/interface/compiled/_next/static/css/24f141a6e37cd204.css +0 -25
- khoj/interface/compiled/_next/static/css/3e1f1fdd70775091.css +0 -1
- khoj/interface/compiled/_next/static/css/60fc94dfe42ddfe9.css +0 -1
- /khoj/interface/compiled/_next/static/{sXEsDJ1Vi3HypDes8jcxW → MyYNlmGMz32TGV_-febR4}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{sXEsDJ1Vi3HypDes8jcxW → MyYNlmGMz32TGV_-febR4}/_ssgManifest.js +0 -0
- {khoj-1.24.2.dev2.dist-info → khoj-1.24.2.dev16.dist-info}/WHEEL +0 -0
- {khoj-1.24.2.dev2.dist-info → khoj-1.24.2.dev16.dist-info}/entry_points.txt +0 -0
- {khoj-1.24.2.dev2.dist-info → khoj-1.24.2.dev16.dist-info}/licenses/LICENSE +0 -0
khoj/routers/api_content.py
CHANGED
@@ -2,11 +2,13 @@ import asyncio
|
|
2
2
|
import json
|
3
3
|
import logging
|
4
4
|
import math
|
5
|
+
from concurrent.futures import ThreadPoolExecutor
|
5
6
|
from typing import Dict, List, Optional, Union
|
6
7
|
|
7
8
|
from asgiref.sync import sync_to_async
|
8
9
|
from fastapi import (
|
9
10
|
APIRouter,
|
11
|
+
BackgroundTasks,
|
10
12
|
Depends,
|
11
13
|
Header,
|
12
14
|
HTTPException,
|
@@ -58,6 +60,8 @@ logger = logging.getLogger(__name__)
|
|
58
60
|
|
59
61
|
api_content = APIRouter()
|
60
62
|
|
63
|
+
executor = ThreadPoolExecutor()
|
64
|
+
|
61
65
|
|
62
66
|
class File(BaseModel):
|
63
67
|
path: str
|
@@ -77,6 +81,11 @@ class IndexerInput(BaseModel):
|
|
77
81
|
docx: Optional[dict[str, bytes]] = None
|
78
82
|
|
79
83
|
|
84
|
+
async def run_in_executor(func, *args):
|
85
|
+
loop = asyncio.get_event_loop()
|
86
|
+
return await loop.run_in_executor(executor, func, *args)
|
87
|
+
|
88
|
+
|
80
89
|
@api_content.put("")
|
81
90
|
@requires(["authenticated"])
|
82
91
|
async def put_content(
|
@@ -209,6 +218,7 @@ async def set_content_github(
|
|
209
218
|
@requires(["authenticated"])
|
210
219
|
async def set_content_notion(
|
211
220
|
request: Request,
|
221
|
+
background_tasks: BackgroundTasks,
|
212
222
|
updated_config: Union[NotionContentConfig, None],
|
213
223
|
client: Optional[str] = None,
|
214
224
|
):
|
@@ -225,6 +235,10 @@ async def set_content_notion(
|
|
225
235
|
logger.error(e, exc_info=True)
|
226
236
|
raise HTTPException(status_code=500, detail="Failed to set Notion config")
|
227
237
|
|
238
|
+
if updated_config.token:
|
239
|
+
# Trigger an async job to configure_content. Let it run without blocking the response.
|
240
|
+
background_tasks.add_task(run_in_executor, configure_content, {}, False, SearchType.Notion, user)
|
241
|
+
|
228
242
|
update_telemetry_state(
|
229
243
|
request=request,
|
230
244
|
telemetry_type="api",
|
khoj/routers/helpers.py
CHANGED
@@ -47,6 +47,7 @@ from khoj.database.adapters import (
|
|
47
47
|
run_with_process_lock,
|
48
48
|
)
|
49
49
|
from khoj.database.models import (
|
50
|
+
Agent,
|
50
51
|
ChatModelOptions,
|
51
52
|
ClientApplication,
|
52
53
|
Conversation,
|
@@ -257,8 +258,39 @@ async def acreate_title_from_query(query: str) -> str:
|
|
257
258
|
return response.strip()
|
258
259
|
|
259
260
|
|
261
|
+
async def acheck_if_safe_prompt(system_prompt: str) -> Tuple[bool, str]:
|
262
|
+
"""
|
263
|
+
Check if the system prompt is safe to use
|
264
|
+
"""
|
265
|
+
safe_prompt_check = prompts.personality_prompt_safety_expert.format(prompt=system_prompt)
|
266
|
+
is_safe = True
|
267
|
+
reason = ""
|
268
|
+
|
269
|
+
with timer("Chat actor: Check if safe prompt", logger):
|
270
|
+
response = await send_message_to_model_wrapper(safe_prompt_check)
|
271
|
+
|
272
|
+
response = response.strip()
|
273
|
+
try:
|
274
|
+
response = json.loads(response)
|
275
|
+
is_safe = response.get("safe", "True") == "True"
|
276
|
+
if not is_safe:
|
277
|
+
reason = response.get("reason", "")
|
278
|
+
except Exception:
|
279
|
+
logger.error(f"Invalid response for checking safe prompt: {response}")
|
280
|
+
|
281
|
+
if not is_safe:
|
282
|
+
logger.error(f"Unsafe prompt: {system_prompt}. Reason: {reason}")
|
283
|
+
|
284
|
+
return is_safe, reason
|
285
|
+
|
286
|
+
|
260
287
|
async def aget_relevant_information_sources(
|
261
|
-
query: str,
|
288
|
+
query: str,
|
289
|
+
conversation_history: dict,
|
290
|
+
is_task: bool,
|
291
|
+
subscribed: bool,
|
292
|
+
uploaded_image_url: str = None,
|
293
|
+
agent: Agent = None,
|
262
294
|
):
|
263
295
|
"""
|
264
296
|
Given a query, determine which of the available tools the agent should use in order to answer appropriately.
|
@@ -267,19 +299,27 @@ async def aget_relevant_information_sources(
|
|
267
299
|
tool_options = dict()
|
268
300
|
tool_options_str = ""
|
269
301
|
|
302
|
+
agent_tools = agent.input_tools if agent else []
|
303
|
+
|
270
304
|
for tool, description in tool_descriptions_for_llm.items():
|
271
305
|
tool_options[tool.value] = description
|
272
|
-
|
306
|
+
if len(agent_tools) == 0 or tool.value in agent_tools:
|
307
|
+
tool_options_str += f'- "{tool.value}": "{description}"\n'
|
273
308
|
|
274
309
|
chat_history = construct_chat_history(conversation_history)
|
275
310
|
|
276
311
|
if uploaded_image_url:
|
277
312
|
query = f"[placeholder for user attached image]\n{query}"
|
278
313
|
|
314
|
+
personality_context = (
|
315
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
316
|
+
)
|
317
|
+
|
279
318
|
relevant_tools_prompt = prompts.pick_relevant_information_collection_tools.format(
|
280
319
|
query=query,
|
281
320
|
tools=tool_options_str,
|
282
321
|
chat_history=chat_history,
|
322
|
+
personality_context=personality_context,
|
283
323
|
)
|
284
324
|
|
285
325
|
with timer("Chat actor: Infer information sources to refer", logger):
|
@@ -300,7 +340,10 @@ async def aget_relevant_information_sources(
|
|
300
340
|
|
301
341
|
final_response = [] if not is_task else [ConversationCommand.AutomatedTask]
|
302
342
|
for llm_suggested_tool in response:
|
303
|
-
|
343
|
+
# Add a double check to verify it's in the agent list, because the LLM sometimes gets confused by the tool options.
|
344
|
+
if llm_suggested_tool in tool_options.keys() and (
|
345
|
+
len(agent_tools) == 0 or llm_suggested_tool in agent_tools
|
346
|
+
):
|
304
347
|
# Check whether the tool exists as a valid ConversationCommand
|
305
348
|
final_response.append(ConversationCommand(llm_suggested_tool))
|
306
349
|
|
@@ -313,7 +356,7 @@ async def aget_relevant_information_sources(
|
|
313
356
|
|
314
357
|
|
315
358
|
async def aget_relevant_output_modes(
|
316
|
-
query: str, conversation_history: dict, is_task: bool = False, uploaded_image_url: str = None
|
359
|
+
query: str, conversation_history: dict, is_task: bool = False, uploaded_image_url: str = None, agent: Agent = None
|
317
360
|
):
|
318
361
|
"""
|
319
362
|
Given a query, determine which of the available tools the agent should use in order to answer appropriately.
|
@@ -322,22 +365,30 @@ async def aget_relevant_output_modes(
|
|
322
365
|
mode_options = dict()
|
323
366
|
mode_options_str = ""
|
324
367
|
|
368
|
+
output_modes = agent.output_modes if agent else []
|
369
|
+
|
325
370
|
for mode, description in mode_descriptions_for_llm.items():
|
326
371
|
# Do not allow tasks to schedule another task
|
327
372
|
if is_task and mode == ConversationCommand.Automation:
|
328
373
|
continue
|
329
374
|
mode_options[mode.value] = description
|
330
|
-
|
375
|
+
if len(output_modes) == 0 or mode.value in output_modes:
|
376
|
+
mode_options_str += f'- "{mode.value}": "{description}"\n'
|
331
377
|
|
332
378
|
chat_history = construct_chat_history(conversation_history)
|
333
379
|
|
334
380
|
if uploaded_image_url:
|
335
381
|
query = f"[placeholder for user attached image]\n{query}"
|
336
382
|
|
383
|
+
personality_context = (
|
384
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
385
|
+
)
|
386
|
+
|
337
387
|
relevant_mode_prompt = prompts.pick_relevant_output_mode.format(
|
338
388
|
query=query,
|
339
389
|
modes=mode_options_str,
|
340
390
|
chat_history=chat_history,
|
391
|
+
personality_context=personality_context,
|
341
392
|
)
|
342
393
|
|
343
394
|
with timer("Chat actor: Infer output mode for chat response", logger):
|
@@ -352,7 +403,9 @@ async def aget_relevant_output_modes(
|
|
352
403
|
return ConversationCommand.Text
|
353
404
|
|
354
405
|
output_mode = response["output"]
|
355
|
-
|
406
|
+
|
407
|
+
# Add a double check to verify it's in the agent list, because the LLM sometimes gets confused by the tool options.
|
408
|
+
if output_mode in mode_options.keys() and (len(output_modes) == 0 or output_mode in output_modes):
|
356
409
|
# Check whether the tool exists as a valid ConversationCommand
|
357
410
|
return ConversationCommand(output_mode)
|
358
411
|
|
@@ -364,7 +417,12 @@ async def aget_relevant_output_modes(
|
|
364
417
|
|
365
418
|
|
366
419
|
async def infer_webpage_urls(
|
367
|
-
q: str,
|
420
|
+
q: str,
|
421
|
+
conversation_history: dict,
|
422
|
+
location_data: LocationData,
|
423
|
+
user: KhojUser,
|
424
|
+
uploaded_image_url: str = None,
|
425
|
+
agent: Agent = None,
|
368
426
|
) -> List[str]:
|
369
427
|
"""
|
370
428
|
Infer webpage links from the given query
|
@@ -374,12 +432,17 @@ async def infer_webpage_urls(
|
|
374
432
|
chat_history = construct_chat_history(conversation_history)
|
375
433
|
|
376
434
|
utc_date = datetime.utcnow().strftime("%Y-%m-%d")
|
435
|
+
personality_context = (
|
436
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
437
|
+
)
|
438
|
+
|
377
439
|
online_queries_prompt = prompts.infer_webpages_to_read.format(
|
378
440
|
current_date=utc_date,
|
379
441
|
query=q,
|
380
442
|
chat_history=chat_history,
|
381
443
|
location=location,
|
382
444
|
username=username,
|
445
|
+
personality_context=personality_context,
|
383
446
|
)
|
384
447
|
|
385
448
|
with timer("Chat actor: Infer webpage urls to read", logger):
|
@@ -400,7 +463,12 @@ async def infer_webpage_urls(
|
|
400
463
|
|
401
464
|
|
402
465
|
async def generate_online_subqueries(
|
403
|
-
q: str,
|
466
|
+
q: str,
|
467
|
+
conversation_history: dict,
|
468
|
+
location_data: LocationData,
|
469
|
+
user: KhojUser,
|
470
|
+
uploaded_image_url: str = None,
|
471
|
+
agent: Agent = None,
|
404
472
|
) -> List[str]:
|
405
473
|
"""
|
406
474
|
Generate subqueries from the given query
|
@@ -410,12 +478,17 @@ async def generate_online_subqueries(
|
|
410
478
|
chat_history = construct_chat_history(conversation_history)
|
411
479
|
|
412
480
|
utc_date = datetime.utcnow().strftime("%Y-%m-%d")
|
481
|
+
personality_context = (
|
482
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
483
|
+
)
|
484
|
+
|
413
485
|
online_queries_prompt = prompts.online_search_conversation_subqueries.format(
|
414
486
|
current_date=utc_date,
|
415
487
|
query=q,
|
416
488
|
chat_history=chat_history,
|
417
489
|
location=location,
|
418
490
|
username=username,
|
491
|
+
personality_context=personality_context,
|
419
492
|
)
|
420
493
|
|
421
494
|
with timer("Chat actor: Generate online search subqueries", logger):
|
@@ -464,7 +537,7 @@ async def schedule_query(q: str, conversation_history: dict, uploaded_image_url:
|
|
464
537
|
raise AssertionError(f"Invalid response for scheduling query: {raw_response}")
|
465
538
|
|
466
539
|
|
467
|
-
async def extract_relevant_info(q: str, corpus: str, subscribed: bool) -> Union[str, None]:
|
540
|
+
async def extract_relevant_info(q: str, corpus: str, subscribed: bool, agent: Agent = None) -> Union[str, None]:
|
468
541
|
"""
|
469
542
|
Extract relevant information for a given query from the target corpus
|
470
543
|
"""
|
@@ -472,9 +545,14 @@ async def extract_relevant_info(q: str, corpus: str, subscribed: bool) -> Union[
|
|
472
545
|
if is_none_or_empty(corpus) or is_none_or_empty(q):
|
473
546
|
return None
|
474
547
|
|
548
|
+
personality_context = (
|
549
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
550
|
+
)
|
551
|
+
|
475
552
|
extract_relevant_information = prompts.extract_relevant_information.format(
|
476
553
|
query=q,
|
477
554
|
corpus=corpus.strip(),
|
555
|
+
personality_context=personality_context,
|
478
556
|
)
|
479
557
|
|
480
558
|
chat_model: ChatModelOptions = await ConversationAdapters.aget_default_conversation_config()
|
@@ -490,7 +568,7 @@ async def extract_relevant_info(q: str, corpus: str, subscribed: bool) -> Union[
|
|
490
568
|
|
491
569
|
|
492
570
|
async def extract_relevant_summary(
|
493
|
-
q: str, corpus: str, subscribed: bool = False, uploaded_image_url: str = None
|
571
|
+
q: str, corpus: str, subscribed: bool = False, uploaded_image_url: str = None, agent: Agent = None
|
494
572
|
) -> Union[str, None]:
|
495
573
|
"""
|
496
574
|
Extract relevant information for a given query from the target corpus
|
@@ -499,9 +577,14 @@ async def extract_relevant_summary(
|
|
499
577
|
if is_none_or_empty(corpus) or is_none_or_empty(q):
|
500
578
|
return None
|
501
579
|
|
580
|
+
personality_context = (
|
581
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
582
|
+
)
|
583
|
+
|
502
584
|
extract_relevant_information = prompts.extract_relevant_summary.format(
|
503
585
|
query=q,
|
504
586
|
corpus=corpus.strip(),
|
587
|
+
personality_context=personality_context,
|
505
588
|
)
|
506
589
|
|
507
590
|
chat_model: ChatModelOptions = await ConversationAdapters.aget_default_conversation_config()
|
@@ -526,12 +609,16 @@ async def generate_better_image_prompt(
|
|
526
609
|
model_type: Optional[str] = None,
|
527
610
|
subscribed: bool = False,
|
528
611
|
uploaded_image_url: Optional[str] = None,
|
612
|
+
agent: Agent = None,
|
529
613
|
) -> str:
|
530
614
|
"""
|
531
615
|
Generate a better image prompt from the given query
|
532
616
|
"""
|
533
617
|
|
534
618
|
today_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d, %A")
|
619
|
+
personality_context = (
|
620
|
+
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
621
|
+
)
|
535
622
|
model_type = model_type or TextToImageModelConfig.ModelType.OPENAI
|
536
623
|
|
537
624
|
if location_data:
|
@@ -558,6 +645,7 @@ async def generate_better_image_prompt(
|
|
558
645
|
current_date=today_date,
|
559
646
|
references=user_references,
|
560
647
|
online_results=simplified_online_results,
|
648
|
+
personality_context=personality_context,
|
561
649
|
)
|
562
650
|
elif model_type in [TextToImageModelConfig.ModelType.STABILITYAI, TextToImageModelConfig.ModelType.REPLICATE]:
|
563
651
|
image_prompt = prompts.image_generation_improve_prompt_sd.format(
|
@@ -567,6 +655,7 @@ async def generate_better_image_prompt(
|
|
567
655
|
current_date=today_date,
|
568
656
|
references=user_references,
|
569
657
|
online_results=simplified_online_results,
|
658
|
+
personality_context=personality_context,
|
570
659
|
)
|
571
660
|
|
572
661
|
chat_model: ChatModelOptions = await ConversationAdapters.aget_default_conversation_config()
|
@@ -651,15 +740,13 @@ async def send_message_to_model_wrapper(
|
|
651
740
|
model_type=conversation_config.model_type,
|
652
741
|
)
|
653
742
|
|
654
|
-
|
743
|
+
return send_message_to_model(
|
655
744
|
messages=truncated_messages,
|
656
745
|
api_key=api_key,
|
657
746
|
model=chat_model,
|
658
747
|
response_type=response_type,
|
659
748
|
api_base_url=api_base_url,
|
660
749
|
)
|
661
|
-
|
662
|
-
return openai_response
|
663
750
|
elif model_type == ChatModelOptions.ModelType.ANTHROPIC:
|
664
751
|
api_key = conversation_config.openai_config.api_key
|
665
752
|
truncated_messages = generate_chatml_messages_with_context(
|
@@ -942,13 +1029,23 @@ class ApiUserRateLimiter:
|
|
942
1029
|
|
943
1030
|
# Check if the user has exceeded the rate limit
|
944
1031
|
if subscribed and count_requests >= self.subscribed_requests:
|
1032
|
+
logger.info(
|
1033
|
+
f"Rate limit: {count_requests} requests in {self.window} seconds for user: {user}. Limit is {self.subscribed_requests} requests."
|
1034
|
+
)
|
945
1035
|
raise HTTPException(status_code=429, detail="Slow down! Too Many Requests")
|
946
1036
|
if not subscribed and count_requests >= self.requests:
|
947
1037
|
if self.requests >= self.subscribed_requests:
|
1038
|
+
logger.info(
|
1039
|
+
f"Rate limit: {count_requests} requests in {self.window} seconds for user: {user}. Limit is {self.subscribed_requests} requests."
|
1040
|
+
)
|
948
1041
|
raise HTTPException(
|
949
1042
|
status_code=429,
|
950
1043
|
detail="Slow down! Too Many Requests",
|
951
1044
|
)
|
1045
|
+
|
1046
|
+
logger.info(
|
1047
|
+
f"Rate limit: {count_requests} requests in {self.window} seconds for user: {user}. Limit is {self.subscribed_requests} requests."
|
1048
|
+
)
|
952
1049
|
raise HTTPException(
|
953
1050
|
status_code=429,
|
954
1051
|
detail="We're glad you're enjoying Khoj! You've exceeded your usage limit for today. Come back tomorrow or subscribe to increase your usage limit via [your settings](https://app.khoj.dev/settings).",
|
@@ -986,6 +1083,9 @@ class ConversationCommandRateLimiter:
|
|
986
1083
|
).acount()
|
987
1084
|
|
988
1085
|
if subscribed and count_requests >= self.subscribed_rate_limit:
|
1086
|
+
logger.info(
|
1087
|
+
f"Rate limit: {count_requests} requests in 24 hours for user: {user}. Limit is {self.subscribed_rate_limit} requests."
|
1088
|
+
)
|
989
1089
|
raise HTTPException(status_code=429, detail="Slow down! Too Many Requests")
|
990
1090
|
if not subscribed and count_requests >= self.trial_rate_limit:
|
991
1091
|
raise HTTPException(
|
khoj/search_type/text_search.py
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
import logging
|
2
2
|
import math
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import List, Tuple, Type, Union
|
4
|
+
from typing import List, Optional, Tuple, Type, Union
|
5
5
|
|
6
6
|
import torch
|
7
7
|
from asgiref.sync import sync_to_async
|
8
8
|
from sentence_transformers import util
|
9
9
|
|
10
10
|
from khoj.database.adapters import EntryAdapters, get_user_search_model_or_default
|
11
|
+
from khoj.database.models import Agent
|
11
12
|
from khoj.database.models import Entry as DbEntry
|
12
13
|
from khoj.database.models import KhojUser
|
13
14
|
from khoj.processor.content.text_to_entries import TextToEntries
|
@@ -101,6 +102,7 @@ async def query(
|
|
101
102
|
type: SearchType = SearchType.All,
|
102
103
|
question_embedding: Union[torch.Tensor, None] = None,
|
103
104
|
max_distance: float = None,
|
105
|
+
agent: Optional[Agent] = None,
|
104
106
|
) -> Tuple[List[dict], List[Entry]]:
|
105
107
|
"Search for entries that answer the query"
|
106
108
|
|
@@ -129,6 +131,7 @@ async def query(
|
|
129
131
|
file_type_filter=file_type,
|
130
132
|
raw_query=raw_query,
|
131
133
|
max_distance=max_distance,
|
134
|
+
agent=agent,
|
132
135
|
).all()
|
133
136
|
hits = await sync_to_async(list)(hits) # type: ignore[call-arg]
|
134
137
|
|
khoj/utils/helpers.py
CHANGED
@@ -325,7 +325,15 @@ command_descriptions = {
|
|
325
325
|
ConversationCommand.Image: "Generate images by describing your imagination in words.",
|
326
326
|
ConversationCommand.Automation: "Automatically run your query at a specified time or interval.",
|
327
327
|
ConversationCommand.Help: "Get help with how to use or setup Khoj from the documentation",
|
328
|
-
ConversationCommand.Summarize: "
|
328
|
+
ConversationCommand.Summarize: "Get help with a question pertaining to an entire document.",
|
329
|
+
}
|
330
|
+
|
331
|
+
command_descriptions_for_agent = {
|
332
|
+
ConversationCommand.General: "Respond without any outside information or personal knowledge.",
|
333
|
+
ConversationCommand.Notes: "Search through the knowledge base. Required if the agent expects context from the knowledge base.",
|
334
|
+
ConversationCommand.Online: "Search for the latest, up-to-date information from the internet.",
|
335
|
+
ConversationCommand.Webpage: "Scrape specific web pages for information.",
|
336
|
+
ConversationCommand.Summarize: "Retrieve an answer that depends on the entire document or a large text. Knowledge base must be a single document.",
|
329
337
|
}
|
330
338
|
|
331
339
|
tool_descriptions_for_llm = {
|
@@ -334,7 +342,7 @@ tool_descriptions_for_llm = {
|
|
334
342
|
ConversationCommand.Notes: "To search the user's personal knowledge base. Especially helpful if the question expects context from the user's notes or documents.",
|
335
343
|
ConversationCommand.Online: "To search for the latest, up-to-date information from the internet. Note: **Questions about Khoj should always use this data source**",
|
336
344
|
ConversationCommand.Webpage: "To use if the user has directly provided the webpage urls or you are certain of the webpage urls to read.",
|
337
|
-
ConversationCommand.Summarize: "To
|
345
|
+
ConversationCommand.Summarize: "To retrieve an answer that depends on the entire document or a large text.",
|
338
346
|
}
|
339
347
|
|
340
348
|
mode_descriptions_for_llm = {
|
@@ -343,6 +351,11 @@ mode_descriptions_for_llm = {
|
|
343
351
|
ConversationCommand.Text: "Use this if the other response modes don't seem to fit the query.",
|
344
352
|
}
|
345
353
|
|
354
|
+
mode_descriptions_for_agent = {
|
355
|
+
ConversationCommand.Image: "Allow the agent to generate images.",
|
356
|
+
ConversationCommand.Text: "Allow the agent to generate text.",
|
357
|
+
}
|
358
|
+
|
346
359
|
|
347
360
|
class ImageIntentType(Enum):
|
348
361
|
"""
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: khoj
|
3
|
-
Version: 1.24.2.
|
3
|
+
Version: 1.24.2.dev16
|
4
4
|
Summary: Your Second Brain
|
5
5
|
Project-URL: Homepage, https://khoj.dev
|
6
6
|
Project-URL: Documentation, https://docs.khoj.dev
|
@@ -169,10 +169,3 @@ Made with [contrib.rocks](https://contrib.rocks).
|
|
169
169
|
### Interested in Contributing?
|
170
170
|
|
171
171
|
We are always looking for contributors to help us build new features, improve the project documentation, or fix bugs. If you're interested, please see our [Contributing Guidelines](https://docs.khoj.dev/contributing/development) and check out our [Contributors Project Board](https://github.com/orgs/khoj-ai/projects/4).
|
172
|
-
|
173
|
-
## [Sponsors](https://github.com/sponsors/khoj-ai)
|
174
|
-
Shout out to our brilliant sponsors! 🌈
|
175
|
-
|
176
|
-
<a href="http://github.com/beekeeb">
|
177
|
-
<img src="https://raw.githubusercontent.com/beekeeb/piantor/main/docs/beekeeb.png" width=250/>
|
178
|
-
</a>
|