khoj 2.0.0b14.dev51__py3-none-any.whl → 2.0.0b15.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/database/adapters/__init__.py +59 -20
- khoj/database/admin.py +4 -0
- khoj/database/migrations/0094_serverchatsettings_think_free_deep_and_more.py +61 -0
- khoj/database/models/__init__.py +18 -2
- khoj/interface/compiled/404/index.html +2 -2
- khoj/interface/compiled/_next/static/chunks/{9808-0ae18d938933fea3.js → 9808-bd5d7361ad026094.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-e572645654c4335e.js → webpack-820bd66958a5e279.js} +1 -1
- khoj/interface/compiled/_next/static/css/821d0d60b0b6871d.css +1 -0
- khoj/interface/compiled/_next/static/css/fb7ea16e60b40ecd.css +1 -0
- khoj/interface/compiled/agents/index.html +2 -2
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/automations/index.html +2 -2
- khoj/interface/compiled/automations/index.txt +3 -3
- 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 +2 -2
- khoj/interface/compiled/search/index.html +2 -2
- khoj/interface/compiled/search/index.txt +2 -2
- khoj/interface/compiled/settings/index.html +2 -2
- khoj/interface/compiled/settings/index.txt +4 -4
- khoj/interface/compiled/share/chat/index.html +2 -2
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/processor/conversation/anthropic/anthropic_chat.py +4 -88
- khoj/processor/conversation/anthropic/utils.py +1 -2
- khoj/processor/conversation/google/gemini_chat.py +4 -88
- khoj/processor/conversation/google/utils.py +6 -3
- khoj/processor/conversation/openai/gpt.py +16 -93
- khoj/processor/conversation/openai/utils.py +38 -30
- khoj/processor/conversation/prompts.py +30 -39
- khoj/processor/conversation/utils.py +72 -84
- khoj/processor/image/generate.py +69 -15
- khoj/processor/tools/run_code.py +3 -2
- khoj/routers/api_chat.py +8 -21
- khoj/routers/helpers.py +243 -156
- khoj/routers/research.py +6 -6
- khoj/utils/helpers.py +6 -2
- {khoj-2.0.0b14.dev51.dist-info → khoj-2.0.0b15.dev23.dist-info}/METADATA +1 -1
- {khoj-2.0.0b14.dev51.dist-info → khoj-2.0.0b15.dev23.dist-info}/RECORD +51 -50
- khoj/interface/compiled/_next/static/css/2945c4a857922f3b.css +0 -1
- khoj/interface/compiled/_next/static/css/ecea704005ba630c.css +0 -1
- /khoj/interface/compiled/_next/static/{yBzbL9kxl5BudSA9F4Gr6 → PcD2gC0kChVzNip15DdDQ}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{yBzbL9kxl5BudSA9F4Gr6 → PcD2gC0kChVzNip15DdDQ}/_ssgManifest.js +0 -0
- /khoj/interface/compiled/_next/static/chunks/{1327-511bb0a862efce80.js → 1327-e254819a9172cfa7.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-2.0.0b14.dev51.dist-info → khoj-2.0.0b15.dev23.dist-info}/WHEEL +0 -0
- {khoj-2.0.0b14.dev51.dist-info → khoj-2.0.0b15.dev23.dist-info}/entry_points.txt +0 -0
- {khoj-2.0.0b14.dev51.dist-info → khoj-2.0.0b15.dev23.dist-info}/licenses/LICENSE +0 -0
khoj/routers/helpers.py
CHANGED
@@ -33,6 +33,7 @@ from apscheduler.triggers.cron import CronTrigger
|
|
33
33
|
from asgiref.sync import sync_to_async
|
34
34
|
from django.utils import timezone as django_timezone
|
35
35
|
from fastapi import Depends, Header, HTTPException, Request, UploadFile, WebSocket
|
36
|
+
from langchain_core.messages.chat import ChatMessage
|
36
37
|
from pydantic import BaseModel, EmailStr, Field
|
37
38
|
from starlette.authentication import has_required_scope
|
38
39
|
from starlette.requests import URL
|
@@ -113,6 +114,7 @@ from khoj.utils import state
|
|
113
114
|
from khoj.utils.helpers import (
|
114
115
|
LRU,
|
115
116
|
ConversationCommand,
|
117
|
+
ImageShape,
|
116
118
|
ToolDefinition,
|
117
119
|
get_file_type,
|
118
120
|
in_debug_mode,
|
@@ -123,15 +125,16 @@ from khoj.utils.helpers import (
|
|
123
125
|
mode_descriptions_for_llm,
|
124
126
|
timer,
|
125
127
|
tool_descriptions_for_llm,
|
128
|
+
truncate_code_context,
|
126
129
|
)
|
127
130
|
from khoj.utils.rawconfig import (
|
128
131
|
ChatRequestBody,
|
129
|
-
FileAttachment,
|
130
132
|
FileData,
|
131
133
|
LocationData,
|
132
134
|
SearchResponse,
|
133
135
|
)
|
134
136
|
from khoj.utils.state import SearchType
|
137
|
+
from khoj.utils.yaml import yaml_dump
|
135
138
|
|
136
139
|
logger = logging.getLogger(__name__)
|
137
140
|
|
@@ -286,7 +289,7 @@ async def acreate_title_from_history(
|
|
286
289
|
title_generation_prompt = prompts.conversation_title_generation.format(chat_history=chat_history)
|
287
290
|
|
288
291
|
with timer("Chat actor: Generate title from conversation history", logger):
|
289
|
-
response = await send_message_to_model_wrapper(title_generation_prompt, user=user)
|
292
|
+
response = await send_message_to_model_wrapper(title_generation_prompt, fast_model=True, user=user)
|
290
293
|
|
291
294
|
return response.text.strip()
|
292
295
|
|
@@ -298,7 +301,7 @@ async def acreate_title_from_query(query: str, user: KhojUser = None) -> str:
|
|
298
301
|
title_generation_prompt = prompts.subject_generation.format(query=query)
|
299
302
|
|
300
303
|
with timer("Chat actor: Generate title from query", logger):
|
301
|
-
response = await send_message_to_model_wrapper(title_generation_prompt, user=user)
|
304
|
+
response = await send_message_to_model_wrapper(title_generation_prompt, fast_model=True, user=user)
|
302
305
|
|
303
306
|
return response.text.strip()
|
304
307
|
|
@@ -321,7 +324,7 @@ async def acheck_if_safe_prompt(system_prompt: str, user: KhojUser = None, lax:
|
|
321
324
|
|
322
325
|
with timer("Chat actor: Check if safe prompt", logger):
|
323
326
|
response = await send_message_to_model_wrapper(
|
324
|
-
safe_prompt_check,
|
327
|
+
safe_prompt_check, response_type="json_object", response_schema=SafetyCheck, fast_model=True, user=user
|
325
328
|
)
|
326
329
|
|
327
330
|
response = response.text.strip()
|
@@ -342,7 +345,6 @@ async def acheck_if_safe_prompt(system_prompt: str, user: KhojUser = None, lax:
|
|
342
345
|
async def aget_data_sources_and_output_format(
|
343
346
|
query: str,
|
344
347
|
chat_history: list[ChatMessageModel],
|
345
|
-
is_task: bool,
|
346
348
|
user: KhojUser,
|
347
349
|
query_images: List[str] = None,
|
348
350
|
agent: Agent = None,
|
@@ -381,9 +383,6 @@ async def aget_data_sources_and_output_format(
|
|
381
383
|
|
382
384
|
chat_history_str = construct_chat_history(chat_history, n=6)
|
383
385
|
|
384
|
-
if query_images:
|
385
|
-
query = f"[placeholder for {len(query_images)} user attached images]\n{query}"
|
386
|
-
|
387
386
|
personality_context = (
|
388
387
|
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
389
388
|
)
|
@@ -405,11 +404,13 @@ async def aget_data_sources_and_output_format(
|
|
405
404
|
with timer("Chat actor: Infer information sources to refer", logger):
|
406
405
|
raw_response = await send_message_to_model_wrapper(
|
407
406
|
relevant_tools_prompt,
|
407
|
+
query_files=query_files,
|
408
|
+
query_images=query_images,
|
408
409
|
response_type="json_object",
|
409
410
|
response_schema=PickTools,
|
410
|
-
|
411
|
-
query_files=query_files,
|
411
|
+
fast_model=False,
|
412
412
|
agent_chat_model=agent_chat_model,
|
413
|
+
user=user,
|
413
414
|
tracer=tracer,
|
414
415
|
)
|
415
416
|
|
@@ -493,12 +494,13 @@ async def infer_webpage_urls(
|
|
493
494
|
with timer("Chat actor: Infer webpage urls to read", logger):
|
494
495
|
raw_response = await send_message_to_model_wrapper(
|
495
496
|
online_queries_prompt,
|
497
|
+
query_files=query_files,
|
496
498
|
query_images=query_images,
|
497
499
|
response_type="json_object",
|
498
500
|
response_schema=WebpageUrls,
|
499
|
-
|
500
|
-
query_files=query_files,
|
501
|
+
fast_model=False,
|
501
502
|
agent_chat_model=agent_chat_model,
|
503
|
+
user=user,
|
502
504
|
tracer=tracer,
|
503
505
|
)
|
504
506
|
|
@@ -558,12 +560,13 @@ async def generate_online_subqueries(
|
|
558
560
|
with timer("Chat actor: Generate online search subqueries", logger):
|
559
561
|
raw_response = await send_message_to_model_wrapper(
|
560
562
|
online_queries_prompt,
|
563
|
+
query_files=query_files,
|
561
564
|
query_images=query_images,
|
562
565
|
response_type="json_object",
|
563
566
|
response_schema=OnlineQueries,
|
564
|
-
|
565
|
-
query_files=query_files,
|
567
|
+
fast_model=False,
|
566
568
|
agent_chat_model=agent_chat_model,
|
569
|
+
user=user,
|
567
570
|
tracer=tracer,
|
568
571
|
)
|
569
572
|
|
@@ -625,7 +628,12 @@ async def aschedule_query(
|
|
625
628
|
)
|
626
629
|
|
627
630
|
raw_response = await send_message_to_model_wrapper(
|
628
|
-
crontime_prompt,
|
631
|
+
crontime_prompt,
|
632
|
+
query_images=query_images,
|
633
|
+
response_type="json_object",
|
634
|
+
fast_model=False,
|
635
|
+
user=user,
|
636
|
+
tracer=tracer,
|
629
637
|
)
|
630
638
|
|
631
639
|
# Validate that the response is a non-empty, JSON-serializable list
|
@@ -663,9 +671,10 @@ async def extract_relevant_info(
|
|
663
671
|
|
664
672
|
response = await send_message_to_model_wrapper(
|
665
673
|
extract_relevant_information,
|
666
|
-
prompts.system_prompt_extract_relevant_information,
|
667
|
-
|
674
|
+
system_message=prompts.system_prompt_extract_relevant_information,
|
675
|
+
fast_model=True,
|
668
676
|
agent_chat_model=agent_chat_model,
|
677
|
+
user=user,
|
669
678
|
tracer=tracer,
|
670
679
|
)
|
671
680
|
return response.text.strip()
|
@@ -705,10 +714,11 @@ async def extract_relevant_summary(
|
|
705
714
|
with timer("Chat actor: Extract relevant information from data", logger):
|
706
715
|
response = await send_message_to_model_wrapper(
|
707
716
|
extract_relevant_information,
|
708
|
-
prompts.system_prompt_extract_relevant_summary,
|
709
|
-
user=user,
|
710
717
|
query_images=query_images,
|
718
|
+
system_message=prompts.system_prompt_extract_relevant_summary,
|
719
|
+
fast_model=True,
|
711
720
|
agent_chat_model=agent_chat_model,
|
721
|
+
user=user,
|
712
722
|
tracer=tracer,
|
713
723
|
)
|
714
724
|
return response.text.strip()
|
@@ -877,9 +887,10 @@ async def generate_better_diagram_description(
|
|
877
887
|
response = await send_message_to_model_wrapper(
|
878
888
|
improve_diagram_description_prompt,
|
879
889
|
query_images=query_images,
|
880
|
-
user=user,
|
881
890
|
query_files=query_files,
|
891
|
+
fast_model=False,
|
882
892
|
agent_chat_model=agent_chat_model,
|
893
|
+
user=user,
|
883
894
|
tracer=tracer,
|
884
895
|
)
|
885
896
|
response = response.text.strip()
|
@@ -908,7 +919,11 @@ async def generate_excalidraw_diagram_from_description(
|
|
908
919
|
|
909
920
|
with timer("Chat actor: Generate excalidraw diagram", logger):
|
910
921
|
raw_response = await send_message_to_model_wrapper(
|
911
|
-
query=excalidraw_diagram_generation,
|
922
|
+
query=excalidraw_diagram_generation,
|
923
|
+
fast_model=False,
|
924
|
+
agent_chat_model=agent_chat_model,
|
925
|
+
user=user,
|
926
|
+
tracer=tracer,
|
912
927
|
)
|
913
928
|
raw_response_text = clean_json(raw_response.text)
|
914
929
|
try:
|
@@ -1027,10 +1042,11 @@ async def generate_better_mermaidjs_diagram_description(
|
|
1027
1042
|
with timer("Chat actor: Generate better Mermaid.js diagram description", logger):
|
1028
1043
|
response = await send_message_to_model_wrapper(
|
1029
1044
|
improve_diagram_description_prompt,
|
1030
|
-
query_images=query_images,
|
1031
|
-
user=user,
|
1032
1045
|
query_files=query_files,
|
1046
|
+
query_images=query_images,
|
1047
|
+
fast_model=False,
|
1033
1048
|
agent_chat_model=agent_chat_model,
|
1049
|
+
user=user,
|
1034
1050
|
tracer=tracer,
|
1035
1051
|
)
|
1036
1052
|
response_text = response.text.strip()
|
@@ -1059,14 +1075,18 @@ async def generate_mermaidjs_diagram_from_description(
|
|
1059
1075
|
|
1060
1076
|
with timer("Chat actor: Generate Mermaid.js diagram", logger):
|
1061
1077
|
raw_response = await send_message_to_model_wrapper(
|
1062
|
-
query=mermaidjs_diagram_generation,
|
1078
|
+
query=mermaidjs_diagram_generation,
|
1079
|
+
fast_model=False,
|
1080
|
+
agent_chat_model=agent_chat_model,
|
1081
|
+
user=user,
|
1082
|
+
tracer=tracer,
|
1063
1083
|
)
|
1064
1084
|
return clean_mermaidjs(raw_response.text.strip())
|
1065
1085
|
|
1066
1086
|
|
1067
1087
|
async def generate_better_image_prompt(
|
1068
1088
|
q: str,
|
1069
|
-
conversation_history:
|
1089
|
+
conversation_history: List[ChatMessageModel],
|
1070
1090
|
location_data: LocationData,
|
1071
1091
|
note_references: List[Dict[str, Any]],
|
1072
1092
|
online_results: Optional[dict] = None,
|
@@ -1076,12 +1096,11 @@ async def generate_better_image_prompt(
|
|
1076
1096
|
agent: Agent = None,
|
1077
1097
|
query_files: str = "",
|
1078
1098
|
tracer: dict = {},
|
1079
|
-
) ->
|
1099
|
+
) -> dict:
|
1080
1100
|
"""
|
1081
1101
|
Generate a better image prompt from the given query
|
1082
1102
|
"""
|
1083
1103
|
|
1084
|
-
today_date = datetime.now(tz=timezone.utc).strftime("%Y-%m-%d, %A")
|
1085
1104
|
personality_context = (
|
1086
1105
|
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
1087
1106
|
)
|
@@ -1089,58 +1108,53 @@ async def generate_better_image_prompt(
|
|
1089
1108
|
|
1090
1109
|
location = f"{location_data}" if location_data else "Unknown"
|
1091
1110
|
|
1092
|
-
user_references = "\n\n".join([f"
|
1111
|
+
user_references = "\n\n".join([f"- text:\n{item['compiled']}" for item in note_references])
|
1093
1112
|
|
1094
1113
|
simplified_online_results = {}
|
1114
|
+
for result in online_results or []:
|
1115
|
+
if online_results[result].get("answerBox"):
|
1116
|
+
simplified_online_results[result] = online_results[result]["answerBox"]
|
1117
|
+
elif online_results[result].get("webpages"):
|
1118
|
+
simplified_online_results[result] = online_results[result]["webpages"]
|
1095
1119
|
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1120
|
+
enhance_image_system_message = prompts.enhance_image_system_message.format(
|
1121
|
+
location=location,
|
1122
|
+
references=user_references,
|
1123
|
+
online_results=simplified_online_results or "",
|
1124
|
+
personality_context=personality_context,
|
1125
|
+
)
|
1102
1126
|
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
location=location,
|
1108
|
-
current_date=today_date,
|
1109
|
-
references=user_references,
|
1110
|
-
online_results=simplified_online_results,
|
1111
|
-
personality_context=personality_context,
|
1112
|
-
)
|
1113
|
-
elif model_type in [
|
1114
|
-
TextToImageModelConfig.ModelType.STABILITYAI,
|
1115
|
-
TextToImageModelConfig.ModelType.REPLICATE,
|
1116
|
-
TextToImageModelConfig.ModelType.GOOGLE,
|
1117
|
-
]:
|
1118
|
-
image_prompt = prompts.image_generation_improve_prompt_sd.format(
|
1119
|
-
query=q,
|
1120
|
-
chat_history=conversation_history,
|
1121
|
-
location=location,
|
1122
|
-
current_date=today_date,
|
1123
|
-
references=user_references,
|
1124
|
-
online_results=simplified_online_results,
|
1125
|
-
personality_context=personality_context,
|
1127
|
+
class ImagePromptResponse(BaseModel):
|
1128
|
+
description: str = Field(description="Enhanced image description")
|
1129
|
+
shape: ImageShape = Field(
|
1130
|
+
description="Aspect ratio/shape best suited to render the image: Portrait, Landscape, or Square"
|
1126
1131
|
)
|
1127
1132
|
|
1128
1133
|
agent_chat_model = AgentAdapters.get_agent_chat_model(agent, user) if agent else None
|
1129
1134
|
|
1130
1135
|
with timer("Chat actor: Generate contextual image prompt", logger):
|
1131
|
-
|
1132
|
-
|
1133
|
-
query_images=query_images,
|
1134
|
-
user=user,
|
1136
|
+
raw_response = await send_message_to_model_wrapper(
|
1137
|
+
q,
|
1135
1138
|
query_files=query_files,
|
1139
|
+
query_images=query_images,
|
1140
|
+
chat_history=conversation_history,
|
1141
|
+
system_message=enhance_image_system_message,
|
1142
|
+
response_type="json_object",
|
1143
|
+
response_schema=ImagePromptResponse,
|
1144
|
+
fast_model=False,
|
1136
1145
|
agent_chat_model=agent_chat_model,
|
1146
|
+
user=user,
|
1137
1147
|
tracer=tracer,
|
1138
1148
|
)
|
1139
|
-
response_text = response.text.strip()
|
1140
|
-
if response_text.startswith(('"', "'")) and response_text.endswith(('"', "'")):
|
1141
|
-
response_text = response_text[1:-1]
|
1142
1149
|
|
1143
|
-
|
1150
|
+
# Parse the structured response
|
1151
|
+
try:
|
1152
|
+
response = clean_json(raw_response.text)
|
1153
|
+
parsed_response = pyjson5.loads(response)
|
1154
|
+
return parsed_response
|
1155
|
+
except Exception:
|
1156
|
+
# Fallback to user query as image description
|
1157
|
+
return {"description": q, "shape": ImageShape.SQUARE}
|
1144
1158
|
|
1145
1159
|
|
1146
1160
|
async def search_documents(
|
@@ -1150,7 +1164,7 @@ async def search_documents(
|
|
1150
1164
|
user: KhojUser,
|
1151
1165
|
chat_history: list[ChatMessageModel],
|
1152
1166
|
conversation_id: str,
|
1153
|
-
conversation_commands: List[ConversationCommand] = [ConversationCommand.
|
1167
|
+
conversation_commands: List[ConversationCommand] = [ConversationCommand.Notes],
|
1154
1168
|
location_data: LocationData = None,
|
1155
1169
|
send_status_func: Optional[Callable] = None,
|
1156
1170
|
query_images: Optional[List[str]] = None,
|
@@ -1168,19 +1182,12 @@ async def search_documents(
|
|
1168
1182
|
if agent:
|
1169
1183
|
agent_has_entries = await sync_to_async(EntryAdapters.agent_has_entries)(agent=agent)
|
1170
1184
|
|
1171
|
-
if
|
1172
|
-
ConversationCommand.Notes not in conversation_commands
|
1173
|
-
and ConversationCommand.Default not in conversation_commands
|
1174
|
-
and not agent_has_entries
|
1175
|
-
):
|
1185
|
+
if ConversationCommand.Notes not in conversation_commands and not agent_has_entries:
|
1176
1186
|
yield compiled_references, inferred_queries, q
|
1177
1187
|
return
|
1178
1188
|
|
1179
|
-
# If Notes
|
1180
|
-
should_limit_to_agent_knowledge =
|
1181
|
-
ConversationCommand.Notes not in conversation_commands
|
1182
|
-
and ConversationCommand.Default not in conversation_commands
|
1183
|
-
)
|
1189
|
+
# If Notes is not in the conversation command, then the search should be restricted to the agent's knowledge base
|
1190
|
+
should_limit_to_agent_knowledge = ConversationCommand.Notes not in conversation_commands
|
1184
1191
|
|
1185
1192
|
if not await sync_to_async(EntryAdapters.user_has_entries)(user=user):
|
1186
1193
|
if not agent_has_entries:
|
@@ -1209,11 +1216,12 @@ async def search_documents(
|
|
1209
1216
|
inferred_queries = await extract_questions(
|
1210
1217
|
query=defiltered_query,
|
1211
1218
|
user=user,
|
1219
|
+
query_files=query_files,
|
1220
|
+
query_images=query_images,
|
1212
1221
|
personality_context=personality_context,
|
1213
|
-
chat_history=chat_history,
|
1214
1222
|
location_data=location_data,
|
1215
|
-
|
1216
|
-
|
1223
|
+
chat_history=chat_history,
|
1224
|
+
agent=agent,
|
1217
1225
|
tracer=tracer,
|
1218
1226
|
)
|
1219
1227
|
|
@@ -1259,12 +1267,13 @@ async def search_documents(
|
|
1259
1267
|
async def extract_questions(
|
1260
1268
|
query: str,
|
1261
1269
|
user: KhojUser,
|
1270
|
+
query_files: str = None,
|
1271
|
+
query_images: Optional[List[str]] = None,
|
1262
1272
|
personality_context: str = "",
|
1263
|
-
chat_history: List[ChatMessageModel] = [],
|
1264
1273
|
location_data: LocationData = None,
|
1265
|
-
|
1266
|
-
query_files: str = None,
|
1274
|
+
chat_history: List[ChatMessageModel] = [],
|
1267
1275
|
max_queries: int = 5,
|
1276
|
+
agent: Agent = None,
|
1268
1277
|
tracer: dict = {},
|
1269
1278
|
):
|
1270
1279
|
"""
|
@@ -1309,13 +1318,17 @@ async def extract_questions(
|
|
1309
1318
|
description="List of semantic search queries to run on user documents.",
|
1310
1319
|
)
|
1311
1320
|
|
1321
|
+
agent_chat_model = AgentAdapters.get_agent_chat_model(agent, user) if agent else None
|
1322
|
+
|
1312
1323
|
raw_response = await send_message_to_model_wrapper(
|
1313
|
-
system_message=system_prompt,
|
1314
1324
|
query=prompt,
|
1315
|
-
query_images=query_images,
|
1316
1325
|
query_files=query_files,
|
1326
|
+
query_images=query_images,
|
1327
|
+
system_message=system_prompt,
|
1317
1328
|
response_type="json_object",
|
1318
1329
|
response_schema=DocumentQueries,
|
1330
|
+
fast_model=False,
|
1331
|
+
agent_chat_model=agent_chat_model,
|
1319
1332
|
user=user,
|
1320
1333
|
tracer=tracer,
|
1321
1334
|
)
|
@@ -1428,21 +1441,26 @@ async def execute_search(
|
|
1428
1441
|
|
1429
1442
|
|
1430
1443
|
async def send_message_to_model_wrapper(
|
1444
|
+
# Context
|
1431
1445
|
query: str,
|
1446
|
+
query_files: str = None,
|
1447
|
+
query_images: List[str] = None,
|
1448
|
+
context: str = "",
|
1449
|
+
chat_history: list[ChatMessageModel] = [],
|
1432
1450
|
system_message: str = "",
|
1451
|
+
# Model Config
|
1433
1452
|
response_type: str = "text",
|
1434
1453
|
response_schema: BaseModel = None,
|
1435
1454
|
tools: List[ToolDefinition] = None,
|
1436
1455
|
deepthought: bool = False,
|
1437
|
-
|
1438
|
-
query_images: List[str] = None,
|
1439
|
-
context: str = "",
|
1440
|
-
query_files: str = None,
|
1441
|
-
chat_history: list[ChatMessageModel] = [],
|
1456
|
+
fast_model: Optional[bool] = None,
|
1442
1457
|
agent_chat_model: ChatModel = None,
|
1458
|
+
# User
|
1459
|
+
user: KhojUser = None,
|
1460
|
+
# Tracer
|
1443
1461
|
tracer: dict = {},
|
1444
1462
|
):
|
1445
|
-
chat_model: ChatModel = await ConversationAdapters.aget_default_chat_model(user, agent_chat_model)
|
1463
|
+
chat_model: ChatModel = await ConversationAdapters.aget_default_chat_model(user, agent_chat_model, fast=fast_model)
|
1446
1464
|
vision_available = chat_model.vision_enabled
|
1447
1465
|
if not vision_available and query_images:
|
1448
1466
|
logger.warning(f"Vision is not enabled for default model: {chat_model.name}.")
|
@@ -1463,16 +1481,16 @@ async def send_message_to_model_wrapper(
|
|
1463
1481
|
|
1464
1482
|
truncated_messages = generate_chatml_messages_with_context(
|
1465
1483
|
user_message=query,
|
1484
|
+
query_files=query_files,
|
1485
|
+
query_images=query_images,
|
1466
1486
|
context_message=context,
|
1467
|
-
system_message=system_message,
|
1468
1487
|
chat_history=chat_history,
|
1488
|
+
system_message=system_message,
|
1469
1489
|
model_name=chat_model_name,
|
1490
|
+
model_type=model_type,
|
1470
1491
|
tokenizer_name=tokenizer,
|
1471
1492
|
max_prompt_size=max_tokens,
|
1472
1493
|
vision_enabled=vision_available,
|
1473
|
-
query_images=query_images,
|
1474
|
-
model_type=model_type,
|
1475
|
-
query_files=query_files,
|
1476
1494
|
)
|
1477
1495
|
|
1478
1496
|
if model_type == ChatModel.ModelType.OPENAI:
|
@@ -1540,14 +1558,14 @@ def send_message_to_model_wrapper_sync(
|
|
1540
1558
|
|
1541
1559
|
truncated_messages = generate_chatml_messages_with_context(
|
1542
1560
|
user_message=message,
|
1543
|
-
|
1561
|
+
query_files=query_files,
|
1562
|
+
query_images=query_images,
|
1544
1563
|
chat_history=chat_history,
|
1564
|
+
system_message=system_message,
|
1545
1565
|
model_name=chat_model_name,
|
1566
|
+
model_type=model_type,
|
1546
1567
|
max_prompt_size=max_tokens,
|
1547
1568
|
vision_enabled=vision_available,
|
1548
|
-
model_type=model_type,
|
1549
|
-
query_images=query_images,
|
1550
|
-
query_files=query_files,
|
1551
1569
|
)
|
1552
1570
|
|
1553
1571
|
if model_type == ChatModel.ModelType.OPENAI:
|
@@ -1585,6 +1603,105 @@ def send_message_to_model_wrapper_sync(
|
|
1585
1603
|
raise HTTPException(status_code=500, detail="Invalid conversation config")
|
1586
1604
|
|
1587
1605
|
|
1606
|
+
def build_conversation_context(
|
1607
|
+
# Query and Context
|
1608
|
+
user_query: str,
|
1609
|
+
references: List[Dict],
|
1610
|
+
online_results: Dict[str, Dict],
|
1611
|
+
code_results: Dict[str, Dict],
|
1612
|
+
operator_results: List[OperatorRun],
|
1613
|
+
query_files: str = None,
|
1614
|
+
query_images: Optional[List[str]] = None,
|
1615
|
+
generated_asset_results: Dict[str, Dict] = {},
|
1616
|
+
program_execution_context: List[str] = None,
|
1617
|
+
chat_history: List[ChatMessageModel] = [],
|
1618
|
+
location_data: LocationData = None,
|
1619
|
+
user_name: str = None,
|
1620
|
+
# Model config
|
1621
|
+
agent: Agent = None,
|
1622
|
+
model_name: str = None,
|
1623
|
+
model_type: ChatModel.ModelType = None,
|
1624
|
+
max_prompt_size: int = None,
|
1625
|
+
tokenizer_name: str = None,
|
1626
|
+
vision_available: bool = False,
|
1627
|
+
) -> List[ChatMessage]:
|
1628
|
+
"""
|
1629
|
+
Construct system, context and chatml messages for chat response.
|
1630
|
+
Share common logic across different model types.
|
1631
|
+
|
1632
|
+
Returns:
|
1633
|
+
List of ChatMessages with context
|
1634
|
+
"""
|
1635
|
+
# Initialize Variables
|
1636
|
+
current_date = datetime.now()
|
1637
|
+
|
1638
|
+
# Build system prompt
|
1639
|
+
if agent and agent.personality:
|
1640
|
+
system_prompt = prompts.custom_personality.format(
|
1641
|
+
name=agent.name,
|
1642
|
+
bio=agent.personality,
|
1643
|
+
current_date=current_date.strftime("%Y-%m-%d"),
|
1644
|
+
day_of_week=current_date.strftime("%A"),
|
1645
|
+
)
|
1646
|
+
else:
|
1647
|
+
system_prompt = prompts.personality.format(
|
1648
|
+
current_date=current_date.strftime("%Y-%m-%d"),
|
1649
|
+
day_of_week=current_date.strftime("%A"),
|
1650
|
+
)
|
1651
|
+
|
1652
|
+
# Add Gemini-specific personality enhancement
|
1653
|
+
if model_type == ChatModel.ModelType.GOOGLE:
|
1654
|
+
system_prompt += f"\n\n{prompts.gemini_verbose_language_personality}"
|
1655
|
+
|
1656
|
+
# Add location context if available
|
1657
|
+
if location_data:
|
1658
|
+
location_prompt = prompts.user_location.format(location=f"{location_data}")
|
1659
|
+
system_prompt += f"\n{location_prompt}"
|
1660
|
+
|
1661
|
+
# Add user name context if available
|
1662
|
+
if user_name:
|
1663
|
+
user_name_prompt = prompts.user_name.format(name=user_name)
|
1664
|
+
system_prompt += f"\n{user_name_prompt}"
|
1665
|
+
|
1666
|
+
# Build context message
|
1667
|
+
context_message = ""
|
1668
|
+
if not is_none_or_empty(references):
|
1669
|
+
context_message = f"{prompts.notes_conversation.format(references=yaml_dump(references))}\n\n"
|
1670
|
+
if not is_none_or_empty(online_results):
|
1671
|
+
context_message += f"{prompts.online_search_conversation.format(online_results=yaml_dump(online_results))}\n\n"
|
1672
|
+
if not is_none_or_empty(code_results):
|
1673
|
+
context_message += (
|
1674
|
+
f"{prompts.code_executed_context.format(code_results=truncate_code_context(code_results))}\n\n"
|
1675
|
+
)
|
1676
|
+
if not is_none_or_empty(operator_results):
|
1677
|
+
operator_content = [
|
1678
|
+
{"query": oc.query, "response": oc.response, "webpages": oc.webpages} for oc in operator_results
|
1679
|
+
]
|
1680
|
+
context_message += (
|
1681
|
+
f"{prompts.operator_execution_context.format(operator_results=yaml_dump(operator_content))}\n\n"
|
1682
|
+
)
|
1683
|
+
context_message = context_message.strip()
|
1684
|
+
|
1685
|
+
# Generate the chatml messages
|
1686
|
+
messages = generate_chatml_messages_with_context(
|
1687
|
+
user_message=user_query,
|
1688
|
+
query_files=query_files,
|
1689
|
+
query_images=query_images,
|
1690
|
+
context_message=context_message,
|
1691
|
+
generated_asset_results=generated_asset_results,
|
1692
|
+
program_execution_context=program_execution_context,
|
1693
|
+
chat_history=chat_history,
|
1694
|
+
system_message=system_prompt,
|
1695
|
+
model_name=model_name,
|
1696
|
+
model_type=model_type,
|
1697
|
+
max_prompt_size=max_prompt_size,
|
1698
|
+
tokenizer_name=tokenizer_name,
|
1699
|
+
vision_enabled=vision_available,
|
1700
|
+
)
|
1701
|
+
|
1702
|
+
return messages
|
1703
|
+
|
1704
|
+
|
1588
1705
|
async def agenerate_chat_response(
|
1589
1706
|
q: str,
|
1590
1707
|
chat_history: List[ChatMessageModel],
|
@@ -1599,7 +1716,6 @@ async def agenerate_chat_response(
|
|
1599
1716
|
user_name: Optional[str] = None,
|
1600
1717
|
query_images: Optional[List[str]] = None,
|
1601
1718
|
query_files: str = None,
|
1602
|
-
raw_generated_files: List[FileAttachment] = [],
|
1603
1719
|
program_execution_context: List[str] = [],
|
1604
1720
|
generated_asset_results: Dict[str, Dict] = {},
|
1605
1721
|
is_subscribed: bool = False,
|
@@ -1633,34 +1749,39 @@ async def agenerate_chat_response(
|
|
1633
1749
|
chat_model = vision_enabled_config
|
1634
1750
|
vision_available = True
|
1635
1751
|
|
1752
|
+
# Build shared conversation context and generate chatml messages
|
1753
|
+
messages = build_conversation_context(
|
1754
|
+
user_query=query_to_run,
|
1755
|
+
references=compiled_references,
|
1756
|
+
online_results=online_results,
|
1757
|
+
code_results=code_results,
|
1758
|
+
operator_results=operator_results,
|
1759
|
+
query_files=query_files,
|
1760
|
+
query_images=query_images,
|
1761
|
+
generated_asset_results=generated_asset_results,
|
1762
|
+
program_execution_context=program_execution_context,
|
1763
|
+
chat_history=chat_history,
|
1764
|
+
location_data=location_data,
|
1765
|
+
user_name=user_name,
|
1766
|
+
agent=agent,
|
1767
|
+
model_type=chat_model.model_type,
|
1768
|
+
model_name=chat_model.name,
|
1769
|
+
max_prompt_size=max_prompt_size,
|
1770
|
+
tokenizer_name=chat_model.tokenizer,
|
1771
|
+
vision_available=vision_available,
|
1772
|
+
)
|
1773
|
+
|
1636
1774
|
if chat_model.model_type == ChatModel.ModelType.OPENAI:
|
1637
1775
|
openai_chat_config = chat_model.ai_model_api
|
1638
1776
|
api_key = openai_chat_config.api_key
|
1639
1777
|
chat_model_name = chat_model.name
|
1640
1778
|
chat_response_generator = converse_openai(
|
1641
|
-
# Query
|
1642
|
-
|
1643
|
-
# Context
|
1644
|
-
references=compiled_references,
|
1645
|
-
online_results=online_results,
|
1646
|
-
code_results=code_results,
|
1647
|
-
operator_results=operator_results,
|
1648
|
-
query_images=query_images,
|
1649
|
-
query_files=query_files,
|
1650
|
-
generated_files=raw_generated_files,
|
1651
|
-
generated_asset_results=generated_asset_results,
|
1652
|
-
program_execution_context=program_execution_context,
|
1653
|
-
location_data=location_data,
|
1654
|
-
user_name=user_name,
|
1655
|
-
chat_history=chat_history,
|
1779
|
+
# Query + Context Messages
|
1780
|
+
messages,
|
1656
1781
|
# Model
|
1657
1782
|
model=chat_model_name,
|
1658
1783
|
api_key=api_key,
|
1659
1784
|
api_base_url=openai_chat_config.api_base_url,
|
1660
|
-
max_prompt_size=max_prompt_size,
|
1661
|
-
tokenizer_name=chat_model.tokenizer,
|
1662
|
-
agent=agent,
|
1663
|
-
vision_available=vision_available,
|
1664
1785
|
deepthought=deepthought,
|
1665
1786
|
tracer=tracer,
|
1666
1787
|
)
|
@@ -1669,29 +1790,12 @@ async def agenerate_chat_response(
|
|
1669
1790
|
api_key = chat_model.ai_model_api.api_key
|
1670
1791
|
api_base_url = chat_model.ai_model_api.api_base_url
|
1671
1792
|
chat_response_generator = converse_anthropic(
|
1672
|
-
# Query
|
1673
|
-
|
1674
|
-
# Context
|
1675
|
-
references=compiled_references,
|
1676
|
-
online_results=online_results,
|
1677
|
-
code_results=code_results,
|
1678
|
-
operator_results=operator_results,
|
1679
|
-
query_images=query_images,
|
1680
|
-
query_files=query_files,
|
1681
|
-
generated_files=raw_generated_files,
|
1682
|
-
generated_asset_results=generated_asset_results,
|
1683
|
-
program_execution_context=program_execution_context,
|
1684
|
-
location_data=location_data,
|
1685
|
-
user_name=user_name,
|
1686
|
-
chat_history=chat_history,
|
1793
|
+
# Query + Context Messages
|
1794
|
+
messages,
|
1687
1795
|
# Model
|
1688
1796
|
model=chat_model.name,
|
1689
1797
|
api_key=api_key,
|
1690
1798
|
api_base_url=api_base_url,
|
1691
|
-
max_prompt_size=max_prompt_size,
|
1692
|
-
tokenizer_name=chat_model.tokenizer,
|
1693
|
-
agent=agent,
|
1694
|
-
vision_available=vision_available,
|
1695
1799
|
deepthought=deepthought,
|
1696
1800
|
tracer=tracer,
|
1697
1801
|
)
|
@@ -1699,29 +1803,12 @@ async def agenerate_chat_response(
|
|
1699
1803
|
api_key = chat_model.ai_model_api.api_key
|
1700
1804
|
api_base_url = chat_model.ai_model_api.api_base_url
|
1701
1805
|
chat_response_generator = converse_gemini(
|
1702
|
-
# Query
|
1703
|
-
|
1704
|
-
# Context
|
1705
|
-
references=compiled_references,
|
1706
|
-
online_results=online_results,
|
1707
|
-
code_results=code_results,
|
1708
|
-
operator_results=operator_results,
|
1709
|
-
query_images=query_images,
|
1710
|
-
query_files=query_files,
|
1711
|
-
generated_files=raw_generated_files,
|
1712
|
-
generated_asset_results=generated_asset_results,
|
1713
|
-
program_execution_context=program_execution_context,
|
1714
|
-
location_data=location_data,
|
1715
|
-
user_name=user_name,
|
1716
|
-
chat_history=chat_history,
|
1806
|
+
# Query + Context Messages
|
1807
|
+
messages,
|
1717
1808
|
# Model
|
1718
1809
|
model=chat_model.name,
|
1719
1810
|
api_key=api_key,
|
1720
1811
|
api_base_url=api_base_url,
|
1721
|
-
max_prompt_size=max_prompt_size,
|
1722
|
-
tokenizer_name=chat_model.tokenizer,
|
1723
|
-
agent=agent,
|
1724
|
-
vision_available=vision_available,
|
1725
1812
|
deepthought=deepthought,
|
1726
1813
|
tracer=tracer,
|
1727
1814
|
)
|