khoj 1.30.11.dev64__py3-none-any.whl → 1.32.3.dev34__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 +4 -2
- khoj/database/adapters/__init__.py +67 -58
- khoj/database/admin.py +9 -9
- khoj/database/migrations/0077_chatmodel_alter_agent_chat_model_and_more.py +62 -0
- khoj/database/migrations/0078_khojuser_email_verification_code_expiry.py +17 -0
- khoj/database/models/__init__.py +9 -8
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/182-8cd8b17d40e6e989.js +20 -0
- khoj/interface/compiled/_next/static/chunks/1915-605f698f2573cfd4.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2117-9886e6a0232dc093.js +2 -0
- khoj/interface/compiled/_next/static/chunks/2581-455000f8aeb08fc3.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3175-b2e522f8ca392f7e.js +3 -0
- khoj/interface/compiled/_next/static/chunks/3727.dcea8f2193111552.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3789-a09e37a819171a9d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4124-0baa32400521e909.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4357-03ea130575287c27.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5243-f7f0a2a6e1ac5d28.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5427-3e7360c8e6ac9728.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{1279-4cb23143aa2c0228.js → 5473-b1cf56dedac6577a.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/5477-c5d7eabee28a789a.js +1 -0
- khoj/interface/compiled/_next/static/chunks/6065-64db9ad305ba0bcd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8667-d3e5bc726e4ff4e3.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9259-27d1ff42af9a43e0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/94ca1967.1d9b42d929a1ee8c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{1210.ef7a0f9a7e43da1d.js → 9597.83583248dfbf6e73.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/964ecbae.51d6faf8801d15e6.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9665-1ab5c8c667b74dca.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/_not-found/{page-cfba071f5a657256.js → page-a834eddae3e235df.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-e00fb81dca656a10.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-ab5ebe4efba9b582.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/{layout-7f1b79a2c67af0b4.js → layout-1fe1537449f43496.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-37d56a7bbfd307df.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-33934fc2d6ae6838.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/page-a0b61f10b0bf6dd5.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/layout-30e7fda7262713ce.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/page-33a3375b1414d1bd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/layout-c02531d586972d7d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/page-bbbfda90fa03c5be.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/layout-d09d6510a45cd4bd.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/page-430db6215e48aea2.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-e8e5db7830bf3f47.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-02dc1f2e2a41e522.js +1 -0
- khoj/interface/compiled/_next/static/chunks/d3ac728e-44ebd2a0c99b12a0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{fd9d1056-2e6c8140e79afc3b.js → fd9d1056-4482b99a36fd1673.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/main-app-de1f09df97a3cfc7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/main-db4bfac6b0a8d00b.js +1 -0
- khoj/interface/compiled/_next/static/chunks/pages/{_app-f870474a17b7f2fd.js → _app-3c9ca398d360b709.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/pages/{_error-c66a4e8afc46f17b.js → _error-cf5ca766ac8f493f.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- khoj/interface/compiled/_next/static/chunks/webpack-b0a1b08bb62bdc15.js +1 -0
- khoj/interface/compiled/_next/static/css/0f04760e76bba6c1.css +25 -0
- khoj/interface/compiled/_next/static/css/37a73b87f02df402.css +1 -0
- khoj/interface/compiled/_next/static/css/8e6a3ca11a60b189.css +1 -0
- khoj/interface/compiled/_next/static/css/9c164d9727dd8092.css +1 -0
- khoj/interface/compiled/_next/static/css/c3acbadc30537d04.css +1 -0
- khoj/interface/compiled/_next/static/css/dac88c17aaee5fcf.css +1 -0
- khoj/interface/compiled/_next/static/css/df4b47a2d0d85eae.css +1 -0
- khoj/interface/compiled/_next/static/css/e546bf5cc4914244.css +1 -0
- khoj/interface/compiled/_next/static/mqcIHpVqVWkmBuN0npYHA/_buildManifest.js +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +6 -6
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +7 -7
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +6 -6
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +6 -6
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +6 -6
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +8 -8
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +6 -6
- khoj/interface/email/magic_link.html +36 -13
- khoj/main.py +1 -1
- khoj/migrations/migrate_server_pg.py +7 -7
- khoj/processor/conversation/anthropic/anthropic_chat.py +5 -7
- khoj/processor/conversation/google/gemini_chat.py +5 -7
- khoj/processor/conversation/google/utils.py +0 -1
- khoj/processor/conversation/offline/chat_model.py +15 -14
- khoj/processor/conversation/openai/gpt.py +7 -9
- khoj/processor/conversation/openai/utils.py +31 -17
- khoj/processor/conversation/prompts.py +65 -49
- khoj/processor/conversation/utils.py +46 -44
- khoj/processor/tools/online_search.py +49 -2
- khoj/routers/api.py +22 -27
- khoj/routers/api_agents.py +4 -4
- khoj/routers/api_chat.py +33 -13
- khoj/routers/api_model.py +4 -4
- khoj/routers/auth.py +108 -7
- khoj/routers/email.py +10 -14
- khoj/routers/helpers.py +187 -143
- khoj/routers/web_client.py +1 -1
- khoj/utils/constants.py +1 -1
- khoj/utils/helpers.py +5 -3
- khoj/utils/initialization.py +28 -26
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/METADATA +7 -7
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/RECORD +102 -99
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/WHEEL +1 -1
- khoj/interface/compiled/_next/static/67DcUiU9MqkM1fhksWunh/_buildManifest.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1459.690bf20e7d7b7090.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1603-13cef426e0e650ec.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1970-1b63ac1497b03a10.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2646-92ba433951d02d52.js +0 -20
- khoj/interface/compiled/_next/static/chunks/3072-be830e4f8412b9d2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3463-081c031e873b7966.js +0 -3
- khoj/interface/compiled/_next/static/chunks/3690-51312931ba1eae30.js +0 -1
- khoj/interface/compiled/_next/static/chunks/3717-b46079dbe9f55694.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4504-62ac13e7d94c52f9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4602-460621c3241e0d13.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4752-554a3db270186ce3.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5512-7cc62049bbe60e11.js +0 -1
- khoj/interface/compiled/_next/static/chunks/5538-0ea2d3944ca051e1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7023-e8de2bded4df6539.js +0 -2
- khoj/interface/compiled/_next/static/chunks/7592-a09c39a38e60634b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8423-1dda16bc56236523.js +0 -1
- khoj/interface/compiled/_next/static/chunks/94ca1967.5584df65931cfe83.js +0 -1
- khoj/interface/compiled/_next/static/chunks/964ecbae.ea4eab2a3a835ffe.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-1878cc328ea380bd.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-8eead7920b0ff92a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-b5800b5286306140.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-9219a85f3477e722.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-d7d2ab93e519f0b2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/layout-6310c57b674dd6f5.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/page-3c32ad5472f75965.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-2ca475462c0b2176.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-faa998c71eb7ca8e.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/layout-f285795bc3154b8c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-cbe7f56b1f87d77a.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-592e8c470f2c2084.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-cd5757199539bbf2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/d3ac728e-a9e3522eef9b6b28.js +0 -1
- khoj/interface/compiled/_next/static/chunks/main-1ea5c2e0fdef4626.js +0 -1
- khoj/interface/compiled/_next/static/chunks/main-app-6d6ee3495efe03d4.js +0 -1
- khoj/interface/compiled/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/webpack-616f0694bfe6f6c1.js +0 -1
- khoj/interface/compiled/_next/static/css/1f293605f2871853.css +0 -1
- khoj/interface/compiled/_next/static/css/3c34171b174cc381.css +0 -25
- khoj/interface/compiled/_next/static/css/3cf13271869a4aeb.css +0 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
- khoj/interface/compiled/_next/static/css/5a400c87d295e68a.css +0 -1
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.css +0 -1
- khoj/interface/compiled/_next/static/css/9c4221ae0779cc04.css +0 -1
- /khoj/interface/compiled/_next/static/{67DcUiU9MqkM1fhksWunh → mqcIHpVqVWkmBuN0npYHA}/_ssgManifest.js +0 -0
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/entry_points.txt +0 -0
- {khoj-1.30.11.dev64.dist-info → khoj-1.32.3.dev34.dist-info}/licenses/LICENSE +0 -0
@@ -3,13 +3,13 @@ import logging
|
|
3
3
|
import os
|
4
4
|
from datetime import datetime, timedelta
|
5
5
|
from threading import Thread
|
6
|
-
from typing import Any, Iterator, List, Optional, Union
|
6
|
+
from typing import Any, Dict, Iterator, List, Optional, Union
|
7
7
|
|
8
8
|
import pyjson5
|
9
9
|
from langchain.schema import ChatMessage
|
10
10
|
from llama_cpp import Llama
|
11
11
|
|
12
|
-
from khoj.database.models import Agent,
|
12
|
+
from khoj.database.models import Agent, ChatModel, KhojUser
|
13
13
|
from khoj.processor.conversation import prompts
|
14
14
|
from khoj.processor.conversation.offline.utils import download_model
|
15
15
|
from khoj.processor.conversation.utils import (
|
@@ -23,7 +23,6 @@ from khoj.utils import state
|
|
23
23
|
from khoj.utils.constants import empty_escape_sequences
|
24
24
|
from khoj.utils.helpers import (
|
25
25
|
ConversationCommand,
|
26
|
-
in_debug_mode,
|
27
26
|
is_none_or_empty,
|
28
27
|
is_promptrace_enabled,
|
29
28
|
truncate_code_context,
|
@@ -96,7 +95,7 @@ def extract_questions_offline(
|
|
96
95
|
model_name=model,
|
97
96
|
loaded_model=offline_chat_model,
|
98
97
|
max_prompt_size=max_prompt_size,
|
99
|
-
model_type=
|
98
|
+
model_type=ChatModel.ModelType.OFFLINE,
|
100
99
|
query_files=query_files,
|
101
100
|
)
|
102
101
|
|
@@ -105,7 +104,7 @@ def extract_questions_offline(
|
|
105
104
|
response = send_message_to_model_offline(
|
106
105
|
messages,
|
107
106
|
loaded_model=offline_chat_model,
|
108
|
-
|
107
|
+
model_name=model,
|
109
108
|
max_prompt_size=max_prompt_size,
|
110
109
|
temperature=temperature,
|
111
110
|
response_type="json_object",
|
@@ -154,7 +153,7 @@ def converse_offline(
|
|
154
153
|
online_results={},
|
155
154
|
code_results={},
|
156
155
|
conversation_log={},
|
157
|
-
|
156
|
+
model_name: str = "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
|
158
157
|
loaded_model: Union[Any, None] = None,
|
159
158
|
completion_func=None,
|
160
159
|
conversation_commands=[ConversationCommand.Default],
|
@@ -166,6 +165,7 @@ def converse_offline(
|
|
166
165
|
query_files: str = None,
|
167
166
|
generated_files: List[FileAttachment] = None,
|
168
167
|
additional_context: List[str] = None,
|
168
|
+
generated_asset_results: Dict[str, Dict] = {},
|
169
169
|
tracer: dict = {},
|
170
170
|
) -> Union[ThreadedGenerator, Iterator[str]]:
|
171
171
|
"""
|
@@ -173,8 +173,8 @@ def converse_offline(
|
|
173
173
|
"""
|
174
174
|
# Initialize Variables
|
175
175
|
assert loaded_model is None or isinstance(loaded_model, Llama), "loaded_model must be of type Llama, if configured"
|
176
|
-
offline_chat_model = loaded_model or download_model(
|
177
|
-
tracer["chat_model"] =
|
176
|
+
offline_chat_model = loaded_model or download_model(model_name, max_tokens=max_prompt_size)
|
177
|
+
tracer["chat_model"] = model_name
|
178
178
|
current_date = datetime.now()
|
179
179
|
|
180
180
|
if agent and agent.personality:
|
@@ -227,17 +227,18 @@ def converse_offline(
|
|
227
227
|
system_prompt,
|
228
228
|
conversation_log,
|
229
229
|
context_message=context_message,
|
230
|
-
model_name=
|
230
|
+
model_name=model_name,
|
231
231
|
loaded_model=offline_chat_model,
|
232
232
|
max_prompt_size=max_prompt_size,
|
233
233
|
tokenizer_name=tokenizer_name,
|
234
|
-
model_type=
|
234
|
+
model_type=ChatModel.ModelType.OFFLINE,
|
235
235
|
query_files=query_files,
|
236
236
|
generated_files=generated_files,
|
237
|
+
generated_asset_results=generated_asset_results,
|
237
238
|
program_execution_context=additional_context,
|
238
239
|
)
|
239
240
|
|
240
|
-
logger.debug(f"Conversation Context for {
|
241
|
+
logger.debug(f"Conversation Context for {model_name}: {messages_to_print(messages)}")
|
241
242
|
|
242
243
|
g = ThreadedGenerator(references, online_results, completion_func=completion_func)
|
243
244
|
t = Thread(target=llm_thread, args=(g, messages, offline_chat_model, max_prompt_size, tracer))
|
@@ -271,7 +272,7 @@ def llm_thread(g, messages: List[ChatMessage], model: Any, max_prompt_size: int
|
|
271
272
|
def send_message_to_model_offline(
|
272
273
|
messages: List[ChatMessage],
|
273
274
|
loaded_model=None,
|
274
|
-
|
275
|
+
model_name="bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
|
275
276
|
temperature: float = 0.2,
|
276
277
|
streaming=False,
|
277
278
|
stop=[],
|
@@ -280,7 +281,7 @@ def send_message_to_model_offline(
|
|
280
281
|
tracer: dict = {},
|
281
282
|
):
|
282
283
|
assert loaded_model is None or isinstance(loaded_model, Llama), "loaded_model must be of type Llama, if configured"
|
283
|
-
offline_chat_model = loaded_model or download_model(
|
284
|
+
offline_chat_model = loaded_model or download_model(model_name, max_tokens=max_prompt_size)
|
284
285
|
messages_dict = [{"role": message.role, "content": message.content} for message in messages]
|
285
286
|
seed = int(os.getenv("KHOJ_LLM_SEED")) if os.getenv("KHOJ_LLM_SEED") else None
|
286
287
|
response = offline_chat_model.create_chat_completion(
|
@@ -299,7 +300,7 @@ def send_message_to_model_offline(
|
|
299
300
|
|
300
301
|
# Save conversation trace for non-streaming responses
|
301
302
|
# Streamed responses need to be saved by the calling function
|
302
|
-
tracer["chat_model"] =
|
303
|
+
tracer["chat_model"] = model_name
|
303
304
|
tracer["temperature"] = temperature
|
304
305
|
if is_promptrace_enabled():
|
305
306
|
commit_conversation_trace(messages, response_text, tracer)
|
@@ -5,7 +5,7 @@ from typing import Dict, List, Optional
|
|
5
5
|
import pyjson5
|
6
6
|
from langchain.schema import ChatMessage
|
7
7
|
|
8
|
-
from khoj.database.models import Agent,
|
8
|
+
from khoj.database.models import Agent, ChatModel, KhojUser
|
9
9
|
from khoj.processor.conversation import prompts
|
10
10
|
from khoj.processor.conversation.openai.utils import (
|
11
11
|
chat_completion_with_backoff,
|
@@ -83,7 +83,7 @@ def extract_questions(
|
|
83
83
|
prompt = construct_structured_message(
|
84
84
|
message=prompt,
|
85
85
|
images=query_images,
|
86
|
-
model_type=
|
86
|
+
model_type=ChatModel.ModelType.OPENAI,
|
87
87
|
vision_enabled=vision_enabled,
|
88
88
|
attached_file_context=query_files,
|
89
89
|
)
|
@@ -128,7 +128,7 @@ def send_message_to_model(
|
|
128
128
|
# Get Response from GPT
|
129
129
|
return completion_with_backoff(
|
130
130
|
messages=messages,
|
131
|
-
|
131
|
+
model_name=model,
|
132
132
|
openai_api_key=api_key,
|
133
133
|
temperature=temperature,
|
134
134
|
api_base_url=api_base_url,
|
@@ -137,7 +137,7 @@ def send_message_to_model(
|
|
137
137
|
)
|
138
138
|
|
139
139
|
|
140
|
-
def
|
140
|
+
def converse_openai(
|
141
141
|
references,
|
142
142
|
user_query,
|
143
143
|
online_results: Optional[Dict[str, Dict]] = None,
|
@@ -157,9 +157,8 @@ def converse(
|
|
157
157
|
query_images: Optional[list[str]] = None,
|
158
158
|
vision_available: bool = False,
|
159
159
|
query_files: str = None,
|
160
|
-
generated_images: Optional[list[str]] = None,
|
161
160
|
generated_files: List[FileAttachment] = None,
|
162
|
-
|
161
|
+
generated_asset_results: Dict[str, Dict] = {},
|
163
162
|
program_execution_context: List[str] = None,
|
164
163
|
tracer: dict = {},
|
165
164
|
):
|
@@ -221,11 +220,10 @@ def converse(
|
|
221
220
|
tokenizer_name=tokenizer_name,
|
222
221
|
query_images=query_images,
|
223
222
|
vision_enabled=vision_available,
|
224
|
-
model_type=
|
223
|
+
model_type=ChatModel.ModelType.OPENAI,
|
225
224
|
query_files=query_files,
|
226
|
-
generated_excalidraw_diagram=generated_excalidraw_diagram,
|
227
225
|
generated_files=generated_files,
|
228
|
-
|
226
|
+
generated_asset_results=generated_asset_results,
|
229
227
|
program_execution_context=program_execution_context,
|
230
228
|
)
|
231
229
|
logger.debug(f"Conversation Context for GPT: {messages_to_print(messages)}")
|
@@ -40,7 +40,13 @@ openai_clients: Dict[str, openai.OpenAI] = {}
|
|
40
40
|
reraise=True,
|
41
41
|
)
|
42
42
|
def completion_with_backoff(
|
43
|
-
messages,
|
43
|
+
messages,
|
44
|
+
model_name: str,
|
45
|
+
temperature=0,
|
46
|
+
openai_api_key=None,
|
47
|
+
api_base_url=None,
|
48
|
+
model_kwargs: dict = {},
|
49
|
+
tracer: dict = {},
|
44
50
|
) -> str:
|
45
51
|
client_key = f"{openai_api_key}--{api_base_url}"
|
46
52
|
client: openai.OpenAI | None = openai_clients.get(client_key)
|
@@ -52,13 +58,17 @@ def completion_with_backoff(
|
|
52
58
|
openai_clients[client_key] = client
|
53
59
|
|
54
60
|
formatted_messages = [{"role": message.role, "content": message.content} for message in messages]
|
55
|
-
stream = True
|
56
61
|
|
57
62
|
# Update request parameters for compatability with o1 model series
|
58
63
|
# Refer: https://platform.openai.com/docs/guides/reasoning/beta-limitations
|
59
|
-
|
64
|
+
stream = True
|
65
|
+
model_kwargs["stream_options"] = {"include_usage": True}
|
66
|
+
if model_name == "o1":
|
67
|
+
temperature = 1
|
68
|
+
stream = False
|
69
|
+
model_kwargs.pop("stream_options", None)
|
70
|
+
elif model_name.startswith("o1"):
|
60
71
|
temperature = 1
|
61
|
-
model_kwargs.pop("stop", None)
|
62
72
|
model_kwargs.pop("response_format", None)
|
63
73
|
|
64
74
|
if os.getenv("KHOJ_LLM_SEED"):
|
@@ -66,12 +76,11 @@ def completion_with_backoff(
|
|
66
76
|
|
67
77
|
chat: ChatCompletion | openai.Stream[ChatCompletionChunk] = client.chat.completions.create(
|
68
78
|
messages=formatted_messages, # type: ignore
|
69
|
-
model=
|
79
|
+
model=model_name, # type: ignore
|
70
80
|
stream=stream,
|
71
|
-
stream_options={"include_usage": True} if stream else {},
|
72
81
|
temperature=temperature,
|
73
82
|
timeout=20,
|
74
|
-
**
|
83
|
+
**model_kwargs,
|
75
84
|
)
|
76
85
|
|
77
86
|
aggregated_response = ""
|
@@ -91,10 +100,11 @@ def completion_with_backoff(
|
|
91
100
|
# Calculate cost of chat
|
92
101
|
input_tokens = chunk.usage.prompt_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
93
102
|
output_tokens = chunk.usage.completion_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
94
|
-
|
103
|
+
cost = chunk.usage.model_extra.get("estimated_cost") or 0 # Estimated costs returned by DeepInfra API
|
104
|
+
tracer["usage"] = get_chat_usage_metrics(model_name, input_tokens, output_tokens, tracer.get("usage"), cost)
|
95
105
|
|
96
106
|
# Save conversation trace
|
97
|
-
tracer["chat_model"] =
|
107
|
+
tracer["chat_model"] = model_name
|
98
108
|
tracer["temperature"] = temperature
|
99
109
|
if is_promptrace_enabled():
|
100
110
|
commit_conversation_trace(messages, aggregated_response, tracer)
|
@@ -139,11 +149,11 @@ def chat_completion_with_backoff(
|
|
139
149
|
def llm_thread(
|
140
150
|
g,
|
141
151
|
messages,
|
142
|
-
model_name,
|
152
|
+
model_name: str,
|
143
153
|
temperature,
|
144
154
|
openai_api_key=None,
|
145
155
|
api_base_url=None,
|
146
|
-
model_kwargs=
|
156
|
+
model_kwargs: dict = {},
|
147
157
|
tracer: dict = {},
|
148
158
|
):
|
149
159
|
try:
|
@@ -158,13 +168,17 @@ def llm_thread(
|
|
158
168
|
client = openai_clients[client_key]
|
159
169
|
|
160
170
|
formatted_messages = [{"role": message.role, "content": message.content} for message in messages]
|
161
|
-
stream = True
|
162
171
|
|
163
172
|
# Update request parameters for compatability with o1 model series
|
164
173
|
# Refer: https://platform.openai.com/docs/guides/reasoning/beta-limitations
|
165
|
-
|
174
|
+
stream = True
|
175
|
+
model_kwargs["stream_options"] = {"include_usage": True}
|
176
|
+
if model_name == "o1":
|
177
|
+
temperature = 1
|
178
|
+
stream = False
|
179
|
+
model_kwargs.pop("stream_options", None)
|
180
|
+
elif model_name.startswith("o1-"):
|
166
181
|
temperature = 1
|
167
|
-
model_kwargs.pop("stop", None)
|
168
182
|
model_kwargs.pop("response_format", None)
|
169
183
|
|
170
184
|
if os.getenv("KHOJ_LLM_SEED"):
|
@@ -174,10 +188,9 @@ def llm_thread(
|
|
174
188
|
messages=formatted_messages,
|
175
189
|
model=model_name, # type: ignore
|
176
190
|
stream=stream,
|
177
|
-
stream_options={"include_usage": True} if stream else {},
|
178
191
|
temperature=temperature,
|
179
192
|
timeout=20,
|
180
|
-
**
|
193
|
+
**model_kwargs,
|
181
194
|
)
|
182
195
|
|
183
196
|
aggregated_response = ""
|
@@ -202,7 +215,8 @@ def llm_thread(
|
|
202
215
|
# Calculate cost of chat
|
203
216
|
input_tokens = chunk.usage.prompt_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
204
217
|
output_tokens = chunk.usage.completion_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
205
|
-
|
218
|
+
cost = chunk.usage.model_extra.get("estimated_cost") or 0 # Estimated costs returned by DeepInfra API
|
219
|
+
tracer["usage"] = get_chat_usage_metrics(model_name, input_tokens, output_tokens, tracer.get("usage"), cost)
|
206
220
|
|
207
221
|
# Save conversation trace
|
208
222
|
tracer["chat_model"] = model_name
|
@@ -178,40 +178,41 @@ Improved Prompt:
|
|
178
178
|
""".strip()
|
179
179
|
)
|
180
180
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
)
|
181
|
+
generated_assets_context = PromptTemplate.from_template(
|
182
|
+
"""
|
183
|
+
You have ALREADY created the assets described below. They will automatically be added to the final response.
|
184
|
+
You can provide a summary of your reasoning from the information below or use it to respond to my previous query.
|
186
185
|
|
187
|
-
|
188
|
-
|
189
|
-
|
186
|
+
Generated Assets:
|
187
|
+
{generated_assets}
|
188
|
+
|
189
|
+
Limit your response to 3 sentences max. Be succinct, clear, and informative.
|
190
190
|
""".strip()
|
191
191
|
)
|
192
192
|
|
193
|
+
|
193
194
|
## Diagram Generation
|
194
195
|
## --
|
195
196
|
|
196
197
|
improve_diagram_description_prompt = PromptTemplate.from_template(
|
197
198
|
"""
|
198
|
-
|
199
|
+
You are an architect working with a novice digital artist using a diagramming software.
|
199
200
|
{personality_context}
|
200
201
|
|
201
|
-
|
202
|
-
-
|
203
|
-
-
|
204
|
-
-
|
205
|
-
-
|
206
|
-
-
|
202
|
+
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
|
203
|
+
- Text
|
204
|
+
- Rectangle
|
205
|
+
- Ellipse
|
206
|
+
- Line
|
207
|
+
- Arrow
|
207
208
|
|
208
|
-
|
209
|
+
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.
|
209
210
|
|
210
|
-
-
|
211
|
-
-
|
212
|
-
-
|
213
|
-
-
|
214
|
-
-
|
211
|
+
- Include the full, exact description. the artist does not have much experience, so be precise.
|
212
|
+
- Describe the layout.
|
213
|
+
- You can only use straight lines.
|
214
|
+
- Use simple, concise language.
|
215
|
+
- Keep it simple and easy to understand. the artist is easily distracted.
|
215
216
|
|
216
217
|
Today's Date: {current_date}
|
217
218
|
User's Location: {location}
|
@@ -337,6 +338,17 @@ Diagram Description: {query}
|
|
337
338
|
""".strip()
|
338
339
|
)
|
339
340
|
|
341
|
+
failed_diagram_generation = PromptTemplate.from_template(
|
342
|
+
"""
|
343
|
+
You attempted to programmatically generate a diagram but failed due to a system issue. You are normally able to generate diagrams, but you encountered a system issue this time.
|
344
|
+
|
345
|
+
You can create an ASCII image of the diagram in response instead.
|
346
|
+
|
347
|
+
This is the diagram you attempted to make:
|
348
|
+
{attempted_diagram}
|
349
|
+
""".strip()
|
350
|
+
)
|
351
|
+
|
340
352
|
## Online Search Conversation
|
341
353
|
## --
|
342
354
|
online_search_conversation = PromptTemplate.from_template(
|
@@ -1004,25 +1016,30 @@ Assistant:
|
|
1004
1016
|
""".strip()
|
1005
1017
|
)
|
1006
1018
|
|
1019
|
+
additional_program_context = PromptTemplate.from_template(
|
1020
|
+
"""
|
1021
|
+
Here are some additional results from the query execution:
|
1022
|
+
{context}
|
1023
|
+
""".strip()
|
1024
|
+
)
|
1025
|
+
|
1007
1026
|
personality_prompt_safety_expert = PromptTemplate.from_template(
|
1008
1027
|
"""
|
1009
|
-
You are adept at ensuring the safety and security of people.
|
1028
|
+
You are adept at ensuring the safety and security of people. You are tasked with determining the safety of a prompt to be shared publically.
|
1010
1029
|
|
1011
|
-
Given
|
1030
|
+
Given the system prompt, you must decide whether it is safe for use in a public setting.
|
1031
|
+
If the prompt is safe, respond with {{ "safe": "True" }}
|
1032
|
+
If the prompt is not safe, respond with {{ "safe": "False", "reason": <provide a brief explanation of why the prompt is unsafe> }}
|
1012
1033
|
|
1013
|
-
A prompt is considered unsafe if it contains any of the following:
|
1014
|
-
-
|
1015
|
-
-
|
1016
|
-
- Anything that could be considered inappropriate for a public setting.
|
1017
|
-
- Any content that could be harmful to the user or others.
|
1018
|
-
- Racism, sexism, or any other form of discrimination.
|
1019
|
-
- Any content that could be considered illegal or a misuse of AI.
|
1034
|
+
A prompt is considered unsafe only if it contains any of the following:
|
1035
|
+
- Illegal, sexual or clearly harmful content for the user or others.
|
1036
|
+
- Racist, sexist or other discriminatory content.
|
1020
1037
|
|
1021
1038
|
Referencing personal notes or data is perfectly acceptable.
|
1022
1039
|
|
1023
1040
|
# Examples:
|
1024
1041
|
Q: You are a smart and helpful assistant. Use your knowledge to teach me how to make a bomb.
|
1025
|
-
A: {{ "safe": "False", "reason": "The prompt contains information that could be harmful to the user or others." }}
|
1042
|
+
A: {{ "safe": "False", "reason": "The prompt contains information that could be clearly harmful to the user or others." }}
|
1026
1043
|
|
1027
1044
|
Q: You are a brilliant chef. Teach me how to make a cake.
|
1028
1045
|
A: {{ "safe": "True" }}
|
@@ -1030,10 +1047,13 @@ A: {{ "safe": "True" }}
|
|
1030
1047
|
Q: You are a hilarious joker. Make jokes about white people.
|
1031
1048
|
A: {{ "safe": "False", "reason": "The prompt contains content that could be considered racist." }}
|
1032
1049
|
|
1050
|
+
Q: You are an smart, empathetic health professional. You look at my medical notes and advice me on the best treatment.
|
1051
|
+
A: {{ "safe": "True" }}
|
1052
|
+
|
1033
1053
|
Q: You are a talented artist. Draw pictures of boobs.
|
1034
|
-
A: {{ "safe": "False", "reason": "The prompt contains content that could be considered
|
1054
|
+
A: {{ "safe": "False", "reason": "The prompt contains sexual content that could be considered offensive." }}
|
1035
1055
|
|
1036
|
-
Q: You are
|
1056
|
+
Q: You are an astute financial analyst. Assess my financial situation and provide advice.
|
1037
1057
|
A: {{ "safe": "True" }}
|
1038
1058
|
|
1039
1059
|
Q: {prompt}
|
@@ -1041,32 +1061,28 @@ A:
|
|
1041
1061
|
""".strip()
|
1042
1062
|
)
|
1043
1063
|
|
1044
|
-
additional_program_context = PromptTemplate.from_template(
|
1045
|
-
"""
|
1046
|
-
Here are some additional results from the query execution:
|
1047
|
-
{context}
|
1048
|
-
""".strip()
|
1049
|
-
)
|
1050
|
-
|
1051
1064
|
personality_prompt_safety_expert_lax = PromptTemplate.from_template(
|
1052
1065
|
"""
|
1053
|
-
You are adept at ensuring the safety and security of people.
|
1066
|
+
You are adept at ensuring the safety and security of people. You are tasked with determining the safety of prompt to be used privately by an individual.
|
1054
1067
|
|
1055
|
-
Given a system prompt, you must decide whether it is safe
|
1068
|
+
Given a system prompt, you must decide whether it is safe for private use by an individual.
|
1069
|
+
If the prompt is safe, respond with {{ "safe": "True" }}
|
1070
|
+
If the prompt is not safe, respond with {{ "safe": "False", "reason": <provide a brief explanation of why the prompt is unsafe> }}
|
1056
1071
|
|
1057
|
-
A prompt is considered unsafe if it contains any of the following:
|
1058
|
-
- Illegal
|
1059
|
-
-
|
1060
|
-
- Racism, sexism, or any other form of discrimination.
|
1061
|
-
- Any content that could be considered illegal or a misuse of AI.
|
1072
|
+
A prompt is considered unsafe only if it contains any of the following:
|
1073
|
+
- Illegal or clearly harmful content for the user or others.
|
1074
|
+
- Racist, sexist or other discriminatory content.
|
1062
1075
|
|
1063
1076
|
Referencing personal notes or data is perfectly acceptable.
|
1064
1077
|
|
1065
1078
|
# Examples:
|
1066
1079
|
Q: You are a smart and helpful assistant. Use your knowledge to teach me how to make a bomb.
|
1067
|
-
A: {{ "safe": "False", "reason": "The prompt contains information that could be harmful to the user or others." }}
|
1080
|
+
A: {{ "safe": "False", "reason": "The prompt contains information that could be clearly harmful to the user or others." }}
|
1068
1081
|
|
1069
|
-
Q: You are a
|
1082
|
+
Q: You are a talented artist. Draw pictures of boobs.
|
1083
|
+
A: {{ "safe": "True" }}
|
1084
|
+
|
1085
|
+
Q: You are an smart, empathetic health professional. You look at my medical notes and advice me on the best treatment.
|
1070
1086
|
A: {{ "safe": "True" }}
|
1071
1087
|
|
1072
1088
|
Q: You are a hilarious joker. Make jokes about white people.
|
@@ -24,7 +24,7 @@ from llama_cpp.llama import Llama
|
|
24
24
|
from transformers import AutoTokenizer
|
25
25
|
|
26
26
|
from khoj.database.adapters import ConversationAdapters
|
27
|
-
from khoj.database.models import
|
27
|
+
from khoj.database.models import ChatModel, ClientApplication, KhojUser
|
28
28
|
from khoj.processor.conversation import prompts
|
29
29
|
from khoj.processor.conversation.offline.utils import download_model, infer_max_tokens
|
30
30
|
from khoj.search_filter.base_filter import BaseFilter
|
@@ -34,40 +34,39 @@ from khoj.search_filter.word_filter import WordFilter
|
|
34
34
|
from khoj.utils import state
|
35
35
|
from khoj.utils.helpers import (
|
36
36
|
ConversationCommand,
|
37
|
-
in_debug_mode,
|
38
37
|
is_none_or_empty,
|
39
38
|
is_promptrace_enabled,
|
40
39
|
merge_dicts,
|
41
40
|
)
|
42
41
|
from khoj.utils.rawconfig import FileAttachment
|
42
|
+
from khoj.utils.yaml import yaml_dump
|
43
43
|
|
44
44
|
logger = logging.getLogger(__name__)
|
45
45
|
|
46
46
|
try:
|
47
47
|
from git import Repo
|
48
48
|
except ImportError:
|
49
|
-
if
|
50
|
-
logger.warning("GitPython not installed. `pip install gitpython` to
|
49
|
+
if is_promptrace_enabled():
|
50
|
+
logger.warning("GitPython not installed. `pip install gitpython` to use prompt tracer.")
|
51
51
|
|
52
52
|
model_to_prompt_size = {
|
53
53
|
# OpenAI Models
|
54
|
-
"gpt-4o":
|
55
|
-
"gpt-4o-mini":
|
56
|
-
"o1
|
57
|
-
"o1-mini":
|
54
|
+
"gpt-4o": 60000,
|
55
|
+
"gpt-4o-mini": 60000,
|
56
|
+
"o1": 20000,
|
57
|
+
"o1-mini": 60000,
|
58
58
|
# Google Models
|
59
|
-
"gemini-1.5-flash":
|
60
|
-
"gemini-1.5-pro":
|
59
|
+
"gemini-1.5-flash": 60000,
|
60
|
+
"gemini-1.5-pro": 60000,
|
61
61
|
# Anthropic Models
|
62
|
-
"claude-3-5-sonnet-20241022":
|
63
|
-
"claude-3-5-haiku-20241022":
|
62
|
+
"claude-3-5-sonnet-20241022": 60000,
|
63
|
+
"claude-3-5-haiku-20241022": 60000,
|
64
64
|
# Offline Models
|
65
|
-
"
|
65
|
+
"Qwen/Qwen2.5-14B-Instruct-GGUF": 20000,
|
66
66
|
"bartowski/Meta-Llama-3.1-8B-Instruct-GGUF": 20000,
|
67
67
|
"bartowski/Llama-3.2-3B-Instruct-GGUF": 20000,
|
68
68
|
"bartowski/gemma-2-9b-it-GGUF": 6000,
|
69
69
|
"bartowski/gemma-2-2b-it-GGUF": 6000,
|
70
|
-
"Qwen/Qwen2.5-14B-Instruct-GGUF": 20000,
|
71
70
|
}
|
72
71
|
model_to_tokenizer: Dict[str, str] = {}
|
73
72
|
|
@@ -329,9 +328,9 @@ def construct_structured_message(
|
|
329
328
|
Format messages into appropriate multimedia format for supported chat model types
|
330
329
|
"""
|
331
330
|
if model_type in [
|
332
|
-
|
333
|
-
|
334
|
-
|
331
|
+
ChatModel.ModelType.OPENAI,
|
332
|
+
ChatModel.ModelType.GOOGLE,
|
333
|
+
ChatModel.ModelType.ANTHROPIC,
|
335
334
|
]:
|
336
335
|
if not attached_file_context and not (vision_enabled and images):
|
337
336
|
return message
|
@@ -381,9 +380,8 @@ def generate_chatml_messages_with_context(
|
|
381
380
|
model_type="",
|
382
381
|
context_message="",
|
383
382
|
query_files: str = None,
|
384
|
-
generated_images: Optional[list[str]] = None,
|
385
383
|
generated_files: List[FileAttachment] = None,
|
386
|
-
|
384
|
+
generated_asset_results: Dict[str, Dict] = {},
|
387
385
|
program_execution_context: List[str] = [],
|
388
386
|
):
|
389
387
|
"""Generate chat messages with appropriate context from previous conversation to send to the chat model"""
|
@@ -403,11 +401,15 @@ def generate_chatml_messages_with_context(
|
|
403
401
|
message_context = ""
|
404
402
|
message_attached_files = ""
|
405
403
|
|
404
|
+
generated_assets = {}
|
405
|
+
|
406
406
|
chat_message = chat.get("message")
|
407
407
|
role = "user" if chat["by"] == "you" else "assistant"
|
408
408
|
|
409
|
+
# Legacy code to handle excalidraw diagrams prior to Dec 2024
|
409
410
|
if chat["by"] == "khoj" and "excalidraw" in chat["intent"].get("type", ""):
|
410
411
|
chat_message = chat["intent"].get("inferred-queries")[0]
|
412
|
+
|
411
413
|
if not is_none_or_empty(chat.get("context")):
|
412
414
|
references = "\n\n".join(
|
413
415
|
{
|
@@ -434,15 +436,23 @@ def generate_chatml_messages_with_context(
|
|
434
436
|
reconstructed_context_message = ChatMessage(content=message_context, role="user")
|
435
437
|
chatml_messages.insert(0, reconstructed_context_message)
|
436
438
|
|
437
|
-
if chat.get("images") and role == "assistant":
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
439
|
+
if not is_none_or_empty(chat.get("images")) and role == "assistant":
|
440
|
+
generated_assets["image"] = {
|
441
|
+
"query": chat.get("intent", {}).get("inferred-queries", [user_message])[0],
|
442
|
+
}
|
443
|
+
|
444
|
+
if not is_none_or_empty(chat.get("excalidrawDiagram")) and role == "assistant":
|
445
|
+
generated_assets["diagram"] = {
|
446
|
+
"query": chat.get("intent", {}).get("inferred-queries", [user_message])[0],
|
447
|
+
}
|
448
|
+
|
449
|
+
if not is_none_or_empty(generated_assets):
|
450
|
+
chatml_messages.append(
|
451
|
+
ChatMessage(
|
452
|
+
content=f"{prompts.generated_assets_context.format(generated_assets=yaml_dump(generated_assets))}\n",
|
453
|
+
role="user",
|
454
|
+
)
|
444
455
|
)
|
445
|
-
chatml_messages.append(ChatMessage(content=file_attachment_message, role="user"))
|
446
456
|
|
447
457
|
message_content = construct_structured_message(
|
448
458
|
chat_message, chat.get("images") if role == "user" else [], model_type, vision_enabled
|
@@ -456,23 +466,19 @@ def generate_chatml_messages_with_context(
|
|
456
466
|
|
457
467
|
messages = []
|
458
468
|
|
459
|
-
if not is_none_or_empty(
|
469
|
+
if not is_none_or_empty(generated_asset_results):
|
460
470
|
messages.append(
|
461
471
|
ChatMessage(
|
462
|
-
content=
|
463
|
-
user_message, query_images, model_type, vision_enabled, query_files
|
464
|
-
),
|
472
|
+
content=f"{prompts.generated_assets_context.format(generated_assets=yaml_dump(generated_asset_results))}\n\n",
|
465
473
|
role="user",
|
466
474
|
)
|
467
475
|
)
|
468
|
-
if not is_none_or_empty(context_message):
|
469
|
-
messages.append(ChatMessage(content=context_message, role="user"))
|
470
476
|
|
471
|
-
if
|
477
|
+
if not is_none_or_empty(user_message):
|
472
478
|
messages.append(
|
473
479
|
ChatMessage(
|
474
480
|
content=construct_structured_message(
|
475
|
-
|
481
|
+
user_message, query_images, model_type, vision_enabled, query_files
|
476
482
|
),
|
477
483
|
role="user",
|
478
484
|
)
|
@@ -482,16 +488,12 @@ def generate_chatml_messages_with_context(
|
|
482
488
|
message_attached_files = gather_raw_query_files({file.name: file.content for file in generated_files})
|
483
489
|
messages.append(ChatMessage(content=message_attached_files, role="assistant"))
|
484
490
|
|
485
|
-
if generated_excalidraw_diagram:
|
486
|
-
messages.append(ChatMessage(content=prompts.generated_diagram_attachment.format(), role="assistant"))
|
487
|
-
|
488
491
|
if program_execution_context:
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
)
|
492
|
+
program_context_text = "\n".join(program_execution_context)
|
493
|
+
context_message += f"{prompts.additional_program_context.format(context=program_context_text)}\n"
|
494
|
+
|
495
|
+
if not is_none_or_empty(context_message):
|
496
|
+
messages.append(ChatMessage(content=context_message, role="user"))
|
495
497
|
|
496
498
|
if len(chatml_messages) > 0:
|
497
499
|
messages += chatml_messages
|