khoj 1.26.4.dev2__py3-none-any.whl → 1.26.5.dev29__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 (84) hide show
  1. khoj/interface/compiled/404/index.html +1 -1
  2. khoj/interface/compiled/_next/static/chunks/1210.132a7e1910006bbb.js +1 -0
  3. khoj/interface/compiled/_next/static/chunks/1279-f37ee4a388ebf544.js +1 -0
  4. khoj/interface/compiled/_next/static/chunks/1459.690bf20e7d7b7090.js +1 -0
  5. khoj/interface/compiled/_next/static/chunks/1603-b9d95833e0e025e8.js +1 -0
  6. khoj/interface/compiled/_next/static/chunks/1970-1d6d0c1b00b4f343.js +1 -0
  7. khoj/interface/compiled/_next/static/chunks/2697-61fcba89fd87eab4.js +1 -0
  8. khoj/interface/compiled/_next/static/chunks/3423-aad88d6c1f029135.js +1 -0
  9. khoj/interface/compiled/_next/static/chunks/394-6bcb8c429f168f21.js +3 -0
  10. khoj/interface/compiled/_next/static/chunks/4602-8eeb4b76385ad159.js +1 -0
  11. khoj/interface/compiled/_next/static/chunks/5512-94c7c2bbcf58c19d.js +1 -0
  12. khoj/interface/compiled/_next/static/chunks/7113-f2e114d7034a0835.js +1 -0
  13. khoj/interface/compiled/_next/static/chunks/{4086-2c74808ba38a5a0f.js → 8840-b8d7b9f0923c6651.js} +1 -1
  14. khoj/interface/compiled/_next/static/chunks/9417-759984ad62caa3dc.js +1 -0
  15. khoj/interface/compiled/_next/static/chunks/9479-4b443fdcc99141c9.js +1 -0
  16. khoj/interface/compiled/_next/static/chunks/94ca1967.5584df65931cfe83.js +1 -0
  17. khoj/interface/compiled/_next/static/chunks/964ecbae.ea4eab2a3a835ffe.js +1 -0
  18. khoj/interface/compiled/_next/static/chunks/app/agents/page-5ae1e540bb5be8a9.js +1 -0
  19. khoj/interface/compiled/_next/static/chunks/app/automations/{page-5480731341f34450.js → page-774ae3e033f938cd.js} +1 -1
  20. khoj/interface/compiled/_next/static/chunks/app/chat/page-97f5b61aaf46d364.js +1 -0
  21. khoj/interface/compiled/_next/static/chunks/app/factchecker/{page-e7b34316ec6f44de.js → page-d82403db2866bad8.js} +1 -1
  22. khoj/interface/compiled/_next/static/chunks/app/{page-10a5aad6e04f3cf8.js → page-75bbfb564884054b.js} +1 -1
  23. khoj/interface/compiled/_next/static/chunks/app/search/{page-d56541c746fded7d.js → page-9b64f61caa5bd7f9.js} +1 -1
  24. khoj/interface/compiled/_next/static/chunks/app/settings/{page-e044a999468a7c5d.js → page-989cf38b87b19427.js} +1 -1
  25. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-eb9e282691858f2e.js +1 -0
  26. khoj/interface/compiled/_next/static/chunks/webpack-8f4afe09848e24e1.js +1 -0
  27. khoj/interface/compiled/_next/static/css/{c808691c459e3887.css → 3cf13271869a4aeb.css} +1 -1
  28. khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +1 -0
  29. khoj/interface/compiled/_next/static/css/76d55eb435962b19.css +25 -0
  30. khoj/interface/compiled/_next/static/css/{3e1f1fdd70775091.css → 80bd6301fc657983.css} +1 -1
  31. khoj/interface/compiled/_next/static/css/ddcc0cf73e062476.css +1 -0
  32. khoj/interface/compiled/agents/index.html +1 -1
  33. khoj/interface/compiled/agents/index.txt +2 -2
  34. khoj/interface/compiled/automations/index.html +1 -1
  35. khoj/interface/compiled/automations/index.txt +2 -2
  36. khoj/interface/compiled/chat/index.html +1 -1
  37. khoj/interface/compiled/chat/index.txt +2 -2
  38. khoj/interface/compiled/factchecker/index.html +1 -1
  39. khoj/interface/compiled/factchecker/index.txt +2 -2
  40. khoj/interface/compiled/index.html +1 -1
  41. khoj/interface/compiled/index.txt +2 -2
  42. khoj/interface/compiled/search/index.html +1 -1
  43. khoj/interface/compiled/search/index.txt +2 -2
  44. khoj/interface/compiled/settings/index.html +1 -1
  45. khoj/interface/compiled/settings/index.txt +2 -2
  46. khoj/interface/compiled/share/chat/index.html +1 -1
  47. khoj/interface/compiled/share/chat/index.txt +2 -2
  48. khoj/processor/conversation/google/gemini_chat.py +28 -13
  49. khoj/processor/conversation/google/utils.py +34 -12
  50. khoj/processor/conversation/openai/gpt.py +4 -4
  51. khoj/processor/conversation/prompts.py +144 -0
  52. khoj/processor/conversation/utils.py +22 -13
  53. khoj/processor/image/generate.py +5 -5
  54. khoj/processor/tools/online_search.py +4 -4
  55. khoj/routers/api.py +4 -2
  56. khoj/routers/api_agents.py +41 -20
  57. khoj/routers/api_chat.py +85 -46
  58. khoj/routers/helpers.py +225 -29
  59. khoj/routers/web_client.py +0 -11
  60. khoj/utils/helpers.py +7 -3
  61. {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/METADATA +1 -1
  62. {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/RECORD +67 -62
  63. khoj/interface/compiled/_next/static/chunks/121-7024f479c297aef0.js +0 -1
  64. khoj/interface/compiled/_next/static/chunks/1603-fa3ee48860b9dc5c.js +0 -1
  65. khoj/interface/compiled/_next/static/chunks/2697-a38d01981ad3bdf8.js +0 -1
  66. khoj/interface/compiled/_next/static/chunks/4051-2cf66369d6ca0f1d.js +0 -3
  67. khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +0 -1
  68. khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +0 -1
  69. khoj/interface/compiled/_next/static/chunks/7762-79f2205740622b5c.js +0 -1
  70. khoj/interface/compiled/_next/static/chunks/9178-899fe9a6b754ecfe.js +0 -1
  71. khoj/interface/compiled/_next/static/chunks/9417-46ed3aaa639c85ef.js +0 -1
  72. khoj/interface/compiled/_next/static/chunks/9479-ea776e73f549090c.js +0 -1
  73. khoj/interface/compiled/_next/static/chunks/app/agents/page-88aa3042711107b7.js +0 -1
  74. khoj/interface/compiled/_next/static/chunks/app/chat/page-702057ccbcf27881.js +0 -1
  75. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-fbbd66a4d4633438.js +0 -1
  76. khoj/interface/compiled/_next/static/chunks/webpack-2651a68f46ac3cb7.js +0 -1
  77. khoj/interface/compiled/_next/static/css/2de69f0be774c768.css +0 -1
  78. khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
  79. khoj/interface/compiled/_next/static/css/b9a6bf04305d98d7.css +0 -25
  80. /khoj/interface/compiled/_next/static/{wyjqS7cuSX-u62BTNYqhU → ZLHCGFLxZSUj0jEJSc99T}/_buildManifest.js +0 -0
  81. /khoj/interface/compiled/_next/static/{wyjqS7cuSX-u62BTNYqhU → ZLHCGFLxZSUj0jEJSc99T}/_ssgManifest.js +0 -0
  82. {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/WHEEL +0 -0
  83. {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/entry_points.txt +0 -0
  84. {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/licenses/LICENSE +0 -0
khoj/routers/api_chat.py CHANGED
@@ -30,8 +30,10 @@ from khoj.processor.speech.text_to_speech import generate_text_to_speech
30
30
  from khoj.processor.tools.online_search import read_webpages, search_online
31
31
  from khoj.routers.api import extract_references_and_questions
32
32
  from khoj.routers.helpers import (
33
+ ApiImageRateLimiter,
33
34
  ApiUserRateLimiter,
34
35
  ChatEvent,
36
+ ChatRequestBody,
35
37
  CommonQueryParams,
36
38
  ConversationCommandRateLimiter,
37
39
  agenerate_chat_response,
@@ -40,6 +42,7 @@ from khoj.routers.helpers import (
40
42
  construct_automation_created_message,
41
43
  create_automation,
42
44
  extract_relevant_summary,
45
+ generate_excalidraw_diagram,
43
46
  get_conversation_command,
44
47
  is_query_empty,
45
48
  is_ready_to_chat,
@@ -523,22 +526,6 @@ async def set_conversation_title(
523
526
  )
524
527
 
525
528
 
526
- class ChatRequestBody(BaseModel):
527
- q: str
528
- n: Optional[int] = 7
529
- d: Optional[float] = None
530
- stream: Optional[bool] = False
531
- title: Optional[str] = None
532
- conversation_id: Optional[str] = None
533
- city: Optional[str] = None
534
- region: Optional[str] = None
535
- country: Optional[str] = None
536
- country_code: Optional[str] = None
537
- timezone: Optional[str] = None
538
- image: Optional[str] = None
539
- create_new: Optional[bool] = False
540
-
541
-
542
529
  @api_chat.post("")
543
530
  @requires(["authenticated"])
544
531
  async def chat(
@@ -551,6 +538,7 @@ async def chat(
551
538
  rate_limiter_per_day=Depends(
552
539
  ApiUserRateLimiter(requests=600, subscribed_requests=6000, window=60 * 60 * 24, slug="chat_day")
553
540
  ),
541
+ image_rate_limiter=Depends(ApiImageRateLimiter(max_images=10, max_combined_size_mb=20)),
554
542
  ):
555
543
  # Access the parameters from the body
556
544
  q = body.q
@@ -564,9 +552,9 @@ async def chat(
564
552
  country = body.country or get_country_name_from_timezone(body.timezone)
565
553
  country_code = body.country_code or get_country_code_from_timezone(body.timezone)
566
554
  timezone = body.timezone
567
- image = body.image
555
+ raw_images = body.images
568
556
 
569
- async def event_generator(q: str, image: str):
557
+ async def event_generator(q: str, images: list[str]):
570
558
  start_time = time.perf_counter()
571
559
  ttft = None
572
560
  chat_metadata: dict = {}
@@ -576,16 +564,16 @@ async def chat(
576
564
  q = unquote(q)
577
565
  nonlocal conversation_id
578
566
 
579
- uploaded_image_url = None
580
- if image:
581
- decoded_string = unquote(image)
582
- base64_data = decoded_string.split(",", 1)[1]
583
- image_bytes = base64.b64decode(base64_data)
584
- webp_image_bytes = convert_image_to_webp(image_bytes)
585
- try:
586
- uploaded_image_url = upload_image_to_bucket(webp_image_bytes, request.user.object.id)
587
- except:
588
- uploaded_image_url = None
567
+ uploaded_images: list[str] = []
568
+ if images:
569
+ for image in images:
570
+ decoded_string = unquote(image)
571
+ base64_data = decoded_string.split(",", 1)[1]
572
+ image_bytes = base64.b64decode(base64_data)
573
+ webp_image_bytes = convert_image_to_webp(image_bytes)
574
+ uploaded_image = upload_image_to_bucket(webp_image_bytes, request.user.object.id)
575
+ if uploaded_image:
576
+ uploaded_images.append(uploaded_image)
589
577
 
590
578
  async def send_event(event_type: ChatEvent, data: str | dict):
591
579
  nonlocal connection_alive, ttft
@@ -692,7 +680,7 @@ async def chat(
692
680
  meta_log,
693
681
  is_automated_task,
694
682
  user=user,
695
- uploaded_image_url=uploaded_image_url,
683
+ query_images=uploaded_images,
696
684
  agent=agent,
697
685
  )
698
686
  conversation_commands_str = ", ".join([cmd.value for cmd in conversation_commands])
@@ -701,7 +689,7 @@ async def chat(
701
689
  ):
702
690
  yield result
703
691
 
704
- mode = await aget_relevant_output_modes(q, meta_log, is_automated_task, user, uploaded_image_url, agent)
692
+ mode = await aget_relevant_output_modes(q, meta_log, is_automated_task, user, uploaded_images, agent)
705
693
  async for result in send_event(ChatEvent.STATUS, f"**Decided Response Mode:** {mode.value}"):
706
694
  yield result
707
695
  if mode not in conversation_commands:
@@ -764,7 +752,7 @@ async def chat(
764
752
  q,
765
753
  contextual_data,
766
754
  conversation_history=meta_log,
767
- uploaded_image_url=uploaded_image_url,
755
+ query_images=uploaded_images,
768
756
  user=user,
769
757
  agent=agent,
770
758
  )
@@ -785,7 +773,7 @@ async def chat(
785
773
  intent_type="summarize",
786
774
  client_application=request.user.client_app,
787
775
  conversation_id=conversation_id,
788
- uploaded_image_url=uploaded_image_url,
776
+ query_images=uploaded_images,
789
777
  )
790
778
  return
791
779
 
@@ -828,7 +816,7 @@ async def chat(
828
816
  conversation_id=conversation_id,
829
817
  inferred_queries=[query_to_run],
830
818
  automation_id=automation.id,
831
- uploaded_image_url=uploaded_image_url,
819
+ query_images=uploaded_images,
832
820
  )
833
821
  async for result in send_llm_response(llm_response):
834
822
  yield result
@@ -848,7 +836,7 @@ async def chat(
848
836
  conversation_commands,
849
837
  location,
850
838
  partial(send_event, ChatEvent.STATUS),
851
- uploaded_image_url=uploaded_image_url,
839
+ query_images=uploaded_images,
852
840
  agent=agent,
853
841
  ):
854
842
  if isinstance(result, dict) and ChatEvent.STATUS in result:
@@ -892,7 +880,7 @@ async def chat(
892
880
  user,
893
881
  partial(send_event, ChatEvent.STATUS),
894
882
  custom_filters,
895
- uploaded_image_url=uploaded_image_url,
883
+ query_images=uploaded_images,
896
884
  agent=agent,
897
885
  ):
898
886
  if isinstance(result, dict) and ChatEvent.STATUS in result:
@@ -916,7 +904,7 @@ async def chat(
916
904
  location,
917
905
  user,
918
906
  partial(send_event, ChatEvent.STATUS),
919
- uploaded_image_url=uploaded_image_url,
907
+ query_images=uploaded_images,
920
908
  agent=agent,
921
909
  ):
922
910
  if isinstance(result, dict) and ChatEvent.STATUS in result:
@@ -966,20 +954,20 @@ async def chat(
966
954
  references=compiled_references,
967
955
  online_results=online_results,
968
956
  send_status_func=partial(send_event, ChatEvent.STATUS),
969
- uploaded_image_url=uploaded_image_url,
957
+ query_images=uploaded_images,
970
958
  agent=agent,
971
959
  ):
972
960
  if isinstance(result, dict) and ChatEvent.STATUS in result:
973
961
  yield result[ChatEvent.STATUS]
974
962
  else:
975
- image, status_code, improved_image_prompt, intent_type = result
963
+ generated_image, status_code, improved_image_prompt, intent_type = result
976
964
 
977
- if image is None or status_code != 200:
965
+ if generated_image is None or status_code != 200:
978
966
  content_obj = {
979
967
  "content-type": "application/json",
980
968
  "intentType": intent_type,
981
969
  "detail": improved_image_prompt,
982
- "image": image,
970
+ "image": None,
983
971
  }
984
972
  async for result in send_llm_response(json.dumps(content_obj)):
985
973
  yield result
@@ -987,7 +975,7 @@ async def chat(
987
975
 
988
976
  await sync_to_async(save_to_conversation_log)(
989
977
  q,
990
- image,
978
+ generated_image,
991
979
  user,
992
980
  meta_log,
993
981
  user_message_time,
@@ -997,13 +985,64 @@ async def chat(
997
985
  conversation_id=conversation_id,
998
986
  compiled_references=compiled_references,
999
987
  online_results=online_results,
1000
- uploaded_image_url=uploaded_image_url,
988
+ query_images=uploaded_images,
1001
989
  )
1002
990
  content_obj = {
1003
991
  "intentType": intent_type,
1004
992
  "inferredQueries": [improved_image_prompt],
1005
- "image": image,
993
+ "image": generated_image,
994
+ }
995
+ async for result in send_llm_response(json.dumps(content_obj)):
996
+ yield result
997
+ return
998
+
999
+ if ConversationCommand.Diagram in conversation_commands:
1000
+ async for result in send_event(ChatEvent.STATUS, f"Creating diagram"):
1001
+ yield result
1002
+
1003
+ intent_type = "excalidraw"
1004
+ inferred_queries = []
1005
+ diagram_description = ""
1006
+
1007
+ async for result in generate_excalidraw_diagram(
1008
+ q=defiltered_query,
1009
+ conversation_history=meta_log,
1010
+ location_data=location,
1011
+ note_references=compiled_references,
1012
+ online_results=online_results,
1013
+ query_images=uploaded_images,
1014
+ user=user,
1015
+ agent=agent,
1016
+ send_status_func=partial(send_event, ChatEvent.STATUS),
1017
+ ):
1018
+ if isinstance(result, dict) and ChatEvent.STATUS in result:
1019
+ yield result[ChatEvent.STATUS]
1020
+ else:
1021
+ better_diagram_description_prompt, excalidraw_diagram_description = result
1022
+ inferred_queries.append(better_diagram_description_prompt)
1023
+ diagram_description = excalidraw_diagram_description
1024
+
1025
+ content_obj = {
1026
+ "intentType": intent_type,
1027
+ "inferredQueries": inferred_queries,
1028
+ "image": diagram_description,
1006
1029
  }
1030
+
1031
+ await sync_to_async(save_to_conversation_log)(
1032
+ q,
1033
+ excalidraw_diagram_description,
1034
+ user,
1035
+ meta_log,
1036
+ user_message_time,
1037
+ intent_type="excalidraw",
1038
+ inferred_queries=[better_diagram_description_prompt],
1039
+ client_application=request.user.client_app,
1040
+ conversation_id=conversation_id,
1041
+ compiled_references=compiled_references,
1042
+ online_results=online_results,
1043
+ query_images=uploaded_images,
1044
+ )
1045
+
1007
1046
  async for result in send_llm_response(json.dumps(content_obj)):
1008
1047
  yield result
1009
1048
  return
@@ -1024,7 +1063,7 @@ async def chat(
1024
1063
  conversation_id,
1025
1064
  location,
1026
1065
  user_name,
1027
- uploaded_image_url,
1066
+ uploaded_images,
1028
1067
  )
1029
1068
 
1030
1069
  # Send Response
@@ -1050,9 +1089,9 @@ async def chat(
1050
1089
 
1051
1090
  ## Stream Text Response
1052
1091
  if stream:
1053
- return StreamingResponse(event_generator(q, image=image), media_type="text/plain")
1092
+ return StreamingResponse(event_generator(q, images=raw_images), media_type="text/plain")
1054
1093
  ## Non-Streaming Text Response
1055
1094
  else:
1056
- response_iterator = event_generator(q, image=image)
1095
+ response_iterator = event_generator(q, images=raw_images)
1057
1096
  response_data = await read_chat_stream(response_iterator)
1058
1097
  return Response(content=json.dumps(response_data), media_type="application/json", status_code=200)