khoj 1.30.11.dev15__py3-none-any.whl → 1.30.11.dev46__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/admin.py +3 -10
- khoj/database/migrations/0075_migrate_generated_assets_and_validate.py +85 -0
- khoj/database/models/__init__.py +164 -31
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1603-f5babe72ba9f6a59.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5538-0ea2d3944ca051e1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-2ffa7560aebff9a1.js → page-f5c0801b27a8e95e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-b0a6a6ed2267c1a2.js → page-8691f6c09a0acd44.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/{page-02f8616bba3e449e.js → page-135d56dd4263e40d.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-3ffd8f0934b896f3.js → page-e79ace822d51557b.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-cae84c87073877f0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/{page-059f237514f77628.js → page-e8b578d155550386.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-32e9423bede5b4a1.js → page-b6c835050c970be7.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-6f4879fbbf8b90f7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-0b8d90dc57dbc1d8.js → page-635635e4fb39fe29.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-062298330010d2aa.js → webpack-5203c3872078c10c.js} +1 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +1 -0
- khoj/interface/compiled/_next/static/css/edd3abaf11580924.css +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/assets/icons/khoj_lantern.svg +100 -0
- 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/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/conversation/anthropic/anthropic_chat.py +11 -3
- khoj/processor/conversation/google/gemini_chat.py +11 -3
- khoj/processor/conversation/offline/chat_model.py +6 -2
- khoj/processor/conversation/openai/gpt.py +10 -2
- khoj/processor/conversation/prompts.py +18 -0
- khoj/processor/conversation/utils.py +82 -26
- khoj/processor/image/generate.py +10 -13
- khoj/routers/api_chat.py +49 -98
- khoj/routers/helpers.py +41 -1
- {khoj-1.30.11.dev15.dist-info → khoj-1.30.11.dev46.dist-info}/METADATA +1 -1
- {khoj-1.30.11.dev15.dist-info → khoj-1.30.11.dev46.dist-info}/RECORD +49 -47
- khoj/interface/compiled/_next/static/chunks/1603-c68d44bc4ae6039a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5538-e5f3c9f4d67a64b9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-2ca475462c0b2176.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-592e8c470f2c2084.js +0 -1
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.css +0 -1
- khoj/interface/compiled/_next/static/css/9d45de78fba367c1.css +0 -1
- /khoj/interface/compiled/_next/static/{HwvgU9Hbpk8cDtZdz8u6Z → VhbBwTudxbu82AwZhVKwF}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{HwvgU9Hbpk8cDtZdz8u6Z → VhbBwTudxbu82AwZhVKwF}/_ssgManifest.js +0 -0
- {khoj-1.30.11.dev15.dist-info → khoj-1.30.11.dev46.dist-info}/WHEEL +0 -0
- {khoj-1.30.11.dev15.dist-info → khoj-1.30.11.dev46.dist-info}/entry_points.txt +0 -0
- {khoj-1.30.11.dev15.dist-info → khoj-1.30.11.dev46.dist-info}/licenses/LICENSE +0 -0
@@ -154,7 +154,7 @@ def construct_chat_history(conversation_history: dict, n: int = 4, agent_name="A
|
|
154
154
|
chat_history += f'{agent_name}: {{"queries": {chat["intent"].get("inferred-queries")}}}\n'
|
155
155
|
|
156
156
|
chat_history += f"{agent_name}: {chat['message']}\n\n"
|
157
|
-
elif chat["by"] == "khoj" and
|
157
|
+
elif chat["by"] == "khoj" and chat.get("images"):
|
158
158
|
chat_history += f"User: {chat['intent']['query']}\n"
|
159
159
|
chat_history += f"{agent_name}: [generated image redacted for space]\n"
|
160
160
|
elif chat["by"] == "khoj" and ("excalidraw" in chat["intent"].get("type")):
|
@@ -213,6 +213,7 @@ class ChatEvent(Enum):
|
|
213
213
|
END_LLM_RESPONSE = "end_llm_response"
|
214
214
|
MESSAGE = "message"
|
215
215
|
REFERENCES = "references"
|
216
|
+
GENERATED_ASSETS = "generated_assets"
|
216
217
|
STATUS = "status"
|
217
218
|
METADATA = "metadata"
|
218
219
|
USAGE = "usage"
|
@@ -225,7 +226,6 @@ def message_to_log(
|
|
225
226
|
user_message_metadata={},
|
226
227
|
khoj_message_metadata={},
|
227
228
|
conversation_log=[],
|
228
|
-
train_of_thought=[],
|
229
229
|
):
|
230
230
|
"""Create json logs from messages, metadata for conversation log"""
|
231
231
|
default_khoj_message_metadata = {
|
@@ -234,6 +234,10 @@ def message_to_log(
|
|
234
234
|
}
|
235
235
|
khoj_response_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
236
236
|
|
237
|
+
# Filter out any fields that are set to None
|
238
|
+
user_message_metadata = {k: v for k, v in user_message_metadata.items() if v is not None}
|
239
|
+
khoj_message_metadata = {k: v for k, v in khoj_message_metadata.items() if v is not None}
|
240
|
+
|
237
241
|
# Create json log from Human's message
|
238
242
|
human_log = merge_dicts({"message": user_message, "by": "you"}, user_message_metadata)
|
239
243
|
|
@@ -261,31 +265,41 @@ def save_to_conversation_log(
|
|
261
265
|
automation_id: str = None,
|
262
266
|
query_images: List[str] = None,
|
263
267
|
raw_query_files: List[FileAttachment] = [],
|
268
|
+
generated_images: List[str] = [],
|
269
|
+
raw_generated_files: List[FileAttachment] = [],
|
270
|
+
generated_excalidraw_diagram: str = None,
|
264
271
|
train_of_thought: List[Any] = [],
|
265
272
|
tracer: Dict[str, Any] = {},
|
266
273
|
):
|
267
274
|
user_message_time = user_message_time or datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
268
275
|
turn_id = tracer.get("mid") or str(uuid.uuid4())
|
276
|
+
|
277
|
+
user_message_metadata = {"created": user_message_time, "images": query_images, "turnId": turn_id}
|
278
|
+
|
279
|
+
if raw_query_files and len(raw_query_files) > 0:
|
280
|
+
user_message_metadata["queryFiles"] = [file.model_dump(mode="json") for file in raw_query_files]
|
281
|
+
|
282
|
+
khoj_message_metadata = {
|
283
|
+
"context": compiled_references,
|
284
|
+
"intent": {"inferred-queries": inferred_queries, "type": intent_type},
|
285
|
+
"onlineContext": online_results,
|
286
|
+
"codeContext": code_results,
|
287
|
+
"automationId": automation_id,
|
288
|
+
"trainOfThought": train_of_thought,
|
289
|
+
"turnId": turn_id,
|
290
|
+
"images": generated_images,
|
291
|
+
"queryFiles": [file.model_dump(mode="json") for file in raw_generated_files],
|
292
|
+
}
|
293
|
+
|
294
|
+
if generated_excalidraw_diagram:
|
295
|
+
khoj_message_metadata["excalidrawDiagram"] = generated_excalidraw_diagram
|
296
|
+
|
269
297
|
updated_conversation = message_to_log(
|
270
298
|
user_message=q,
|
271
299
|
chat_response=chat_response,
|
272
|
-
user_message_metadata=
|
273
|
-
|
274
|
-
"images": query_images,
|
275
|
-
"turnId": turn_id,
|
276
|
-
"queryFiles": [file.model_dump(mode="json") for file in raw_query_files],
|
277
|
-
},
|
278
|
-
khoj_message_metadata={
|
279
|
-
"context": compiled_references,
|
280
|
-
"intent": {"inferred-queries": inferred_queries, "type": intent_type},
|
281
|
-
"onlineContext": online_results,
|
282
|
-
"codeContext": code_results,
|
283
|
-
"automationId": automation_id,
|
284
|
-
"trainOfThought": train_of_thought,
|
285
|
-
"turnId": turn_id,
|
286
|
-
},
|
300
|
+
user_message_metadata=user_message_metadata,
|
301
|
+
khoj_message_metadata=khoj_message_metadata,
|
287
302
|
conversation_log=meta_log.get("chat", []),
|
288
|
-
train_of_thought=train_of_thought,
|
289
303
|
)
|
290
304
|
ConversationAdapters.save_conversation(
|
291
305
|
user,
|
@@ -303,13 +317,13 @@ def save_to_conversation_log(
|
|
303
317
|
Saved Conversation Turn
|
304
318
|
You ({user.username}): "{q}"
|
305
319
|
|
306
|
-
Khoj: "{
|
320
|
+
Khoj: "{chat_response}"
|
307
321
|
""".strip()
|
308
322
|
)
|
309
323
|
|
310
324
|
|
311
325
|
def construct_structured_message(
|
312
|
-
message: str, images: list[str], model_type: str, vision_enabled: bool, attached_file_context: str
|
326
|
+
message: str, images: list[str], model_type: str, vision_enabled: bool, attached_file_context: str = None
|
313
327
|
):
|
314
328
|
"""
|
315
329
|
Format messages into appropriate multimedia format for supported chat model types
|
@@ -327,7 +341,8 @@ def construct_structured_message(
|
|
327
341
|
constructed_messages.append({"type": "text", "text": attached_file_context})
|
328
342
|
if vision_enabled and images:
|
329
343
|
for image in images:
|
330
|
-
|
344
|
+
if image.startswith("https://"):
|
345
|
+
constructed_messages.append({"type": "image_url", "image_url": {"url": image}})
|
331
346
|
return constructed_messages
|
332
347
|
|
333
348
|
if not is_none_or_empty(attached_file_context):
|
@@ -365,6 +380,10 @@ def generate_chatml_messages_with_context(
|
|
365
380
|
model_type="",
|
366
381
|
context_message="",
|
367
382
|
query_files: str = None,
|
383
|
+
generated_images: Optional[list[str]] = None,
|
384
|
+
generated_files: List[FileAttachment] = None,
|
385
|
+
generated_excalidraw_diagram: str = None,
|
386
|
+
program_execution_context: List[str] = [],
|
368
387
|
):
|
369
388
|
"""Generate chat messages with appropriate context from previous conversation to send to the chat model"""
|
370
389
|
# Set max prompt size from user config or based on pre-configured for model and machine specs
|
@@ -384,6 +403,7 @@ def generate_chatml_messages_with_context(
|
|
384
403
|
message_attached_files = ""
|
385
404
|
|
386
405
|
chat_message = chat.get("message")
|
406
|
+
role = "user" if chat["by"] == "you" else "assistant"
|
387
407
|
|
388
408
|
if chat["by"] == "khoj" and "excalidraw" in chat["intent"].get("type", ""):
|
389
409
|
chat_message = chat["intent"].get("inferred-queries")[0]
|
@@ -404,7 +424,7 @@ def generate_chatml_messages_with_context(
|
|
404
424
|
query_files_dict[file["name"]] = file["content"]
|
405
425
|
|
406
426
|
message_attached_files = gather_raw_query_files(query_files_dict)
|
407
|
-
chatml_messages.append(ChatMessage(content=message_attached_files, role=
|
427
|
+
chatml_messages.append(ChatMessage(content=message_attached_files, role=role))
|
408
428
|
|
409
429
|
if not is_none_or_empty(chat.get("onlineContext")):
|
410
430
|
message_context += f"{prompts.online_search_conversation.format(online_results=chat.get('onlineContext'))}"
|
@@ -413,10 +433,20 @@ def generate_chatml_messages_with_context(
|
|
413
433
|
reconstructed_context_message = ChatMessage(content=message_context, role="user")
|
414
434
|
chatml_messages.insert(0, reconstructed_context_message)
|
415
435
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
436
|
+
if chat.get("images"):
|
437
|
+
if role == "assistant":
|
438
|
+
# Issue: the assistant role cannot accept an image as a message content, so send it in a separate user message.
|
439
|
+
file_attachment_message = construct_structured_message(
|
440
|
+
message=prompts.generated_image_attachment.format(),
|
441
|
+
images=chat.get("images"),
|
442
|
+
model_type=model_type,
|
443
|
+
vision_enabled=vision_enabled,
|
444
|
+
)
|
445
|
+
chatml_messages.append(ChatMessage(content=file_attachment_message, role="user"))
|
446
|
+
else:
|
447
|
+
message_content = construct_structured_message(
|
448
|
+
chat_message, chat.get("images"), model_type, vision_enabled
|
449
|
+
)
|
420
450
|
|
421
451
|
reconstructed_message = ChatMessage(content=message_content, role=role)
|
422
452
|
chatml_messages.insert(0, reconstructed_message)
|
@@ -425,6 +455,7 @@ def generate_chatml_messages_with_context(
|
|
425
455
|
break
|
426
456
|
|
427
457
|
messages = []
|
458
|
+
|
428
459
|
if not is_none_or_empty(user_message):
|
429
460
|
messages.append(
|
430
461
|
ChatMessage(
|
@@ -437,6 +468,31 @@ def generate_chatml_messages_with_context(
|
|
437
468
|
if not is_none_or_empty(context_message):
|
438
469
|
messages.append(ChatMessage(content=context_message, role="user"))
|
439
470
|
|
471
|
+
if generated_images:
|
472
|
+
messages.append(
|
473
|
+
ChatMessage(
|
474
|
+
content=construct_structured_message(
|
475
|
+
prompts.generated_image_attachment.format(), generated_images, model_type, vision_enabled
|
476
|
+
),
|
477
|
+
role="user",
|
478
|
+
)
|
479
|
+
)
|
480
|
+
|
481
|
+
if generated_files:
|
482
|
+
message_attached_files = gather_raw_query_files({file.name: file.content for file in generated_files})
|
483
|
+
messages.append(ChatMessage(content=message_attached_files, role="assistant"))
|
484
|
+
|
485
|
+
if generated_excalidraw_diagram:
|
486
|
+
messages.append(ChatMessage(content=prompts.generated_diagram_attachment.format(), role="assistant"))
|
487
|
+
|
488
|
+
if program_execution_context:
|
489
|
+
messages.append(
|
490
|
+
ChatMessage(
|
491
|
+
content=prompts.additional_program_context.format(context="\n".join(program_execution_context)),
|
492
|
+
role="assistant",
|
493
|
+
)
|
494
|
+
)
|
495
|
+
|
440
496
|
if len(chatml_messages) > 0:
|
441
497
|
messages += chatml_messages
|
442
498
|
|
khoj/processor/image/generate.py
CHANGED
@@ -12,7 +12,7 @@ from khoj.database.models import Agent, KhojUser, TextToImageModelConfig
|
|
12
12
|
from khoj.routers.helpers import ChatEvent, generate_better_image_prompt
|
13
13
|
from khoj.routers.storage import upload_image
|
14
14
|
from khoj.utils import state
|
15
|
-
from khoj.utils.helpers import
|
15
|
+
from khoj.utils.helpers import convert_image_to_webp, timer
|
16
16
|
from khoj.utils.rawconfig import LocationData
|
17
17
|
|
18
18
|
logger = logging.getLogger(__name__)
|
@@ -34,14 +34,13 @@ async def text_to_image(
|
|
34
34
|
status_code = 200
|
35
35
|
image = None
|
36
36
|
image_url = None
|
37
|
-
intent_type = ImageIntentType.TEXT_TO_IMAGE_V3
|
38
37
|
|
39
38
|
text_to_image_config = await ConversationAdapters.aget_user_text_to_image_model(user)
|
40
39
|
if not text_to_image_config:
|
41
40
|
# If the user has not configured a text to image model, return an unsupported on server error
|
42
41
|
status_code = 501
|
43
42
|
message = "Failed to generate image. Setup image generation on the server."
|
44
|
-
yield image_url or image, status_code, message
|
43
|
+
yield image_url or image, status_code, message
|
45
44
|
return
|
46
45
|
|
47
46
|
text2image_model = text_to_image_config.model_name
|
@@ -50,8 +49,8 @@ async def text_to_image(
|
|
50
49
|
if chat["by"] == "khoj" and chat["intent"].get("type") in ["remember", "reminder"]:
|
51
50
|
chat_history += f"Q: {chat['intent']['query']}\n"
|
52
51
|
chat_history += f"A: {chat['message']}\n"
|
53
|
-
elif chat["by"] == "khoj" and
|
54
|
-
chat_history += f"Q:
|
52
|
+
elif chat["by"] == "khoj" and chat.get("images"):
|
53
|
+
chat_history += f"Q: {chat['intent']['query']}\n"
|
55
54
|
chat_history += f"A: Improved Prompt: {chat['intent']['inferred-queries'][0]}\n"
|
56
55
|
|
57
56
|
if send_status_func:
|
@@ -92,31 +91,29 @@ async def text_to_image(
|
|
92
91
|
logger.error(f"Image Generation blocked by OpenAI: {e}")
|
93
92
|
status_code = e.status_code # type: ignore
|
94
93
|
message = f"Image generation blocked by OpenAI due to policy violation" # type: ignore
|
95
|
-
yield image_url or image, status_code, message
|
94
|
+
yield image_url or image, status_code, message
|
96
95
|
return
|
97
96
|
else:
|
98
97
|
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
99
98
|
message = f"Image generation failed using OpenAI" # type: ignore
|
100
99
|
status_code = e.status_code # type: ignore
|
101
|
-
yield image_url or image, status_code, message
|
100
|
+
yield image_url or image, status_code, message
|
102
101
|
return
|
103
102
|
except requests.RequestException as e:
|
104
103
|
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
105
104
|
message = f"Image generation using {text2image_model} via {text_to_image_config.model_type} failed due to a network error."
|
106
105
|
status_code = 502
|
107
|
-
yield image_url or image, status_code, message
|
106
|
+
yield image_url or image, status_code, message
|
108
107
|
return
|
109
108
|
|
110
109
|
# Decide how to store the generated image
|
111
110
|
with timer("Upload image to S3", logger):
|
112
111
|
image_url = upload_image(webp_image_bytes, user.uuid)
|
113
|
-
|
114
|
-
|
115
|
-
else:
|
116
|
-
intent_type = ImageIntentType.TEXT_TO_IMAGE_V3
|
112
|
+
|
113
|
+
if not image_url:
|
117
114
|
image = base64.b64encode(webp_image_bytes).decode("utf-8")
|
118
115
|
|
119
|
-
yield image_url or image, status_code, image_prompt
|
116
|
+
yield image_url or image, status_code, image_prompt
|
120
117
|
|
121
118
|
|
122
119
|
def generate_image_with_openai(
|
khoj/routers/api_chat.py
CHANGED
@@ -77,6 +77,7 @@ from khoj.utils.helpers import (
|
|
77
77
|
)
|
78
78
|
from khoj.utils.rawconfig import (
|
79
79
|
ChatRequestBody,
|
80
|
+
FileAttachment,
|
80
81
|
FileFilterRequest,
|
81
82
|
FilesFilterRequest,
|
82
83
|
LocationData,
|
@@ -770,6 +771,11 @@ async def chat(
|
|
770
771
|
file_filters = conversation.file_filters if conversation and conversation.file_filters else []
|
771
772
|
attached_file_context = gather_raw_query_files(query_files)
|
772
773
|
|
774
|
+
generated_images: List[str] = []
|
775
|
+
generated_files: List[FileAttachment] = []
|
776
|
+
generated_excalidraw_diagram: str = None
|
777
|
+
program_execution_context: List[str] = []
|
778
|
+
|
773
779
|
if conversation_commands == [ConversationCommand.Default] or is_automated_task:
|
774
780
|
chosen_io = await aget_data_sources_and_output_format(
|
775
781
|
q,
|
@@ -875,21 +881,17 @@ async def chat(
|
|
875
881
|
async for result in send_llm_response(response, tracer.get("usage")):
|
876
882
|
yield result
|
877
883
|
|
878
|
-
|
879
|
-
|
880
|
-
response_log,
|
881
|
-
|
882
|
-
|
883
|
-
user_message_time,
|
884
|
-
intent_type="summarize",
|
885
|
-
client_application=request.user.client_app,
|
886
|
-
conversation_id=conversation_id,
|
887
|
-
query_images=uploaded_images,
|
888
|
-
train_of_thought=train_of_thought,
|
889
|
-
raw_query_files=raw_query_files,
|
890
|
-
tracer=tracer,
|
884
|
+
summarized_document = FileAttachment(
|
885
|
+
name="Summarized Document",
|
886
|
+
content=response_log,
|
887
|
+
type="text/plain",
|
888
|
+
size=len(response_log.encode("utf-8")),
|
891
889
|
)
|
892
|
-
|
890
|
+
|
891
|
+
async for result in send_event(ChatEvent.GENERATED_ASSETS, {"files": [summarized_document.model_dump()]}):
|
892
|
+
yield result
|
893
|
+
|
894
|
+
generated_files.append(summarized_document)
|
893
895
|
|
894
896
|
custom_filters = []
|
895
897
|
if conversation_commands == [ConversationCommand.Help]:
|
@@ -1078,6 +1080,7 @@ async def chat(
|
|
1078
1080
|
async for result in send_event(ChatEvent.STATUS, f"**Ran code snippets**: {len(code_results)}"):
|
1079
1081
|
yield result
|
1080
1082
|
except ValueError as e:
|
1083
|
+
program_execution_context.append(f"Failed to run code")
|
1081
1084
|
logger.warning(
|
1082
1085
|
f"Failed to use code tool: {e}. Attempting to respond without code results",
|
1083
1086
|
exc_info=True,
|
@@ -1115,51 +1118,28 @@ async def chat(
|
|
1115
1118
|
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
1116
1119
|
yield result[ChatEvent.STATUS]
|
1117
1120
|
else:
|
1118
|
-
generated_image, status_code, improved_image_prompt
|
1121
|
+
generated_image, status_code, improved_image_prompt = result
|
1119
1122
|
|
1123
|
+
inferred_queries.append(improved_image_prompt)
|
1120
1124
|
if generated_image is None or status_code != 200:
|
1121
|
-
|
1122
|
-
|
1123
|
-
"intentType": intent_type,
|
1124
|
-
"detail": improved_image_prompt,
|
1125
|
-
"image": None,
|
1126
|
-
}
|
1127
|
-
async for result in send_llm_response(json.dumps(content_obj), tracer.get("usage")):
|
1125
|
+
program_execution_context.append(f"Failed to generate image with {improved_image_prompt}")
|
1126
|
+
async for result in send_event(ChatEvent.STATUS, f"Failed to generate image"):
|
1128
1127
|
yield result
|
1129
|
-
|
1128
|
+
else:
|
1129
|
+
generated_images.append(generated_image)
|
1130
1130
|
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
inferred_queries=[improved_image_prompt],
|
1139
|
-
client_application=request.user.client_app,
|
1140
|
-
conversation_id=conversation_id,
|
1141
|
-
compiled_references=compiled_references,
|
1142
|
-
online_results=online_results,
|
1143
|
-
code_results=code_results,
|
1144
|
-
query_images=uploaded_images,
|
1145
|
-
train_of_thought=train_of_thought,
|
1146
|
-
raw_query_files=raw_query_files,
|
1147
|
-
tracer=tracer,
|
1148
|
-
)
|
1149
|
-
content_obj = {
|
1150
|
-
"intentType": intent_type,
|
1151
|
-
"inferredQueries": [improved_image_prompt],
|
1152
|
-
"image": generated_image,
|
1153
|
-
}
|
1154
|
-
async for result in send_llm_response(json.dumps(content_obj), tracer.get("usage")):
|
1155
|
-
yield result
|
1156
|
-
return
|
1131
|
+
async for result in send_event(
|
1132
|
+
ChatEvent.GENERATED_ASSETS,
|
1133
|
+
{
|
1134
|
+
"images": [generated_image],
|
1135
|
+
},
|
1136
|
+
):
|
1137
|
+
yield result
|
1157
1138
|
|
1158
1139
|
if ConversationCommand.Diagram in conversation_commands:
|
1159
1140
|
async for result in send_event(ChatEvent.STATUS, f"Creating diagram"):
|
1160
1141
|
yield result
|
1161
1142
|
|
1162
|
-
intent_type = "excalidraw"
|
1163
1143
|
inferred_queries = []
|
1164
1144
|
diagram_description = ""
|
1165
1145
|
|
@@ -1183,62 +1163,29 @@ async def chat(
|
|
1183
1163
|
if better_diagram_description_prompt and excalidraw_diagram_description:
|
1184
1164
|
inferred_queries.append(better_diagram_description_prompt)
|
1185
1165
|
diagram_description = excalidraw_diagram_description
|
1166
|
+
|
1167
|
+
generated_excalidraw_diagram = diagram_description
|
1168
|
+
|
1169
|
+
async for result in send_event(
|
1170
|
+
ChatEvent.GENERATED_ASSETS,
|
1171
|
+
{
|
1172
|
+
"excalidrawDiagram": excalidraw_diagram_description,
|
1173
|
+
},
|
1174
|
+
):
|
1175
|
+
yield result
|
1186
1176
|
else:
|
1187
1177
|
error_message = "Failed to generate diagram. Please try again later."
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
await sync_to_async(save_to_conversation_log)(
|
1192
|
-
q,
|
1193
|
-
error_message,
|
1194
|
-
user,
|
1195
|
-
meta_log,
|
1196
|
-
user_message_time,
|
1197
|
-
inferred_queries=[better_diagram_description_prompt],
|
1198
|
-
client_application=request.user.client_app,
|
1199
|
-
conversation_id=conversation_id,
|
1200
|
-
compiled_references=compiled_references,
|
1201
|
-
online_results=online_results,
|
1202
|
-
code_results=code_results,
|
1203
|
-
query_images=uploaded_images,
|
1204
|
-
train_of_thought=train_of_thought,
|
1205
|
-
raw_query_files=raw_query_files,
|
1206
|
-
tracer=tracer,
|
1178
|
+
program_execution_context.append(
|
1179
|
+
f"AI attempted to programmatically generate a diagram but failed due to a program issue. Generally, it is able to do so, but encountered a system issue this time. AI can suggest text description or rendering of the diagram or user can try again with a simpler prompt."
|
1207
1180
|
)
|
1208
|
-
return
|
1209
|
-
|
1210
|
-
content_obj = {
|
1211
|
-
"intentType": intent_type,
|
1212
|
-
"inferredQueries": inferred_queries,
|
1213
|
-
"image": diagram_description,
|
1214
|
-
}
|
1215
1181
|
|
1216
|
-
|
1217
|
-
|
1218
|
-
excalidraw_diagram_description,
|
1219
|
-
user,
|
1220
|
-
meta_log,
|
1221
|
-
user_message_time,
|
1222
|
-
intent_type="excalidraw",
|
1223
|
-
inferred_queries=[better_diagram_description_prompt],
|
1224
|
-
client_application=request.user.client_app,
|
1225
|
-
conversation_id=conversation_id,
|
1226
|
-
compiled_references=compiled_references,
|
1227
|
-
online_results=online_results,
|
1228
|
-
code_results=code_results,
|
1229
|
-
query_images=uploaded_images,
|
1230
|
-
train_of_thought=train_of_thought,
|
1231
|
-
raw_query_files=raw_query_files,
|
1232
|
-
tracer=tracer,
|
1233
|
-
)
|
1234
|
-
|
1235
|
-
async for result in send_llm_response(json.dumps(content_obj), tracer.get("usage")):
|
1236
|
-
yield result
|
1237
|
-
return
|
1182
|
+
async for result in send_event(ChatEvent.STATUS, error_message):
|
1183
|
+
yield result
|
1238
1184
|
|
1239
1185
|
## Generate Text Output
|
1240
1186
|
async for result in send_event(ChatEvent.STATUS, f"**Generating a well-informed response**"):
|
1241
1187
|
yield result
|
1188
|
+
|
1242
1189
|
llm_response, chat_metadata = await agenerate_chat_response(
|
1243
1190
|
defiltered_query,
|
1244
1191
|
meta_log,
|
@@ -1258,6 +1205,10 @@ async def chat(
|
|
1258
1205
|
train_of_thought,
|
1259
1206
|
attached_file_context,
|
1260
1207
|
raw_query_files,
|
1208
|
+
generated_images,
|
1209
|
+
generated_files,
|
1210
|
+
generated_excalidraw_diagram,
|
1211
|
+
program_execution_context,
|
1261
1212
|
tracer,
|
1262
1213
|
)
|
1263
1214
|
|
khoj/routers/helpers.py
CHANGED
@@ -1185,6 +1185,10 @@ def generate_chat_response(
|
|
1185
1185
|
train_of_thought: List[Any] = [],
|
1186
1186
|
query_files: str = None,
|
1187
1187
|
raw_query_files: List[FileAttachment] = None,
|
1188
|
+
generated_images: List[str] = None,
|
1189
|
+
raw_generated_files: List[FileAttachment] = [],
|
1190
|
+
generated_excalidraw_diagram: str = None,
|
1191
|
+
program_execution_context: List[str] = [],
|
1188
1192
|
tracer: dict = {},
|
1189
1193
|
) -> Tuple[Union[ThreadedGenerator, Iterator[str]], Dict[str, str]]:
|
1190
1194
|
# Initialize Variables
|
@@ -1208,6 +1212,9 @@ def generate_chat_response(
|
|
1208
1212
|
query_images=query_images,
|
1209
1213
|
train_of_thought=train_of_thought,
|
1210
1214
|
raw_query_files=raw_query_files,
|
1215
|
+
generated_images=generated_images,
|
1216
|
+
raw_generated_files=raw_generated_files,
|
1217
|
+
generated_excalidraw_diagram=generated_excalidraw_diagram,
|
1211
1218
|
tracer=tracer,
|
1212
1219
|
)
|
1213
1220
|
|
@@ -1243,6 +1250,7 @@ def generate_chat_response(
|
|
1243
1250
|
user_name=user_name,
|
1244
1251
|
agent=agent,
|
1245
1252
|
query_files=query_files,
|
1253
|
+
generated_files=raw_generated_files,
|
1246
1254
|
tracer=tracer,
|
1247
1255
|
)
|
1248
1256
|
|
@@ -1269,6 +1277,10 @@ def generate_chat_response(
|
|
1269
1277
|
agent=agent,
|
1270
1278
|
vision_available=vision_available,
|
1271
1279
|
query_files=query_files,
|
1280
|
+
generated_files=raw_generated_files,
|
1281
|
+
generated_images=generated_images,
|
1282
|
+
generated_excalidraw_diagram=generated_excalidraw_diagram,
|
1283
|
+
program_execution_context=program_execution_context,
|
1272
1284
|
tracer=tracer,
|
1273
1285
|
)
|
1274
1286
|
|
@@ -1292,6 +1304,10 @@ def generate_chat_response(
|
|
1292
1304
|
agent=agent,
|
1293
1305
|
vision_available=vision_available,
|
1294
1306
|
query_files=query_files,
|
1307
|
+
generated_files=raw_generated_files,
|
1308
|
+
generated_images=generated_images,
|
1309
|
+
generated_excalidraw_diagram=generated_excalidraw_diagram,
|
1310
|
+
program_execution_context=program_execution_context,
|
1295
1311
|
tracer=tracer,
|
1296
1312
|
)
|
1297
1313
|
elif conversation_config.model_type == ChatModelOptions.ModelType.GOOGLE:
|
@@ -1314,6 +1330,10 @@ def generate_chat_response(
|
|
1314
1330
|
query_images=query_images,
|
1315
1331
|
vision_available=vision_available,
|
1316
1332
|
query_files=query_files,
|
1333
|
+
generated_files=raw_generated_files,
|
1334
|
+
generated_images=generated_images,
|
1335
|
+
generated_excalidraw_diagram=generated_excalidraw_diagram,
|
1336
|
+
program_execution_context=program_execution_context,
|
1317
1337
|
tracer=tracer,
|
1318
1338
|
)
|
1319
1339
|
|
@@ -1785,6 +1805,9 @@ class MessageProcessor:
|
|
1785
1805
|
self.references = {}
|
1786
1806
|
self.usage = {}
|
1787
1807
|
self.raw_response = ""
|
1808
|
+
self.generated_images = []
|
1809
|
+
self.generated_files = []
|
1810
|
+
self.generated_excalidraw_diagram = []
|
1788
1811
|
|
1789
1812
|
def convert_message_chunk_to_json(self, raw_chunk: str) -> Dict[str, Any]:
|
1790
1813
|
if raw_chunk.startswith("{") and raw_chunk.endswith("}"):
|
@@ -1823,6 +1846,16 @@ class MessageProcessor:
|
|
1823
1846
|
self.raw_response += chunk_data
|
1824
1847
|
else:
|
1825
1848
|
self.raw_response += chunk_data
|
1849
|
+
elif chunk_type == ChatEvent.GENERATED_ASSETS:
|
1850
|
+
chunk_data = chunk["data"]
|
1851
|
+
if isinstance(chunk_data, dict):
|
1852
|
+
for key in chunk_data:
|
1853
|
+
if key == "images":
|
1854
|
+
self.generated_images = chunk_data[key]
|
1855
|
+
elif key == "files":
|
1856
|
+
self.generated_files = chunk_data[key]
|
1857
|
+
elif key == "excalidrawDiagram":
|
1858
|
+
self.generated_excalidraw_diagram = chunk_data[key]
|
1826
1859
|
|
1827
1860
|
def handle_json_response(self, json_data: Dict[str, str]) -> str | Dict[str, str]:
|
1828
1861
|
if "image" in json_data or "details" in json_data:
|
@@ -1853,7 +1886,14 @@ async def read_chat_stream(response_iterator: AsyncGenerator[str, None]) -> Dict
|
|
1853
1886
|
if buffer:
|
1854
1887
|
processor.process_message_chunk(buffer)
|
1855
1888
|
|
1856
|
-
return {
|
1889
|
+
return {
|
1890
|
+
"response": processor.raw_response,
|
1891
|
+
"references": processor.references,
|
1892
|
+
"usage": processor.usage,
|
1893
|
+
"images": processor.generated_images,
|
1894
|
+
"files": processor.generated_files,
|
1895
|
+
"excalidrawDiagram": processor.generated_excalidraw_diagram,
|
1896
|
+
}
|
1857
1897
|
|
1858
1898
|
|
1859
1899
|
def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False):
|