khoj 1.27.2.dev15__py3-none-any.whl → 1.28.1__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/configure.py +1 -1
- khoj/database/adapters/__init__.py +50 -12
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1034-da58b679fcbb79c1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1467-b331e469fe411347.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1603-c1568f45947e9f2c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3423-ff7402ae1dd66592.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8423-e80647edf6c92c27.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-2beaba7c9bb750bd.js → page-fc492762298e975e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-9b5c77e0b0dd772c.js → page-416ee13a00575c39.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-c70f5b0c722d7627.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-1541d90140794f63.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/{page-4b6008223ea79955.js → page-b269e444fc067759.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/{page-ab2995529ece3140.js → page-7d431ce8e565c7c3.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-7946cabb9c54e22d.js → page-95f56e53f48f0289.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-6a01e07fb244c10c.js → page-4eba6154f7bb9771.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-878569182b3af4c6.js → webpack-33a82ccca02cd2b8.js} +1 -1
- khoj/interface/compiled/_next/static/css/2196fae09c2f906e.css +1 -0
- khoj/interface/compiled/_next/static/css/6bde1f2045622ef7.css +1 -0
- khoj/interface/compiled/_next/static/css/a795ee88875f4853.css +25 -0
- khoj/interface/compiled/_next/static/css/ebef43da1c0651d5.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/conversation/anthropic/anthropic_chat.py +19 -10
- khoj/processor/conversation/anthropic/utils.py +37 -6
- khoj/processor/conversation/google/gemini_chat.py +23 -13
- khoj/processor/conversation/google/utils.py +34 -10
- khoj/processor/conversation/offline/chat_model.py +48 -16
- khoj/processor/conversation/openai/gpt.py +25 -10
- khoj/processor/conversation/openai/utils.py +50 -9
- khoj/processor/conversation/prompts.py +156 -65
- khoj/processor/conversation/utils.py +306 -6
- khoj/processor/embeddings.py +4 -4
- khoj/processor/image/generate.py +2 -0
- khoj/processor/tools/online_search.py +27 -12
- khoj/processor/tools/run_code.py +144 -0
- khoj/routers/api.py +11 -6
- khoj/routers/api_chat.py +213 -111
- khoj/routers/helpers.py +171 -60
- khoj/routers/research.py +320 -0
- khoj/search_filter/date_filter.py +1 -3
- khoj/search_filter/file_filter.py +1 -2
- khoj/search_type/text_search.py +3 -3
- khoj/utils/helpers.py +24 -2
- khoj/utils/yaml.py +4 -0
- {khoj-1.27.2.dev15.dist-info → khoj-1.28.1.dist-info}/METADATA +3 -2
- {khoj-1.27.2.dev15.dist-info → khoj-1.28.1.dist-info}/RECORD +68 -65
- khoj/interface/compiled/_next/static/chunks/1603-b9d95833e0e025e8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2697-61fcba89fd87eab4.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3423-0b533af8bf6ac218.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9479-ff7d8c4dae2014d1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-151232d8417a1ea1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-798904432c2417c4.js +0 -1
- khoj/interface/compiled/_next/static/css/2272c73fc7a3b571.css +0 -1
- khoj/interface/compiled/_next/static/css/553f9cdcc7a2bcd6.css +0 -1
- khoj/interface/compiled/_next/static/css/76d55eb435962b19.css +0 -25
- khoj/interface/compiled/_next/static/css/d738728883c68af8.css +0 -1
- /khoj/interface/compiled/_next/static/{vcyFRDGArOFXwUVotHIuv → JcTomiF3o0dIo4RxHR9Vu}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{vcyFRDGArOFXwUVotHIuv → JcTomiF3o0dIo4RxHR9Vu}/_ssgManifest.js +0 -0
- /khoj/interface/compiled/_next/static/chunks/{1970-60c96aed937a4928.js → 1970-90dd510762d820ba.js} +0 -0
- /khoj/interface/compiled/_next/static/chunks/{9417-2ca87207387fc790.js → 9417-951f46451a8dd6d7.js} +0 -0
- {khoj-1.27.2.dev15.dist-info → khoj-1.28.1.dist-info}/WHEEL +0 -0
- {khoj-1.27.2.dev15.dist-info → khoj-1.28.1.dist-info}/entry_points.txt +0 -0
- {khoj-1.27.2.dev15.dist-info → khoj-1.28.1.dist-info}/licenses/LICENSE +0 -0
khoj/routers/api.py
CHANGED
@@ -44,6 +44,7 @@ from khoj.processor.conversation.offline.chat_model import extract_questions_off
|
|
44
44
|
from khoj.processor.conversation.offline.whisper import transcribe_audio_offline
|
45
45
|
from khoj.processor.conversation.openai.gpt import extract_questions
|
46
46
|
from khoj.processor.conversation.openai.whisper import transcribe_audio
|
47
|
+
from khoj.processor.conversation.utils import defilter_query
|
47
48
|
from khoj.routers.helpers import (
|
48
49
|
ApiUserRateLimiter,
|
49
50
|
ChatEvent,
|
@@ -167,8 +168,8 @@ async def execute_search(
|
|
167
168
|
search_futures += [
|
168
169
|
executor.submit(
|
169
170
|
text_search.query,
|
170
|
-
user,
|
171
171
|
user_query,
|
172
|
+
user,
|
172
173
|
t,
|
173
174
|
question_embedding=encoded_asymmetric_query,
|
174
175
|
max_distance=max_distance,
|
@@ -350,11 +351,12 @@ async def extract_references_and_questions(
|
|
350
351
|
send_status_func: Optional[Callable] = None,
|
351
352
|
query_images: Optional[List[str]] = None,
|
352
353
|
agent: Agent = None,
|
354
|
+
tracer: dict = {},
|
353
355
|
):
|
354
356
|
user = request.user.object if request.user.is_authenticated else None
|
355
357
|
|
356
358
|
# Initialize Variables
|
357
|
-
compiled_references: List[
|
359
|
+
compiled_references: List[dict[str, str]] = []
|
358
360
|
inferred_queries: List[str] = []
|
359
361
|
|
360
362
|
agent_has_entries = False
|
@@ -383,9 +385,7 @@ async def extract_references_and_questions(
|
|
383
385
|
return
|
384
386
|
|
385
387
|
# Extract filter terms from user message
|
386
|
-
defiltered_query = q
|
387
|
-
for filter in [DateFilter(), WordFilter(), FileFilter()]:
|
388
|
-
defiltered_query = filter.defilter(defiltered_query)
|
388
|
+
defiltered_query = defilter_query(q)
|
389
389
|
filters_in_query = q.replace(defiltered_query, "").strip()
|
390
390
|
conversation = await sync_to_async(ConversationAdapters.get_conversation_by_id)(conversation_id)
|
391
391
|
|
@@ -425,6 +425,7 @@ async def extract_references_and_questions(
|
|
425
425
|
user=user,
|
426
426
|
max_prompt_size=conversation_config.max_prompt_size,
|
427
427
|
personality_context=personality_context,
|
428
|
+
tracer=tracer,
|
428
429
|
)
|
429
430
|
elif conversation_config.model_type == ChatModelOptions.ModelType.OPENAI:
|
430
431
|
openai_chat_config = conversation_config.openai_config
|
@@ -442,6 +443,7 @@ async def extract_references_and_questions(
|
|
442
443
|
query_images=query_images,
|
443
444
|
vision_enabled=vision_enabled,
|
444
445
|
personality_context=personality_context,
|
446
|
+
tracer=tracer,
|
445
447
|
)
|
446
448
|
elif conversation_config.model_type == ChatModelOptions.ModelType.ANTHROPIC:
|
447
449
|
api_key = conversation_config.openai_config.api_key
|
@@ -456,6 +458,7 @@ async def extract_references_and_questions(
|
|
456
458
|
user=user,
|
457
459
|
vision_enabled=vision_enabled,
|
458
460
|
personality_context=personality_context,
|
461
|
+
tracer=tracer,
|
459
462
|
)
|
460
463
|
elif conversation_config.model_type == ChatModelOptions.ModelType.GOOGLE:
|
461
464
|
api_key = conversation_config.openai_config.api_key
|
@@ -471,6 +474,7 @@ async def extract_references_and_questions(
|
|
471
474
|
user=user,
|
472
475
|
vision_enabled=vision_enabled,
|
473
476
|
personality_context=personality_context,
|
477
|
+
tracer=tracer,
|
474
478
|
)
|
475
479
|
|
476
480
|
# Collate search results as context for GPT
|
@@ -497,7 +501,8 @@ async def extract_references_and_questions(
|
|
497
501
|
)
|
498
502
|
search_results = text_search.deduplicated_search_responses(search_results)
|
499
503
|
compiled_references = [
|
500
|
-
{"compiled": item.additional["compiled"], "file": item.additional["file"]}
|
504
|
+
{"query": q, "compiled": item.additional["compiled"], "file": item.additional["file"]}
|
505
|
+
for q, item in zip(inferred_queries, search_results)
|
501
506
|
]
|
502
507
|
|
503
508
|
yield compiled_references, inferred_queries, defiltered_query
|
khoj/routers/api_chat.py
CHANGED
@@ -3,9 +3,10 @@ import base64
|
|
3
3
|
import json
|
4
4
|
import logging
|
5
5
|
import time
|
6
|
+
import uuid
|
6
7
|
from datetime import datetime
|
7
8
|
from functools import partial
|
8
|
-
from typing import Dict, Optional
|
9
|
+
from typing import Any, Dict, List, Optional
|
9
10
|
from urllib.parse import unquote
|
10
11
|
|
11
12
|
from asgiref.sync import sync_to_async
|
@@ -24,11 +25,13 @@ from khoj.database.adapters import (
|
|
24
25
|
)
|
25
26
|
from khoj.database.models import Agent, KhojUser
|
26
27
|
from khoj.processor.conversation.prompts import help_message, no_entries_found
|
27
|
-
from khoj.processor.conversation.utils import save_to_conversation_log
|
28
|
+
from khoj.processor.conversation.utils import defilter_query, save_to_conversation_log
|
28
29
|
from khoj.processor.image.generate import text_to_image
|
29
30
|
from khoj.processor.speech.text_to_speech import generate_text_to_speech
|
30
31
|
from khoj.processor.tools.online_search import read_webpages, search_online
|
32
|
+
from khoj.processor.tools.run_code import run_code
|
31
33
|
from khoj.routers.api import extract_references_and_questions
|
34
|
+
from khoj.routers.email import send_query_feedback
|
32
35
|
from khoj.routers.helpers import (
|
33
36
|
ApiImageRateLimiter,
|
34
37
|
ApiUserRateLimiter,
|
@@ -36,13 +39,16 @@ from khoj.routers.helpers import (
|
|
36
39
|
ChatRequestBody,
|
37
40
|
CommonQueryParams,
|
38
41
|
ConversationCommandRateLimiter,
|
42
|
+
DeleteMessageRequestBody,
|
43
|
+
FeedbackData,
|
39
44
|
agenerate_chat_response,
|
40
45
|
aget_relevant_information_sources,
|
41
46
|
aget_relevant_output_modes,
|
42
47
|
construct_automation_created_message,
|
43
48
|
create_automation,
|
44
|
-
|
49
|
+
extract_relevant_info,
|
45
50
|
generate_excalidraw_diagram,
|
51
|
+
generate_summary_from_files,
|
46
52
|
get_conversation_command,
|
47
53
|
is_query_empty,
|
48
54
|
is_ready_to_chat,
|
@@ -50,6 +56,10 @@ from khoj.routers.helpers import (
|
|
50
56
|
update_telemetry_state,
|
51
57
|
validate_conversation_config,
|
52
58
|
)
|
59
|
+
from khoj.routers.research import (
|
60
|
+
InformationCollectionIteration,
|
61
|
+
execute_information_collection,
|
62
|
+
)
|
53
63
|
from khoj.routers.storage import upload_image_to_bucket
|
54
64
|
from khoj.utils import state
|
55
65
|
from khoj.utils.helpers import (
|
@@ -67,16 +77,12 @@ from khoj.utils.rawconfig import FileFilterRequest, FilesFilterRequest, Location
|
|
67
77
|
# Initialize Router
|
68
78
|
logger = logging.getLogger(__name__)
|
69
79
|
conversation_command_rate_limiter = ConversationCommandRateLimiter(
|
70
|
-
trial_rate_limit=
|
80
|
+
trial_rate_limit=20, subscribed_rate_limit=75, slug="command"
|
71
81
|
)
|
72
82
|
|
73
83
|
|
74
84
|
api_chat = APIRouter()
|
75
85
|
|
76
|
-
from pydantic import BaseModel
|
77
|
-
|
78
|
-
from khoj.routers.email import send_query_feedback
|
79
|
-
|
80
86
|
|
81
87
|
@api_chat.get("/conversation/file-filters/{conversation_id}", response_class=Response)
|
82
88
|
@requires(["authenticated"])
|
@@ -138,12 +144,6 @@ def remove_file_filter(request: Request, filter: FileFilterRequest) -> Response:
|
|
138
144
|
return Response(content=json.dumps(file_filters), media_type="application/json", status_code=200)
|
139
145
|
|
140
146
|
|
141
|
-
class FeedbackData(BaseModel):
|
142
|
-
uquery: str
|
143
|
-
kquery: str
|
144
|
-
sentiment: str
|
145
|
-
|
146
|
-
|
147
147
|
@api_chat.post("/feedback")
|
148
148
|
@requires(["authenticated"])
|
149
149
|
async def sendfeedback(request: Request, data: FeedbackData):
|
@@ -158,10 +158,10 @@ async def text_to_speech(
|
|
158
158
|
common: CommonQueryParams,
|
159
159
|
text: str,
|
160
160
|
rate_limiter_per_minute=Depends(
|
161
|
-
ApiUserRateLimiter(requests=
|
161
|
+
ApiUserRateLimiter(requests=30, subscribed_requests=30, window=60, slug="chat_minute")
|
162
162
|
),
|
163
163
|
rate_limiter_per_day=Depends(
|
164
|
-
ApiUserRateLimiter(requests=
|
164
|
+
ApiUserRateLimiter(requests=100, subscribed_requests=600, window=60 * 60 * 24, slug="chat_day")
|
165
165
|
),
|
166
166
|
) -> Response:
|
167
167
|
voice_model = await ConversationAdapters.aget_voice_model_config(request.user.object)
|
@@ -526,6 +526,19 @@ async def set_conversation_title(
|
|
526
526
|
)
|
527
527
|
|
528
528
|
|
529
|
+
@api_chat.delete("/conversation/message", response_class=Response)
|
530
|
+
@requires(["authenticated"])
|
531
|
+
def delete_message(request: Request, delete_request: DeleteMessageRequestBody) -> Response:
|
532
|
+
user = request.user.object
|
533
|
+
success = ConversationAdapters.delete_message_by_turn_id(
|
534
|
+
user, delete_request.conversation_id, delete_request.turn_id
|
535
|
+
)
|
536
|
+
if success:
|
537
|
+
return Response(content=json.dumps({"status": "ok"}), media_type="application/json", status_code=200)
|
538
|
+
else:
|
539
|
+
return Response(content=json.dumps({"status": "error", "message": "Message not found"}), status_code=404)
|
540
|
+
|
541
|
+
|
529
542
|
@api_chat.post("")
|
530
543
|
@requires(["authenticated"])
|
531
544
|
async def chat(
|
@@ -533,10 +546,10 @@ async def chat(
|
|
533
546
|
common: CommonQueryParams,
|
534
547
|
body: ChatRequestBody,
|
535
548
|
rate_limiter_per_minute=Depends(
|
536
|
-
ApiUserRateLimiter(requests=
|
549
|
+
ApiUserRateLimiter(requests=20, subscribed_requests=20, window=60, slug="chat_minute")
|
537
550
|
),
|
538
551
|
rate_limiter_per_day=Depends(
|
539
|
-
ApiUserRateLimiter(requests=
|
552
|
+
ApiUserRateLimiter(requests=100, subscribed_requests=600, window=60 * 60 * 24, slug="chat_day")
|
540
553
|
),
|
541
554
|
image_rate_limiter=Depends(ApiImageRateLimiter(max_images=10, max_combined_size_mb=20)),
|
542
555
|
):
|
@@ -547,6 +560,7 @@ async def chat(
|
|
547
560
|
stream = body.stream
|
548
561
|
title = body.title
|
549
562
|
conversation_id = body.conversation_id
|
563
|
+
turn_id = str(body.turn_id or uuid.uuid4())
|
550
564
|
city = body.city
|
551
565
|
region = body.region
|
552
566
|
country = body.country or get_country_name_from_timezone(body.timezone)
|
@@ -562,8 +576,16 @@ async def chat(
|
|
562
576
|
user: KhojUser = request.user.object
|
563
577
|
event_delimiter = "␃🔚␗"
|
564
578
|
q = unquote(q)
|
579
|
+
train_of_thought = []
|
565
580
|
nonlocal conversation_id
|
566
581
|
|
582
|
+
tracer: dict = {
|
583
|
+
"mid": turn_id,
|
584
|
+
"cid": conversation_id,
|
585
|
+
"uid": user.id,
|
586
|
+
"khoj_version": state.khoj_version,
|
587
|
+
}
|
588
|
+
|
567
589
|
uploaded_images: list[str] = []
|
568
590
|
if images:
|
569
591
|
for image in images:
|
@@ -576,7 +598,7 @@ async def chat(
|
|
576
598
|
uploaded_images.append(uploaded_image)
|
577
599
|
|
578
600
|
async def send_event(event_type: ChatEvent, data: str | dict):
|
579
|
-
nonlocal connection_alive, ttft
|
601
|
+
nonlocal connection_alive, ttft, train_of_thought
|
580
602
|
if not connection_alive or await request.is_disconnected():
|
581
603
|
connection_alive = False
|
582
604
|
logger.warning(f"User {user} disconnected from {common.client} client")
|
@@ -584,11 +606,14 @@ async def chat(
|
|
584
606
|
try:
|
585
607
|
if event_type == ChatEvent.END_LLM_RESPONSE:
|
586
608
|
collect_telemetry()
|
587
|
-
|
609
|
+
elif event_type == ChatEvent.START_LLM_RESPONSE:
|
588
610
|
ttft = time.perf_counter() - start_time
|
611
|
+
elif event_type == ChatEvent.STATUS:
|
612
|
+
train_of_thought.append({"type": event_type.value, "data": data})
|
613
|
+
|
589
614
|
if event_type == ChatEvent.MESSAGE:
|
590
615
|
yield data
|
591
|
-
elif event_type == ChatEvent.REFERENCES or stream:
|
616
|
+
elif event_type == ChatEvent.REFERENCES or ChatEvent.METADATA or stream:
|
592
617
|
yield json.dumps({"type": event_type.value, "data": data}, ensure_ascii=False)
|
593
618
|
except asyncio.CancelledError as e:
|
594
619
|
connection_alive = False
|
@@ -632,6 +657,11 @@ async def chat(
|
|
632
657
|
metadata=chat_metadata,
|
633
658
|
)
|
634
659
|
|
660
|
+
if is_query_empty(q):
|
661
|
+
async for result in send_llm_response("Please ask your query to get started."):
|
662
|
+
yield result
|
663
|
+
return
|
664
|
+
|
635
665
|
conversation_commands = [get_conversation_command(query=q, any_references=True)]
|
636
666
|
|
637
667
|
conversation = await ConversationAdapters.aget_conversation_by_user(
|
@@ -647,6 +677,9 @@ async def chat(
|
|
647
677
|
return
|
648
678
|
conversation_id = conversation.id
|
649
679
|
|
680
|
+
async for event in send_event(ChatEvent.METADATA, {"conversationId": str(conversation_id), "turnId": turn_id}):
|
681
|
+
yield event
|
682
|
+
|
650
683
|
agent: Agent | None = None
|
651
684
|
default_agent = await AgentAdapters.aget_default_agent()
|
652
685
|
if conversation.agent and conversation.agent != default_agent:
|
@@ -658,22 +691,23 @@ async def chat(
|
|
658
691
|
agent = default_agent
|
659
692
|
|
660
693
|
await is_ready_to_chat(user)
|
661
|
-
|
662
694
|
user_name = await aget_user_name(user)
|
663
695
|
location = None
|
664
696
|
if city or region or country or country_code:
|
665
697
|
location = LocationData(city=city, region=region, country=country, country_code=country_code)
|
666
698
|
|
667
|
-
if is_query_empty(q):
|
668
|
-
async for result in send_llm_response("Please ask your query to get started."):
|
669
|
-
yield result
|
670
|
-
return
|
671
|
-
|
672
699
|
user_message_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
673
700
|
|
674
701
|
meta_log = conversation.conversation_log
|
675
702
|
is_automated_task = conversation_commands == [ConversationCommand.AutomatedTask]
|
676
703
|
|
704
|
+
researched_results = ""
|
705
|
+
online_results: Dict = dict()
|
706
|
+
code_results: Dict = dict()
|
707
|
+
## Extract Document References
|
708
|
+
compiled_references: List[Any] = []
|
709
|
+
inferred_queries: List[Any] = []
|
710
|
+
|
677
711
|
if conversation_commands == [ConversationCommand.Default] or is_automated_task:
|
678
712
|
conversation_commands = await aget_relevant_information_sources(
|
679
713
|
q,
|
@@ -682,14 +716,22 @@ async def chat(
|
|
682
716
|
user=user,
|
683
717
|
query_images=uploaded_images,
|
684
718
|
agent=agent,
|
719
|
+
tracer=tracer,
|
685
720
|
)
|
721
|
+
|
722
|
+
# If we're doing research, we don't want to do anything else
|
723
|
+
if ConversationCommand.Research in conversation_commands:
|
724
|
+
conversation_commands = [ConversationCommand.Research]
|
725
|
+
|
686
726
|
conversation_commands_str = ", ".join([cmd.value for cmd in conversation_commands])
|
687
727
|
async for result in send_event(
|
688
728
|
ChatEvent.STATUS, f"**Chose Data Sources to Search:** {conversation_commands_str}"
|
689
729
|
):
|
690
730
|
yield result
|
691
731
|
|
692
|
-
mode = await aget_relevant_output_modes(
|
732
|
+
mode = await aget_relevant_output_modes(
|
733
|
+
q, meta_log, is_automated_task, user, uploaded_images, agent, tracer=tracer
|
734
|
+
)
|
693
735
|
async for result in send_event(ChatEvent.STATUS, f"**Decided Response Mode:** {mode.value}"):
|
694
736
|
yield result
|
695
737
|
if mode not in conversation_commands:
|
@@ -699,6 +741,44 @@ async def chat(
|
|
699
741
|
await conversation_command_rate_limiter.update_and_check_if_valid(request, cmd)
|
700
742
|
q = q.replace(f"/{cmd.value}", "").strip()
|
701
743
|
|
744
|
+
defiltered_query = defilter_query(q)
|
745
|
+
|
746
|
+
if conversation_commands == [ConversationCommand.Research]:
|
747
|
+
async for research_result in execute_information_collection(
|
748
|
+
request=request,
|
749
|
+
user=user,
|
750
|
+
query=defiltered_query,
|
751
|
+
conversation_id=conversation_id,
|
752
|
+
conversation_history=meta_log,
|
753
|
+
query_images=uploaded_images,
|
754
|
+
agent=agent,
|
755
|
+
send_status_func=partial(send_event, ChatEvent.STATUS),
|
756
|
+
user_name=user_name,
|
757
|
+
location=location,
|
758
|
+
file_filters=conversation.file_filters if conversation else [],
|
759
|
+
tracer=tracer,
|
760
|
+
):
|
761
|
+
if isinstance(research_result, InformationCollectionIteration):
|
762
|
+
if research_result.summarizedResult:
|
763
|
+
if research_result.onlineContext:
|
764
|
+
online_results.update(research_result.onlineContext)
|
765
|
+
if research_result.codeContext:
|
766
|
+
code_results.update(research_result.codeContext)
|
767
|
+
if research_result.context:
|
768
|
+
compiled_references.extend(research_result.context)
|
769
|
+
|
770
|
+
researched_results += research_result.summarizedResult
|
771
|
+
|
772
|
+
else:
|
773
|
+
yield research_result
|
774
|
+
|
775
|
+
# researched_results = await extract_relevant_info(q, researched_results, agent)
|
776
|
+
logger.info(f"Researched Results: {researched_results}")
|
777
|
+
|
778
|
+
for cmd in conversation_commands:
|
779
|
+
await conversation_command_rate_limiter.update_and_check_if_valid(request, cmd)
|
780
|
+
q = q.replace(f"/{cmd.value}", "").strip()
|
781
|
+
|
702
782
|
used_slash_summarize = conversation_commands == [ConversationCommand.Summarize]
|
703
783
|
file_filters = conversation.file_filters if conversation else []
|
704
784
|
# Skip trying to summarize if
|
@@ -723,47 +803,24 @@ async def chat(
|
|
723
803
|
async for result in send_llm_response(response_log):
|
724
804
|
yield result
|
725
805
|
else:
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
if not q:
|
745
|
-
q = "Create a general summary of the file"
|
746
|
-
async for result in send_event(
|
747
|
-
ChatEvent.STATUS, f"**Constructing Summary Using:** {file_object[0].file_name}"
|
748
|
-
):
|
749
|
-
yield result
|
750
|
-
|
751
|
-
response = await extract_relevant_summary(
|
752
|
-
q,
|
753
|
-
contextual_data,
|
754
|
-
conversation_history=meta_log,
|
755
|
-
query_images=uploaded_images,
|
756
|
-
user=user,
|
757
|
-
agent=agent,
|
758
|
-
)
|
759
|
-
response_log = str(response)
|
760
|
-
async for result in send_llm_response(response_log):
|
761
|
-
yield result
|
762
|
-
except Exception as e:
|
763
|
-
response_log = "Error summarizing file. Please try again, or contact support."
|
764
|
-
logger.error(f"Error summarizing file for {user.email}: {e}", exc_info=True)
|
765
|
-
async for result in send_llm_response(response_log):
|
766
|
-
yield result
|
806
|
+
async for response in generate_summary_from_files(
|
807
|
+
q=q,
|
808
|
+
user=user,
|
809
|
+
file_filters=file_filters,
|
810
|
+
meta_log=meta_log,
|
811
|
+
query_images=uploaded_images,
|
812
|
+
agent=agent,
|
813
|
+
send_status_func=partial(send_event, ChatEvent.STATUS),
|
814
|
+
tracer=tracer,
|
815
|
+
):
|
816
|
+
if isinstance(response, dict) and ChatEvent.STATUS in response:
|
817
|
+
yield response[ChatEvent.STATUS]
|
818
|
+
else:
|
819
|
+
if isinstance(response, str):
|
820
|
+
response_log = response
|
821
|
+
async for result in send_llm_response(response):
|
822
|
+
yield result
|
823
|
+
|
767
824
|
await sync_to_async(save_to_conversation_log)(
|
768
825
|
q,
|
769
826
|
response_log,
|
@@ -774,6 +831,8 @@ async def chat(
|
|
774
831
|
client_application=request.user.client_app,
|
775
832
|
conversation_id=conversation_id,
|
776
833
|
query_images=uploaded_images,
|
834
|
+
tracer=tracer,
|
835
|
+
train_of_thought=train_of_thought,
|
777
836
|
)
|
778
837
|
return
|
779
838
|
|
@@ -782,7 +841,7 @@ async def chat(
|
|
782
841
|
if not q:
|
783
842
|
conversation_config = await ConversationAdapters.aget_user_conversation_config(user)
|
784
843
|
if conversation_config == None:
|
785
|
-
conversation_config = await ConversationAdapters.aget_default_conversation_config()
|
844
|
+
conversation_config = await ConversationAdapters.aget_default_conversation_config(user)
|
786
845
|
model_type = conversation_config.model_type
|
787
846
|
formatted_help = help_message.format(model=model_type, version=state.khoj_version, device=get_device())
|
788
847
|
async for result in send_llm_response(formatted_help):
|
@@ -795,7 +854,7 @@ async def chat(
|
|
795
854
|
if ConversationCommand.Automation in conversation_commands:
|
796
855
|
try:
|
797
856
|
automation, crontime, query_to_run, subject = await create_automation(
|
798
|
-
q, timezone, user, request.url, meta_log
|
857
|
+
q, timezone, user, request.url, meta_log, tracer=tracer
|
799
858
|
)
|
800
859
|
except Exception as e:
|
801
860
|
logger.error(f"Error scheduling task {q} for {user.email}: {e}")
|
@@ -817,6 +876,8 @@ async def chat(
|
|
817
876
|
inferred_queries=[query_to_run],
|
818
877
|
automation_id=automation.id,
|
819
878
|
query_images=uploaded_images,
|
879
|
+
tracer=tracer,
|
880
|
+
train_of_thought=train_of_thought,
|
820
881
|
)
|
821
882
|
async for result in send_llm_response(llm_response):
|
822
883
|
yield result
|
@@ -824,48 +885,49 @@ async def chat(
|
|
824
885
|
|
825
886
|
# Gather Context
|
826
887
|
## Extract Document References
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
# Strip only leading # from headings
|
859
|
-
headings = headings.replace("#", "")
|
860
|
-
async for result in send_event(ChatEvent.STATUS, f"**Found Relevant Notes**: {headings}"):
|
861
|
-
yield result
|
888
|
+
if not ConversationCommand.Research in conversation_commands:
|
889
|
+
try:
|
890
|
+
async for result in extract_references_and_questions(
|
891
|
+
request,
|
892
|
+
meta_log,
|
893
|
+
q,
|
894
|
+
(n or 7),
|
895
|
+
d,
|
896
|
+
conversation_id,
|
897
|
+
conversation_commands,
|
898
|
+
location,
|
899
|
+
partial(send_event, ChatEvent.STATUS),
|
900
|
+
query_images=uploaded_images,
|
901
|
+
agent=agent,
|
902
|
+
tracer=tracer,
|
903
|
+
):
|
904
|
+
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
905
|
+
yield result[ChatEvent.STATUS]
|
906
|
+
else:
|
907
|
+
compiled_references.extend(result[0])
|
908
|
+
inferred_queries.extend(result[1])
|
909
|
+
defiltered_query = result[2]
|
910
|
+
except Exception as e:
|
911
|
+
error_message = (
|
912
|
+
f"Error searching knowledge base: {e}. Attempting to respond without document references."
|
913
|
+
)
|
914
|
+
logger.error(error_message, exc_info=True)
|
915
|
+
async for result in send_event(
|
916
|
+
ChatEvent.STATUS, "Document search failed. I'll try respond without document references"
|
917
|
+
):
|
918
|
+
yield result
|
862
919
|
|
863
|
-
|
920
|
+
if not is_none_or_empty(compiled_references):
|
921
|
+
headings = "\n- " + "\n- ".join(set([c.get("compiled", c).split("\n")[0] for c in compiled_references]))
|
922
|
+
# Strip only leading # from headings
|
923
|
+
headings = headings.replace("#", "")
|
924
|
+
async for result in send_event(ChatEvent.STATUS, f"**Found Relevant Notes**: {headings}"):
|
925
|
+
yield result
|
864
926
|
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
927
|
+
if conversation_commands == [ConversationCommand.Notes] and not await EntryAdapters.auser_has_entries(user):
|
928
|
+
async for result in send_llm_response(f"{no_entries_found.format()}"):
|
929
|
+
yield result
|
930
|
+
return
|
869
931
|
|
870
932
|
if ConversationCommand.Notes in conversation_commands and is_none_or_empty(compiled_references):
|
871
933
|
conversation_commands.remove(ConversationCommand.Notes)
|
@@ -882,6 +944,7 @@ async def chat(
|
|
882
944
|
custom_filters,
|
883
945
|
query_images=uploaded_images,
|
884
946
|
agent=agent,
|
947
|
+
tracer=tracer,
|
885
948
|
):
|
886
949
|
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
887
950
|
yield result[ChatEvent.STATUS]
|
@@ -906,6 +969,7 @@ async def chat(
|
|
906
969
|
partial(send_event, ChatEvent.STATUS),
|
907
970
|
query_images=uploaded_images,
|
908
971
|
agent=agent,
|
972
|
+
tracer=tracer,
|
909
973
|
):
|
910
974
|
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
911
975
|
yield result[ChatEvent.STATUS]
|
@@ -932,6 +996,33 @@ async def chat(
|
|
932
996
|
):
|
933
997
|
yield result
|
934
998
|
|
999
|
+
## Gather Code Results
|
1000
|
+
if ConversationCommand.Code in conversation_commands:
|
1001
|
+
try:
|
1002
|
+
context = f"# Iteration 1:\n#---\nNotes:\n{compiled_references}\n\nOnline Results:{online_results}"
|
1003
|
+
async for result in run_code(
|
1004
|
+
defiltered_query,
|
1005
|
+
meta_log,
|
1006
|
+
context,
|
1007
|
+
location,
|
1008
|
+
user,
|
1009
|
+
partial(send_event, ChatEvent.STATUS),
|
1010
|
+
query_images=uploaded_images,
|
1011
|
+
agent=agent,
|
1012
|
+
tracer=tracer,
|
1013
|
+
):
|
1014
|
+
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
1015
|
+
yield result[ChatEvent.STATUS]
|
1016
|
+
else:
|
1017
|
+
code_results = result
|
1018
|
+
async for result in send_event(ChatEvent.STATUS, f"**Ran code snippets**: {len(code_results)}"):
|
1019
|
+
yield result
|
1020
|
+
except ValueError as e:
|
1021
|
+
logger.warning(
|
1022
|
+
f"Failed to use code tool: {e}. Attempting to respond without code results",
|
1023
|
+
exc_info=True,
|
1024
|
+
)
|
1025
|
+
|
935
1026
|
## Send Gathered References
|
936
1027
|
async for result in send_event(
|
937
1028
|
ChatEvent.REFERENCES,
|
@@ -939,6 +1030,7 @@ async def chat(
|
|
939
1030
|
"inferredQueries": inferred_queries,
|
940
1031
|
"context": compiled_references,
|
941
1032
|
"onlineContext": online_results,
|
1033
|
+
"codeContext": code_results,
|
942
1034
|
},
|
943
1035
|
):
|
944
1036
|
yield result
|
@@ -956,6 +1048,7 @@ async def chat(
|
|
956
1048
|
send_status_func=partial(send_event, ChatEvent.STATUS),
|
957
1049
|
query_images=uploaded_images,
|
958
1050
|
agent=agent,
|
1051
|
+
tracer=tracer,
|
959
1052
|
):
|
960
1053
|
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
961
1054
|
yield result[ChatEvent.STATUS]
|
@@ -986,6 +1079,8 @@ async def chat(
|
|
986
1079
|
compiled_references=compiled_references,
|
987
1080
|
online_results=online_results,
|
988
1081
|
query_images=uploaded_images,
|
1082
|
+
tracer=tracer,
|
1083
|
+
train_of_thought=train_of_thought,
|
989
1084
|
)
|
990
1085
|
content_obj = {
|
991
1086
|
"intentType": intent_type,
|
@@ -1014,6 +1109,7 @@ async def chat(
|
|
1014
1109
|
user=user,
|
1015
1110
|
agent=agent,
|
1016
1111
|
send_status_func=partial(send_event, ChatEvent.STATUS),
|
1112
|
+
tracer=tracer,
|
1017
1113
|
):
|
1018
1114
|
if isinstance(result, dict) and ChatEvent.STATUS in result:
|
1019
1115
|
yield result[ChatEvent.STATUS]
|
@@ -1041,6 +1137,8 @@ async def chat(
|
|
1041
1137
|
compiled_references=compiled_references,
|
1042
1138
|
online_results=online_results,
|
1043
1139
|
query_images=uploaded_images,
|
1140
|
+
tracer=tracer,
|
1141
|
+
train_of_thought=train_of_thought,
|
1044
1142
|
)
|
1045
1143
|
|
1046
1144
|
async for result in send_llm_response(json.dumps(content_obj)):
|
@@ -1056,6 +1154,7 @@ async def chat(
|
|
1056
1154
|
conversation,
|
1057
1155
|
compiled_references,
|
1058
1156
|
online_results,
|
1157
|
+
code_results,
|
1059
1158
|
inferred_queries,
|
1060
1159
|
conversation_commands,
|
1061
1160
|
user,
|
@@ -1063,7 +1162,10 @@ async def chat(
|
|
1063
1162
|
conversation_id,
|
1064
1163
|
location,
|
1065
1164
|
user_name,
|
1165
|
+
researched_results,
|
1066
1166
|
uploaded_images,
|
1167
|
+
tracer,
|
1168
|
+
train_of_thought,
|
1067
1169
|
)
|
1068
1170
|
|
1069
1171
|
# Send Response
|