khoj 1.30.2__py3-none-any.whl → 1.30.2.dev3__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 +1 -1
- khoj/database/admin.py +0 -39
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1210.132a7e1910006bbb.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1603-859ddcf58f3ca639.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1970-e1935a1d0930a7c5.js +1 -0
- khoj/interface/compiled/_next/static/chunks/216-b2e4344315b88832.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3124-e8410bbd01f6f188.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{3717-b46079dbe9f55694.js → 6297-55f82537bb7068dd.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{2646-92ba433951d02d52.js → 6901-e3dc0d315e3f6033.js} +2 -2
- khoj/interface/compiled/_next/static/chunks/796-36ee2d6829448c6d.js +3 -0
- khoj/interface/compiled/_next/static/chunks/9417-06236cd650f1abcd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-f5c0801b27a8e95e.js → page-b086c9b0aa5a3833.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-0393501fad5f8e3d.js → page-697a2d415e11a872.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/{page-f2539e3197d03c0d.js → page-461e26fcb7578d39.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-5cc56a8db5d21b38.js → page-4a3c49c5e996cc40.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/{page-e8b578d155550386.js → page-9013658bebfc3d17.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-23399f8c2d6079b8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-635635e4fb39fe29.js → page-6a68ac7e227b34e7.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-5dbccc5145b80b64.js → webpack-fff9e0e69bdcdb1a.js} +1 -1
- khoj/interface/compiled/_next/static/css/1a4038cc4acc8ee4.css +25 -0
- khoj/interface/compiled/_next/static/css/23f801d22927d568.css +1 -0
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.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/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/main.py +0 -4
- khoj/processor/conversation/anthropic/anthropic_chat.py +2 -8
- khoj/processor/conversation/anthropic/utils.py +3 -22
- khoj/processor/conversation/google/gemini_chat.py +2 -8
- khoj/processor/conversation/google/utils.py +3 -19
- khoj/processor/conversation/offline/chat_model.py +4 -12
- khoj/processor/conversation/openai/gpt.py +2 -9
- khoj/processor/conversation/openai/utils.py +21 -39
- khoj/processor/conversation/prompts.py +21 -40
- khoj/processor/conversation/utils.py +10 -16
- khoj/processor/tools/run_code.py +25 -1
- khoj/routers/api_chat.py +16 -41
- khoj/routers/api_subscription.py +2 -9
- khoj/routers/auth.py +2 -2
- khoj/routers/helpers.py +5 -20
- khoj/routers/research.py +1 -2
- khoj/utils/cli.py +0 -2
- khoj/utils/constants.py +0 -17
- khoj/utils/helpers.py +1 -55
- khoj/utils/state.py +0 -1
- {khoj-1.30.2.dist-info → khoj-1.30.2.dev3.dist-info}/METADATA +4 -9
- {khoj-1.30.2.dist-info → khoj-1.30.2.dev3.dist-info}/RECORD +64 -62
- khoj/interface/compiled/_next/static/chunks/1210.ef7a0f9a7e43da1d.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1603-ba5f9f05e92c8412.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1970-1b63ac1497b03a10.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3463-081c031e873b7966.js +0 -3
- khoj/interface/compiled/_next/static/chunks/4752-554a3db270186ce3.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-b6c835050c970be7.js +0 -1
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +0 -1
- khoj/interface/compiled/_next/static/css/5d8d85d3f2e95bae.css +0 -25
- khoj/interface/compiled/_next/static/css/63e106a52a0ec4ca.css +0 -1
- /khoj/interface/compiled/_next/static/{UR4enQiSbkZKb3SDFX2tx → B2OEgd27mxyHj5180Ng7n}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{UR4enQiSbkZKb3SDFX2tx → B2OEgd27mxyHj5180Ng7n}/_ssgManifest.js +0 -0
- {khoj-1.30.2.dist-info → khoj-1.30.2.dev3.dist-info}/WHEEL +0 -0
- {khoj-1.30.2.dist-info → khoj-1.30.2.dev3.dist-info}/entry_points.txt +0 -0
- {khoj-1.30.2.dist-info → khoj-1.30.2.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -183,23 +183,20 @@ Improved Prompt:
|
|
183
183
|
|
184
184
|
improve_diagram_description_prompt = PromptTemplate.from_template(
|
185
185
|
"""
|
186
|
-
you are an architect working with a novice
|
186
|
+
you are an architect working with a novice artist using a diagramming tool.
|
187
187
|
{personality_context}
|
188
188
|
|
189
189
|
you need to convert the user's query to a description format that the novice artist can use very well. you are allowed to use primitives like
|
190
190
|
- text
|
191
191
|
- rectangle
|
192
|
+
- diamond
|
192
193
|
- ellipse
|
193
194
|
- line
|
194
195
|
- arrow
|
195
196
|
|
196
197
|
use these primitives to describe what sort of diagram the drawer should create. the artist must recreate the diagram every time, so include all relevant prior information in your description.
|
197
198
|
|
198
|
-
|
199
|
-
- describe the layout.
|
200
|
-
- you can only use straight lines.
|
201
|
-
- use simple, concise language.
|
202
|
-
- keep it simple and easy to understand. the artist is easily distracted.
|
199
|
+
use simple, concise language.
|
203
200
|
|
204
201
|
Today's Date: {current_date}
|
205
202
|
User's Location: {location}
|
@@ -221,23 +218,19 @@ Query: {query}
|
|
221
218
|
|
222
219
|
excalidraw_diagram_generation_prompt = PromptTemplate.from_template(
|
223
220
|
"""
|
224
|
-
You are a program manager with the ability to describe diagrams to compose in professional, fine detail.
|
221
|
+
You are a program manager with the ability to describe diagrams to compose in professional, fine detail.
|
225
222
|
{personality_context}
|
226
223
|
|
227
|
-
You need to create a declarative description of the diagram and relevant components, using this base schema.
|
228
|
-
- `label`: specify the text to be rendered in the respective elements.
|
229
|
-
- Always use light colors for the `backgroundColor` property, like white, or light blue, green, red
|
230
|
-
- **ALWAYS Required properties for ALL elements**: `type`, `x`, `y`, `id`.
|
231
|
-
- Be very generous with spacing and composition. Use ample space between elements.
|
224
|
+
You need to create a declarative description of the diagram and relevant components, using this base schema. Use the `label` property to specify the text to be rendered in the respective elements. Always use light colors for the `backgroundColor` property, like white, or light blue, green, red. "type", "x", "y", "id", are required properties for all elements.
|
232
225
|
|
233
226
|
{{
|
234
227
|
type: string,
|
235
228
|
x: number,
|
236
229
|
y: number,
|
237
|
-
width: number,
|
238
|
-
height: number,
|
239
230
|
strokeColor: string,
|
240
231
|
backgroundColor: string,
|
232
|
+
width: number,
|
233
|
+
height: number,
|
241
234
|
id: string,
|
242
235
|
label: {{
|
243
236
|
text: string,
|
@@ -247,30 +240,28 @@ You need to create a declarative description of the diagram and relevant compone
|
|
247
240
|
Valid types:
|
248
241
|
- text
|
249
242
|
- rectangle
|
243
|
+
- diamond
|
250
244
|
- ellipse
|
251
245
|
- line
|
252
246
|
- arrow
|
253
247
|
|
254
|
-
For arrows and lines,
|
255
|
-
- `points`: specify the start and end points of the arrow
|
256
|
-
- **ALWAYS Required properties for ALL elements**: `type`, `x`, `y`, `id`.
|
257
|
-
- `start` and `end` properties: connect the linear elements to other elements. The start and end point can either be the ID to map to an existing object, or the `type` and `text` to create a new object. Mapping to an existing object is useful if you want to connect it to multiple objects. Lines and arrows can only start and end at rectangle, text, or ellipse elements. Even if you're using the `start` and `end` properties, you still need to specify the `x` and `y` properties for the start and end points.
|
248
|
+
For arrows and lines, you can use the `points` property to specify the start and end points of the arrow. You may also use the `label` property to specify the text to be rendered. You may use the `start` and `end` properties to connect the linear elements to other elements. The start and end point can either be the ID to map to an existing object, or the `type` to create a new object. Mapping to an existing object is useful if you want to connect it to multiple objects. Lines and arrows can only start and end at rectangle, text, diamond, or ellipse elements.
|
258
249
|
|
259
250
|
{{
|
260
251
|
type: "arrow",
|
261
252
|
id: string,
|
262
253
|
x: number,
|
263
254
|
y: number,
|
255
|
+
width: number,
|
256
|
+
height: number,
|
264
257
|
strokeColor: string,
|
265
258
|
start: {{
|
266
259
|
id: string,
|
267
260
|
type: string,
|
268
|
-
text: string,
|
269
261
|
}},
|
270
262
|
end: {{
|
271
263
|
id: string,
|
272
264
|
type: string,
|
273
|
-
text: string,
|
274
265
|
}},
|
275
266
|
label: {{
|
276
267
|
text: string,
|
@@ -281,11 +272,7 @@ For arrows and lines,
|
|
281
272
|
]
|
282
273
|
}}
|
283
274
|
|
284
|
-
For text,
|
285
|
-
- `text`: specify the text to be rendered
|
286
|
-
- **ALWAYS Required properties for ALL elements**: `type`, `x`, `y`, `id`.
|
287
|
-
- `fontSize`: optional property to specify the font size of the text
|
288
|
-
- Use this element only for titles, subtitles, and overviews. For labels, use the `label` property in the respective elements.
|
275
|
+
For text, you must use the `text` property to specify the text to be rendered. You may also use `fontSize` property to specify the font size of the text. Only use the `text` element for titles, subtitles, and overviews. For labels, use the `label` property in the respective elements.
|
289
276
|
|
290
277
|
{{
|
291
278
|
type: "text",
|
@@ -300,25 +287,19 @@ Here's an example of a valid diagram:
|
|
300
287
|
|
301
288
|
Design Description: Create a diagram describing a circular development process with 3 stages: design, implementation and feedback. The design stage is connected to the implementation stage and the implementation stage is connected to the feedback stage and the feedback stage is connected to the design stage. Each stage should be labeled with the stage name.
|
302
289
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
"
|
307
|
-
"
|
308
|
-
{{"type":"
|
309
|
-
{{"type":"ellipse","x":-
|
310
|
-
{{"type":"ellipse","x":62,"y":394,"id":"implement_ellipse", "label": {{"text": "Implement"}}}},
|
311
|
-
{{"type":"ellipse","x":-348,"y":430,"id":"feedback_ellipse", "label": {{"text": "Feedback"}}}},
|
290
|
+
Response:
|
291
|
+
|
292
|
+
[
|
293
|
+
{{"type":"text","x":-150,"y":50,"width":300,"height":40,"id":"title_text","text":"Circular Development Process","fontSize":24}},
|
294
|
+
{{"type":"ellipse","x":-169,"y":113,"width":188,"height":202,"id":"design_ellipse", "label": {{"text": "Design"}}}},
|
295
|
+
{{"type":"ellipse","x":62,"y":394,"width":186,"height":188,"id":"implement_ellipse", "label": {{"text": "Implement"}}}},
|
296
|
+
{{"type":"ellipse","x":-348,"y":430,"width":184,"height":170,"id":"feedback_ellipse", "label": {{"text": "Feedback"}}}},
|
312
297
|
{{"type":"arrow","x":21,"y":273,"id":"design_to_implement_arrow","points":[[0,0],[86,105]],"start":{{"id":"design_ellipse"}}, "end":{{"id":"implement_ellipse"}}}},
|
313
298
|
{{"type":"arrow","x":50,"y":519,"id":"implement_to_feedback_arrow","points":[[0,0],[-198,-6]],"start":{{"id":"implement_ellipse"}}, "end":{{"id":"feedback_ellipse"}}}},
|
314
299
|
{{"type":"arrow","x":-228,"y":417,"id":"feedback_to_design_arrow","points":[[0,0],[85,-123]],"start":{{"id":"feedback_ellipse"}}, "end":{{"id":"design_ellipse"}}}},
|
315
|
-
|
316
|
-
}}
|
317
|
-
```
|
318
|
-
|
319
|
-
Think about spacing and composition. Use ample space between elements. Double the amount of space you think you need. Create a detailed diagram from the provided context and user prompt below.
|
300
|
+
]
|
320
301
|
|
321
|
-
|
302
|
+
Create a detailed diagram from the provided context and user prompt below. Return a valid JSON object:
|
322
303
|
|
323
304
|
Diagram Description: {query}
|
324
305
|
|
@@ -5,6 +5,7 @@ import math
|
|
5
5
|
import mimetypes
|
6
6
|
import os
|
7
7
|
import queue
|
8
|
+
import re
|
8
9
|
import uuid
|
9
10
|
from dataclasses import dataclass
|
10
11
|
from datetime import datetime
|
@@ -34,7 +35,6 @@ from khoj.utils.helpers import (
|
|
34
35
|
ConversationCommand,
|
35
36
|
in_debug_mode,
|
36
37
|
is_none_or_empty,
|
37
|
-
is_promptrace_enabled,
|
38
38
|
merge_dicts,
|
39
39
|
)
|
40
40
|
from khoj.utils.rawconfig import FileAttachment
|
@@ -57,7 +57,7 @@ model_to_prompt_size = {
|
|
57
57
|
"gemini-1.5-flash": 20000,
|
58
58
|
"gemini-1.5-pro": 20000,
|
59
59
|
# Anthropic Models
|
60
|
-
"claude-3-5-sonnet-
|
60
|
+
"claude-3-5-sonnet-20240620": 20000,
|
61
61
|
"claude-3-5-haiku-20241022": 20000,
|
62
62
|
# Offline Models
|
63
63
|
"bartowski/Meta-Llama-3.1-8B-Instruct-GGUF": 20000,
|
@@ -213,8 +213,6 @@ class ChatEvent(Enum):
|
|
213
213
|
REFERENCES = "references"
|
214
214
|
STATUS = "status"
|
215
215
|
METADATA = "metadata"
|
216
|
-
USAGE = "usage"
|
217
|
-
END_RESPONSE = "end_response"
|
218
216
|
|
219
217
|
|
220
218
|
def message_to_log(
|
@@ -293,7 +291,7 @@ def save_to_conversation_log(
|
|
293
291
|
user_message=q,
|
294
292
|
)
|
295
293
|
|
296
|
-
if
|
294
|
+
if in_debug_mode() or state.verbose > 1:
|
297
295
|
merge_message_into_conversation_trace(q, chat_response, tracer)
|
298
296
|
|
299
297
|
logger.info(
|
@@ -530,7 +528,7 @@ def reciprocal_conversation_to_chatml(message_pair):
|
|
530
528
|
|
531
529
|
def clean_json(response: str):
|
532
530
|
"""Remove any markdown json codeblock and newline formatting if present. Useful for non schema enforceable models"""
|
533
|
-
return response.strip().replace("\n", "").removeprefix("```json").removesuffix("```")
|
531
|
+
return response.strip().replace("\n", "").removeprefix("```json").removeprefix("```python").removesuffix("```")
|
534
532
|
|
535
533
|
|
536
534
|
def clean_code_python(code: str):
|
@@ -580,7 +578,7 @@ def commit_conversation_trace(
|
|
580
578
|
response: str | list[dict],
|
581
579
|
tracer: dict,
|
582
580
|
system_message: str | list[dict] = "",
|
583
|
-
repo_path: str =
|
581
|
+
repo_path: str = "/tmp/promptrace",
|
584
582
|
) -> str:
|
585
583
|
"""
|
586
584
|
Save trace of conversation step using git. Useful to visualize, compare and debug traces.
|
@@ -591,11 +589,6 @@ def commit_conversation_trace(
|
|
591
589
|
except ImportError:
|
592
590
|
return None
|
593
591
|
|
594
|
-
# Infer repository path from environment variable or provided path
|
595
|
-
repo_path = repo_path if not is_none_or_empty(repo_path) else os.getenv("PROMPTRACE_DIR")
|
596
|
-
if not repo_path:
|
597
|
-
return None
|
598
|
-
|
599
592
|
# Serialize session, system message and response to yaml
|
600
593
|
system_message_yaml = json.dumps(system_message, ensure_ascii=False, sort_keys=False)
|
601
594
|
response_yaml = json.dumps(response, ensure_ascii=False, sort_keys=False)
|
@@ -608,6 +601,9 @@ def commit_conversation_trace(
|
|
608
601
|
# Extract chat metadata for session
|
609
602
|
uid, cid, mid = tracer.get("uid", "main"), tracer.get("cid", "main"), tracer.get("mid")
|
610
603
|
|
604
|
+
# Infer repository path from environment variable or provided path
|
605
|
+
repo_path = os.getenv("PROMPTRACE_DIR", repo_path)
|
606
|
+
|
611
607
|
try:
|
612
608
|
# Prepare git repository
|
613
609
|
os.makedirs(repo_path, exist_ok=True)
|
@@ -687,7 +683,7 @@ Metadata
|
|
687
683
|
return None
|
688
684
|
|
689
685
|
|
690
|
-
def merge_message_into_conversation_trace(query: str, response: str, tracer: dict, repo_path=
|
686
|
+
def merge_message_into_conversation_trace(query: str, response: str, tracer: dict, repo_path="/tmp/promptrace") -> bool:
|
691
687
|
"""
|
692
688
|
Merge the message branch into its parent conversation branch.
|
693
689
|
|
@@ -710,9 +706,7 @@ def merge_message_into_conversation_trace(query: str, response: str, tracer: dic
|
|
710
706
|
conv_branch = f"c_{tracer['cid']}"
|
711
707
|
|
712
708
|
# Infer repository path from environment variable or provided path
|
713
|
-
repo_path =
|
714
|
-
if not repo_path:
|
715
|
-
return None
|
709
|
+
repo_path = os.getenv("PROMPTRACE_DIR", repo_path)
|
716
710
|
repo = Repo(repo_path)
|
717
711
|
|
718
712
|
# Checkout conversation branch
|
khoj/processor/tools/run_code.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import base64
|
2
|
+
import copy
|
2
3
|
import datetime
|
3
4
|
import json
|
4
5
|
import logging
|
@@ -19,7 +20,7 @@ from khoj.processor.conversation.utils import (
|
|
19
20
|
construct_chat_history,
|
20
21
|
)
|
21
22
|
from khoj.routers.helpers import send_message_to_model_wrapper
|
22
|
-
from khoj.utils.helpers import is_none_or_empty, timer
|
23
|
+
from khoj.utils.helpers import is_none_or_empty, timer
|
23
24
|
from khoj.utils.rawconfig import LocationData
|
24
25
|
|
25
26
|
logger = logging.getLogger(__name__)
|
@@ -179,3 +180,26 @@ async def execute_sandboxed_python(code: str, input_data: list[dict], sandbox_ur
|
|
179
180
|
"std_err": f"Failed to execute code with {response.status}",
|
180
181
|
"output_files": [],
|
181
182
|
}
|
183
|
+
|
184
|
+
|
185
|
+
def truncate_code_context(original_code_results: dict[str, Any], max_chars=10000) -> dict[str, Any]:
|
186
|
+
"""
|
187
|
+
Truncate large output files and drop image file data from code results.
|
188
|
+
"""
|
189
|
+
# Create a deep copy of the code results to avoid modifying the original data
|
190
|
+
code_results = copy.deepcopy(original_code_results)
|
191
|
+
for code_result in code_results.values():
|
192
|
+
for idx, output_file in enumerate(code_result["results"]["output_files"]):
|
193
|
+
# Drop image files from code results
|
194
|
+
if Path(output_file["filename"]).suffix in {".png", ".jpg", ".jpeg", ".webp"}:
|
195
|
+
code_result["results"]["output_files"][idx] = {
|
196
|
+
"filename": output_file["filename"],
|
197
|
+
"b64_data": "[placeholder for generated image data for brevity]",
|
198
|
+
}
|
199
|
+
# Truncate large output files
|
200
|
+
elif len(output_file["b64_data"]) > max_chars:
|
201
|
+
code_result["results"]["output_files"][idx] = {
|
202
|
+
"filename": output_file["filename"],
|
203
|
+
"b64_data": output_file["b64_data"][:max_chars] + "...",
|
204
|
+
}
|
205
|
+
return code_results
|
khoj/routers/api_chat.py
CHANGED
@@ -432,15 +432,7 @@ def chat_sessions(
|
|
432
432
|
conversations = conversations[:8]
|
433
433
|
|
434
434
|
sessions = conversations.values_list(
|
435
|
-
"id",
|
436
|
-
"slug",
|
437
|
-
"title",
|
438
|
-
"agent__slug",
|
439
|
-
"agent__name",
|
440
|
-
"created_at",
|
441
|
-
"updated_at",
|
442
|
-
"agent__style_icon",
|
443
|
-
"agent__style_color",
|
435
|
+
"id", "slug", "title", "agent__slug", "agent__name", "created_at", "updated_at"
|
444
436
|
)
|
445
437
|
|
446
438
|
session_values = [
|
@@ -450,8 +442,6 @@ def chat_sessions(
|
|
450
442
|
"agent_name": session[4],
|
451
443
|
"created": session[5].strftime("%Y-%m-%d %H:%M:%S"),
|
452
444
|
"updated": session[6].strftime("%Y-%m-%d %H:%M:%S"),
|
453
|
-
"agent_icon": session[7],
|
454
|
-
"agent_color": session[8],
|
455
445
|
}
|
456
446
|
for session in sessions
|
457
447
|
]
|
@@ -677,37 +667,27 @@ async def chat(
|
|
677
667
|
finally:
|
678
668
|
yield event_delimiter
|
679
669
|
|
680
|
-
async def send_llm_response(response: str
|
681
|
-
# Send Chat Response
|
670
|
+
async def send_llm_response(response: str):
|
682
671
|
async for result in send_event(ChatEvent.START_LLM_RESPONSE, ""):
|
683
672
|
yield result
|
684
673
|
async for result in send_event(ChatEvent.MESSAGE, response):
|
685
674
|
yield result
|
686
675
|
async for result in send_event(ChatEvent.END_LLM_RESPONSE, ""):
|
687
676
|
yield result
|
688
|
-
# Send Usage Metadata once llm interactions are complete
|
689
|
-
if usage:
|
690
|
-
async for event in send_event(ChatEvent.USAGE, usage):
|
691
|
-
yield event
|
692
|
-
async for result in send_event(ChatEvent.END_RESPONSE, ""):
|
693
|
-
yield result
|
694
677
|
|
695
678
|
def collect_telemetry():
|
696
679
|
# Gather chat response telemetry
|
697
680
|
nonlocal chat_metadata
|
698
681
|
latency = time.perf_counter() - start_time
|
699
682
|
cmd_set = set([cmd.value for cmd in conversation_commands])
|
700
|
-
cost = (tracer.get("usage", {}) or {}).get("cost", 0)
|
701
683
|
chat_metadata = chat_metadata or {}
|
702
684
|
chat_metadata["conversation_command"] = cmd_set
|
703
|
-
chat_metadata["agent"] = conversation.agent.slug if conversation
|
685
|
+
chat_metadata["agent"] = conversation.agent.slug if conversation.agent else None
|
704
686
|
chat_metadata["latency"] = f"{latency:.3f}"
|
705
687
|
chat_metadata["ttft_latency"] = f"{ttft:.3f}"
|
706
|
-
chat_metadata["usage"] = tracer.get("usage")
|
707
688
|
|
708
689
|
logger.info(f"Chat response time to first token: {ttft:.3f} seconds")
|
709
690
|
logger.info(f"Chat response total time: {latency:.3f} seconds")
|
710
|
-
logger.info(f"Chat response cost: ${cost:.5f}")
|
711
691
|
update_telemetry_state(
|
712
692
|
request=request,
|
713
693
|
telemetry_type="api",
|
@@ -719,7 +699,7 @@ async def chat(
|
|
719
699
|
)
|
720
700
|
|
721
701
|
if is_query_empty(q):
|
722
|
-
async for result in send_llm_response("Please ask your query to get started."
|
702
|
+
async for result in send_llm_response("Please ask your query to get started."):
|
723
703
|
yield result
|
724
704
|
return
|
725
705
|
|
@@ -733,7 +713,7 @@ async def chat(
|
|
733
713
|
create_new=body.create_new,
|
734
714
|
)
|
735
715
|
if not conversation:
|
736
|
-
async for result in send_llm_response(f"Conversation {conversation_id} not found"
|
716
|
+
async for result in send_llm_response(f"Conversation {conversation_id} not found"):
|
737
717
|
yield result
|
738
718
|
return
|
739
719
|
conversation_id = conversation.id
|
@@ -797,7 +777,7 @@ async def chat(
|
|
797
777
|
await conversation_command_rate_limiter.update_and_check_if_valid(request, cmd)
|
798
778
|
q = q.replace(f"/{cmd.value}", "").strip()
|
799
779
|
except HTTPException as e:
|
800
|
-
async for result in send_llm_response(str(e.detail)
|
780
|
+
async for result in send_llm_response(str(e.detail)):
|
801
781
|
yield result
|
802
782
|
return
|
803
783
|
|
@@ -854,7 +834,7 @@ async def chat(
|
|
854
834
|
agent_has_entries = await EntryAdapters.aagent_has_entries(agent)
|
855
835
|
if len(file_filters) == 0 and not agent_has_entries:
|
856
836
|
response_log = "No files selected for summarization. Please add files using the section on the left."
|
857
|
-
async for result in send_llm_response(response_log
|
837
|
+
async for result in send_llm_response(response_log):
|
858
838
|
yield result
|
859
839
|
else:
|
860
840
|
async for response in generate_summary_from_files(
|
@@ -873,7 +853,7 @@ async def chat(
|
|
873
853
|
else:
|
874
854
|
if isinstance(response, str):
|
875
855
|
response_log = response
|
876
|
-
async for result in send_llm_response(response
|
856
|
+
async for result in send_llm_response(response):
|
877
857
|
yield result
|
878
858
|
|
879
859
|
await sync_to_async(save_to_conversation_log)(
|
@@ -900,7 +880,7 @@ async def chat(
|
|
900
880
|
conversation_config = await ConversationAdapters.aget_default_conversation_config(user)
|
901
881
|
model_type = conversation_config.model_type
|
902
882
|
formatted_help = help_message.format(model=model_type, version=state.khoj_version, device=get_device())
|
903
|
-
async for result in send_llm_response(formatted_help
|
883
|
+
async for result in send_llm_response(formatted_help):
|
904
884
|
yield result
|
905
885
|
return
|
906
886
|
# Adding specification to search online specifically on khoj.dev pages.
|
@@ -915,7 +895,7 @@ async def chat(
|
|
915
895
|
except Exception as e:
|
916
896
|
logger.error(f"Error scheduling task {q} for {user.email}: {e}")
|
917
897
|
error_message = f"Unable to create automation. Ensure the automation doesn't already exist."
|
918
|
-
async for result in send_llm_response(error_message
|
898
|
+
async for result in send_llm_response(error_message):
|
919
899
|
yield result
|
920
900
|
return
|
921
901
|
|
@@ -936,7 +916,7 @@ async def chat(
|
|
936
916
|
raw_query_files=raw_query_files,
|
937
917
|
tracer=tracer,
|
938
918
|
)
|
939
|
-
async for result in send_llm_response(llm_response
|
919
|
+
async for result in send_llm_response(llm_response):
|
940
920
|
yield result
|
941
921
|
return
|
942
922
|
|
@@ -983,7 +963,7 @@ async def chat(
|
|
983
963
|
yield result
|
984
964
|
|
985
965
|
if conversation_commands == [ConversationCommand.Notes] and not await EntryAdapters.auser_has_entries(user):
|
986
|
-
async for result in send_llm_response(f"{no_entries_found.format()}"
|
966
|
+
async for result in send_llm_response(f"{no_entries_found.format()}"):
|
987
967
|
yield result
|
988
968
|
return
|
989
969
|
|
@@ -1125,7 +1105,7 @@ async def chat(
|
|
1125
1105
|
"detail": improved_image_prompt,
|
1126
1106
|
"image": None,
|
1127
1107
|
}
|
1128
|
-
async for result in send_llm_response(json.dumps(content_obj)
|
1108
|
+
async for result in send_llm_response(json.dumps(content_obj)):
|
1129
1109
|
yield result
|
1130
1110
|
return
|
1131
1111
|
|
@@ -1152,7 +1132,7 @@ async def chat(
|
|
1152
1132
|
"inferredQueries": [improved_image_prompt],
|
1153
1133
|
"image": generated_image,
|
1154
1134
|
}
|
1155
|
-
async for result in send_llm_response(json.dumps(content_obj)
|
1135
|
+
async for result in send_llm_response(json.dumps(content_obj)):
|
1156
1136
|
yield result
|
1157
1137
|
return
|
1158
1138
|
|
@@ -1186,7 +1166,7 @@ async def chat(
|
|
1186
1166
|
diagram_description = excalidraw_diagram_description
|
1187
1167
|
else:
|
1188
1168
|
error_message = "Failed to generate diagram. Please try again later."
|
1189
|
-
async for result in send_llm_response(error_message
|
1169
|
+
async for result in send_llm_response(error_message):
|
1190
1170
|
yield result
|
1191
1171
|
|
1192
1172
|
await sync_to_async(save_to_conversation_log)(
|
@@ -1233,7 +1213,7 @@ async def chat(
|
|
1233
1213
|
tracer=tracer,
|
1234
1214
|
)
|
1235
1215
|
|
1236
|
-
async for result in send_llm_response(json.dumps(content_obj)
|
1216
|
+
async for result in send_llm_response(json.dumps(content_obj)):
|
1237
1217
|
yield result
|
1238
1218
|
return
|
1239
1219
|
|
@@ -1272,11 +1252,6 @@ async def chat(
|
|
1272
1252
|
if item is None:
|
1273
1253
|
async for result in send_event(ChatEvent.END_LLM_RESPONSE, ""):
|
1274
1254
|
yield result
|
1275
|
-
# Send Usage Metadata once llm interactions are complete
|
1276
|
-
async for event in send_event(ChatEvent.USAGE, tracer.get("usage")):
|
1277
|
-
yield event
|
1278
|
-
async for result in send_event(ChatEvent.END_RESPONSE, ""):
|
1279
|
-
yield result
|
1280
1255
|
logger.debug("Finished streaming response")
|
1281
1256
|
return
|
1282
1257
|
if not connection_alive or not continue_stream:
|
khoj/routers/api_subscription.py
CHANGED
@@ -66,23 +66,16 @@ async def subscribe(request: Request):
|
|
66
66
|
success = user is not None
|
67
67
|
elif event_type in {"customer.subscription.updated"}:
|
68
68
|
user_subscription = await sync_to_async(adapters.get_user_subscription)(customer_email)
|
69
|
-
|
70
|
-
renewal_date = None
|
71
|
-
if subscription["current_period_end"]:
|
72
|
-
renewal_date = datetime.fromtimestamp(subscription["current_period_end"], tz=timezone.utc)
|
73
|
-
|
74
69
|
# Allow updating subscription status if paid user
|
75
70
|
if user_subscription and user_subscription.renewal_date:
|
76
71
|
# Mark user as unsubscribed or resubscribed
|
77
72
|
is_recurring = not subscription["cancel_at_period_end"]
|
78
|
-
user, is_new = await adapters.set_user_subscription(
|
79
|
-
customer_email, is_recurring=is_recurring, renewal_date=renewal_date
|
80
|
-
)
|
73
|
+
user, is_new = await adapters.set_user_subscription(customer_email, is_recurring=is_recurring)
|
81
74
|
success = user is not None
|
82
75
|
elif event_type in {"customer.subscription.deleted"}:
|
83
76
|
# Reset the user to trial state
|
84
77
|
user, is_new = await adapters.set_user_subscription(
|
85
|
-
customer_email, is_recurring=False, renewal_date=
|
78
|
+
customer_email, is_recurring=False, renewal_date=False, type=Subscription.Type.TRIAL
|
86
79
|
)
|
87
80
|
success = user is not None
|
88
81
|
|
khoj/routers/auth.py
CHANGED
@@ -89,7 +89,7 @@ async def login_magic_link(request: Request, form: MagicLinkForm):
|
|
89
89
|
update_telemetry_state(
|
90
90
|
request=request,
|
91
91
|
telemetry_type="api",
|
92
|
-
api="
|
92
|
+
api="create_user",
|
93
93
|
metadata={"server_id": str(user.uuid)},
|
94
94
|
)
|
95
95
|
logger.log(logging.INFO, f"🥳 New User Created: {user.uuid}")
|
@@ -174,7 +174,7 @@ async def auth(request: Request):
|
|
174
174
|
update_telemetry_state(
|
175
175
|
request=request,
|
176
176
|
telemetry_type="api",
|
177
|
-
api="
|
177
|
+
api="create_user",
|
178
178
|
metadata={"server_id": str(khoj_user.uuid)},
|
179
179
|
)
|
180
180
|
logger.log(logging.INFO, f"🥳 New User Created: {khoj_user.uuid}")
|
khoj/routers/helpers.py
CHANGED
@@ -411,7 +411,7 @@ async def aget_data_sources_and_output_format(
|
|
411
411
|
f"Invalid response for determining relevant tools: {selected_sources}. Raw Response: {response}"
|
412
412
|
)
|
413
413
|
|
414
|
-
result: Dict = {"sources": [], "output": None if not is_task else ConversationCommand.AutomatedTask}
|
414
|
+
result: Dict = {"sources": [], "output": None} if not is_task else {"output": ConversationCommand.AutomatedTask}
|
415
415
|
for selected_source in selected_sources:
|
416
416
|
# Add a double check to verify it's in the agent list, because the LLM sometimes gets confused by the tool options.
|
417
417
|
if (
|
@@ -753,11 +753,7 @@ async def generate_excalidraw_diagram(
|
|
753
753
|
yield None, None
|
754
754
|
return
|
755
755
|
|
756
|
-
|
757
|
-
|
758
|
-
inferred_queries = f"Instruction: {better_diagram_description_prompt}\n\nScratchpad: {scratchpad}"
|
759
|
-
|
760
|
-
yield inferred_queries, excalidraw_diagram_description.get("elements")
|
756
|
+
yield better_diagram_description_prompt, excalidraw_diagram_description
|
761
757
|
|
762
758
|
|
763
759
|
async def generate_better_diagram_description(
|
@@ -826,7 +822,7 @@ async def generate_excalidraw_diagram_from_description(
|
|
826
822
|
user: KhojUser = None,
|
827
823
|
agent: Agent = None,
|
828
824
|
tracer: dict = {},
|
829
|
-
) ->
|
825
|
+
) -> str:
|
830
826
|
personality_context = (
|
831
827
|
prompts.personality_context.format(personality=agent.personality) if agent and agent.personality else ""
|
832
828
|
)
|
@@ -842,18 +838,10 @@ async def generate_excalidraw_diagram_from_description(
|
|
842
838
|
)
|
843
839
|
raw_response = clean_json(raw_response)
|
844
840
|
try:
|
845
|
-
# Expect response to have `elements` and `scratchpad` keys
|
846
841
|
response: Dict[str, str] = json.loads(raw_response)
|
847
|
-
if (
|
848
|
-
not response
|
849
|
-
or not isinstance(response, Dict)
|
850
|
-
or not response.get("elements")
|
851
|
-
or not response.get("scratchpad")
|
852
|
-
):
|
853
|
-
raise AssertionError(f"Invalid response for generating Excalidraw diagram: {response}")
|
854
842
|
except Exception:
|
855
843
|
raise AssertionError(f"Invalid response for generating Excalidraw diagram: {raw_response}")
|
856
|
-
if not response or not isinstance(response
|
844
|
+
if not response or not isinstance(response, List) or not isinstance(response[0], Dict):
|
857
845
|
# TODO Some additional validation here that it's a valid Excalidraw diagram
|
858
846
|
raise AssertionError(f"Invalid response for improving diagram description: {response}")
|
859
847
|
|
@@ -1782,7 +1770,6 @@ Manage your automations [here](/automations).
|
|
1782
1770
|
class MessageProcessor:
|
1783
1771
|
def __init__(self):
|
1784
1772
|
self.references = {}
|
1785
|
-
self.usage = {}
|
1786
1773
|
self.raw_response = ""
|
1787
1774
|
|
1788
1775
|
def convert_message_chunk_to_json(self, raw_chunk: str) -> Dict[str, Any]:
|
@@ -1806,8 +1793,6 @@ class MessageProcessor:
|
|
1806
1793
|
chunk_type = ChatEvent(chunk["type"])
|
1807
1794
|
if chunk_type == ChatEvent.REFERENCES:
|
1808
1795
|
self.references = chunk["data"]
|
1809
|
-
elif chunk_type == ChatEvent.USAGE:
|
1810
|
-
self.usage = chunk["data"]
|
1811
1796
|
elif chunk_type == ChatEvent.MESSAGE:
|
1812
1797
|
chunk_data = chunk["data"]
|
1813
1798
|
if isinstance(chunk_data, dict):
|
@@ -1852,7 +1837,7 @@ async def read_chat_stream(response_iterator: AsyncGenerator[str, None]) -> Dict
|
|
1852
1837
|
if buffer:
|
1853
1838
|
processor.process_message_chunk(buffer)
|
1854
1839
|
|
1855
|
-
return {"response": processor.raw_response, "references": processor.references
|
1840
|
+
return {"response": processor.raw_response, "references": processor.references}
|
1856
1841
|
|
1857
1842
|
|
1858
1843
|
def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False):
|
khoj/routers/research.py
CHANGED
@@ -16,7 +16,7 @@ from khoj.processor.conversation.utils import (
|
|
16
16
|
construct_tool_chat_history,
|
17
17
|
)
|
18
18
|
from khoj.processor.tools.online_search import read_webpages, search_online
|
19
|
-
from khoj.processor.tools.run_code import run_code
|
19
|
+
from khoj.processor.tools.run_code import run_code, truncate_code_context
|
20
20
|
from khoj.routers.api import extract_references_and_questions
|
21
21
|
from khoj.routers.helpers import (
|
22
22
|
ChatEvent,
|
@@ -28,7 +28,6 @@ from khoj.utils.helpers import (
|
|
28
28
|
function_calling_description_for_llm,
|
29
29
|
is_none_or_empty,
|
30
30
|
timer,
|
31
|
-
truncate_code_context,
|
32
31
|
)
|
33
32
|
from khoj.utils.rawconfig import LocationData
|
34
33
|
|
khoj/utils/cli.py
CHANGED
@@ -40,8 +40,6 @@ def cli(args=None):
|
|
40
40
|
type=pathlib.Path,
|
41
41
|
help="Path to UNIX socket for server. Use to run server behind reverse proxy. Default: /tmp/uvicorn.sock",
|
42
42
|
)
|
43
|
-
parser.add_argument("--sslcert", type=str, help="Path to SSL certificate file")
|
44
|
-
parser.add_argument("--sslkey", type=str, help="Path to SSL key file")
|
45
43
|
parser.add_argument("--version", "-V", action="store_true", help="Print the installed Khoj version and exit")
|
46
44
|
parser.add_argument(
|
47
45
|
"--disable-chat-on-gpu", action="store_true", default=False, help="Disable using GPU for the offline chat model"
|
khoj/utils/constants.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
from pathlib import Path
|
2
|
-
from typing import Dict
|
3
2
|
|
4
3
|
app_root_directory = Path(__file__).parent.parent.parent
|
5
4
|
web_directory = app_root_directory / "khoj/interface/web/"
|
@@ -32,19 +31,3 @@ default_config = {
|
|
32
31
|
"image": {"encoder": "sentence-transformers/clip-ViT-B-32", "model_directory": "~/.khoj/search/image/"},
|
33
32
|
},
|
34
33
|
}
|
35
|
-
|
36
|
-
model_to_cost: Dict[str, Dict[str, float]] = {
|
37
|
-
# OpenAI Pricing: https://openai.com/api/pricing/
|
38
|
-
"gpt-4o": {"input": 2.50, "output": 10.00},
|
39
|
-
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
|
40
|
-
"o1-preview": {"input": 15.0, "output": 60.00},
|
41
|
-
"o1-mini": {"input": 3.0, "output": 12.0},
|
42
|
-
# Gemini Pricing: https://ai.google.dev/pricing
|
43
|
-
"gemini-1.5-flash": {"input": 0.075, "output": 0.30},
|
44
|
-
"gemini-1.5-flash-002": {"input": 0.075, "output": 0.30},
|
45
|
-
"gemini-1.5-pro": {"input": 1.25, "output": 5.00},
|
46
|
-
"gemini-1.5-pro-002": {"input": 1.25, "output": 5.00},
|
47
|
-
# Anthropic Pricing: https://www.anthropic.com/pricing#anthropic-api_
|
48
|
-
"claude-3-5-sonnet-20241022": {"input": 3.0, "output": 15.0},
|
49
|
-
"claude-3-5-haiku-20241022": {"input": 1.0, "output": 5.0},
|
50
|
-
}
|