diff --git a/src/interface/web/app/common/iconUtils.tsx b/src/interface/web/app/common/iconUtils.tsx
index 4fd7d443..6f1afe81 100644
--- a/src/interface/web/app/common/iconUtils.tsx
+++ b/src/interface/web/app/common/iconUtils.tsx
@@ -215,6 +215,10 @@ export function getIconForSlashCommand(command: string, customClassName: string
return ;
}
+ if (command.includes("code")) {
+ return
;
+ }
+
return ;
}
diff --git a/src/khoj/processor/conversation/utils.py b/src/khoj/processor/conversation/utils.py
index dd43a785..39c1765b 100644
--- a/src/khoj/processor/conversation/utils.py
+++ b/src/khoj/processor/conversation/utils.py
@@ -5,6 +5,7 @@ import math
import mimetypes
import os
import queue
+import re
import uuid
from dataclasses import dataclass
from datetime import datetime
@@ -377,8 +378,10 @@ def generate_chatml_messages_with_context(
message_context = ""
message_attached_files = ""
+ chat_message = chat.get("message")
+
if chat["by"] == "khoj" and "excalidraw" in chat["intent"].get("type", ""):
- message_context += chat.get("intent").get("inferred-queries")[0]
+ chat_message = chat["intent"].get("inferred-queries")[0]
if not is_none_or_empty(chat.get("context")):
references = "\n\n".join(
{
@@ -407,7 +410,7 @@ def generate_chatml_messages_with_context(
role = "user" if chat["by"] == "you" else "assistant"
message_content = construct_structured_message(
- chat["message"], chat.get("images"), model_type, vision_enabled, attached_file_context=query_files
+ chat_message, chat.get("images"), model_type, vision_enabled, attached_file_context=query_files
)
reconstructed_message = ChatMessage(content=message_content, role=role)
@@ -524,7 +527,25 @@ def reciprocal_conversation_to_chatml(message_pair):
def clean_json(response: str):
"""Remove any markdown json codeblock and newline formatting if present. Useful for non schema enforceable models"""
- return response.strip().replace("\n", "").removeprefix("```json").removesuffix("```")
+ try:
+ # Remove markdown code blocks
+ cleaned = response.strip().replace("```json", "").replace("```", "")
+
+ # Find JSON array/object pattern
+ json_match = re.search(r"\[.*\]|\{.*\}", cleaned, re.DOTALL)
+ if not json_match:
+ return ""
+
+ # Extract matched JSON
+ json_str = json_match.group()
+
+ # Validate by parsing
+ json.loads(json_str)
+
+ return json_str.strip()
+
+ except (json.JSONDecodeError, AttributeError):
+ return ""
def clean_code_python(code: str):
diff --git a/src/khoj/routers/api_chat.py b/src/khoj/routers/api_chat.py
index f3402357..bd4b20e0 100644
--- a/src/khoj/routers/api_chat.py
+++ b/src/khoj/routers/api_chat.py
@@ -1171,8 +1171,13 @@ async def chat(
yield result[ChatEvent.STATUS]
else:
better_diagram_description_prompt, excalidraw_diagram_description = result
- inferred_queries.append(better_diagram_description_prompt)
- diagram_description = excalidraw_diagram_description
+ if better_diagram_description_prompt and excalidraw_diagram_description:
+ inferred_queries.append(better_diagram_description_prompt)
+ diagram_description = excalidraw_diagram_description
+ else:
+ async for result in send_llm_response(f"Failed to generate diagram. Please try again later."):
+ yield result
+ return
content_obj = {
"intentType": intent_type,
diff --git a/src/khoj/routers/helpers.py b/src/khoj/routers/helpers.py
index 6275061a..ea1c3506 100644
--- a/src/khoj/routers/helpers.py
+++ b/src/khoj/routers/helpers.py
@@ -784,13 +784,17 @@ async def generate_excalidraw_diagram(
if send_status_func:
async for event in send_status_func(f"**Diagram to Create:**:\n{better_diagram_description_prompt}"):
yield {ChatEvent.STATUS: event}
-
- excalidraw_diagram_description = await generate_excalidraw_diagram_from_description(
- q=better_diagram_description_prompt,
- user=user,
- agent=agent,
- tracer=tracer,
- )
+ try:
+ excalidraw_diagram_description = await generate_excalidraw_diagram_from_description(
+ q=better_diagram_description_prompt,
+ user=user,
+ agent=agent,
+ tracer=tracer,
+ )
+ except Exception as e:
+ logger.error(f"Error generating Excalidraw diagram for {user.email}: {e}", exc_info=True)
+ yield None, None
+ return
yield better_diagram_description_prompt, excalidraw_diagram_description
@@ -876,7 +880,10 @@ async def generate_excalidraw_diagram_from_description(
query=excalidraw_diagram_generation, user=user, tracer=tracer
)
raw_response = clean_json(raw_response)
- response: Dict[str, str] = json.loads(raw_response)
+ try:
+ response: Dict[str, str] = json.loads(raw_response)
+ except Exception:
+ raise AssertionError(f"Invalid response for generating Excalidraw diagram: {raw_response}")
if not response or not isinstance(response, List) or not isinstance(response[0], Dict):
# TODO Some additional validation here that it's a valid Excalidraw diagram
raise AssertionError(f"Invalid response for improving diagram description: {response}")