khoj 1.31.0__py3-none-any.whl → 1.31.1.dev62__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 +66 -57
- 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 +8 -7
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/9O0zlbSu9rZ459NKSv2aS/_buildManifest.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1201-aac5b5f9a28edf09.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1662-adf4c615bef2fdc2.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1915-878efdc6db697d8f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2117-9886e6a0232dc093.js +2 -0
- khoj/interface/compiled/_next/static/chunks/{5538-0ea2d3944ca051e1.js → 2264-23b2c33cd8c74d07.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/2781-4f022b6e9eb6df6e.js +3 -0
- khoj/interface/compiled/_next/static/chunks/2813-f842b08bce4c61a0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3091-e0ff2288e8a29dd7.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3727.dcea8f2193111552.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5401-980a4f512c81232e.js +20 -0
- khoj/interface/compiled/_next/static/chunks/{1279-4cb23143aa2c0228.js → 5473-b1cf56dedac6577a.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/5477-8d032883aed8a2d2.js +1 -0
- khoj/interface/compiled/_next/static/chunks/6589-f806113de469d684.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8117-2e1697b782c5f185.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8407-af326f8c200e619b.js +1 -0
- khoj/interface/compiled/_next/static/chunks/8667-d3e5bc726e4ff4e3.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9058-25ef3344805f06ea.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9262-21c17de77aafdce8.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/app/_not-found/{page-cfba071f5a657256.js → page-a834eddae3e235df.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/layout-e49165209d2e406c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-6f4ff1d32a66ed71.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/{layout-7f1b79a2c67af0b4.js → layout-dce809da279a4a8a.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-148a48ddfb2ff90d.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-be00870a40de3a25.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/layout-30e7fda7262713ce.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/page-765292332c31523e.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-bd47c769b7700d1d.js → page-7af2cab294dccd81.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/layout-b3f6bc6f1aa118e0.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/page-6b600bf11fa89194.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-6fb51c5c80f8ec67.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-751695d28116e626.js → page-6054e88b56708f44.js} +1 -1
- 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-afd772b9f7c34b3f.js +1 -0
- khoj/interface/compiled/_next/static/css/3f27c3cf45375eb5.css +1 -0
- khoj/interface/compiled/_next/static/css/65ac59e147eb2057.css +25 -0
- khoj/interface/compiled/_next/static/css/8a00c3799ec0c9f8.css +1 -0
- khoj/interface/compiled/_next/static/css/{e8fb39147bff7bb4.css → 9504108437df6804.css} +1 -1
- 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 +3 -3
- khoj/processor/conversation/google/gemini_chat.py +3 -3
- khoj/processor/conversation/offline/chat_model.py +12 -12
- khoj/processor/conversation/openai/gpt.py +4 -4
- khoj/processor/conversation/openai/utils.py +18 -10
- khoj/processor/conversation/utils.py +4 -4
- 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 +19 -12
- khoj/routers/api_model.py +4 -4
- khoj/routers/auth.py +94 -7
- khoj/routers/email.py +10 -14
- khoj/routers/helpers.py +176 -134
- khoj/routers/web_client.py +1 -1
- khoj/utils/helpers.py +5 -3
- khoj/utils/initialization.py +28 -26
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev62.dist-info}/METADATA +5 -5
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev62.dist-info}/RECORD +96 -93
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev62.dist-info}/WHEEL +1 -1
- khoj/interface/compiled/_next/static/SHDrv3iet5TKNwccvVt6m/_buildManifest.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1459.690bf20e7d7b7090.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1603-f8ef9930c1f4eaef.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/4200-ea75740bb3c6ae60.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/5512-7cc62049bbe60e11.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-379949e11f084cf5.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-ca10c1cf79ae54bb.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-1072c3b0ab136e74.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-8a87c5de878f4f44.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/layout-6310c57b674dd6f5.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/page-b09139cb91859cd7.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-cae84c87073877f0.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-2a2679b6e10dbac1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-3b0c60bc13a963db.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-4bd818a6399690ae.js +0 -1
- khoj/interface/compiled/_next/static/css/1f293605f2871853.css +0 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
- khoj/interface/compiled/_next/static/css/fd628f01a581ec3c.css +0 -25
- /khoj/interface/compiled/_next/static/{SHDrv3iet5TKNwccvVt6m → 9O0zlbSu9rZ459NKSv2aS}/_ssgManifest.js +0 -0
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev62.dist-info}/entry_points.txt +0 -0
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev62.dist-info}/licenses/LICENSE +0 -0
@@ -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.anthropic.utils import (
|
11
11
|
anthropic_chat_completion_with_backoff,
|
@@ -85,7 +85,7 @@ def extract_questions_anthropic(
|
|
85
85
|
prompt = construct_structured_message(
|
86
86
|
message=prompt,
|
87
87
|
images=query_images,
|
88
|
-
model_type=
|
88
|
+
model_type=ChatModel.ModelType.ANTHROPIC,
|
89
89
|
vision_enabled=vision_enabled,
|
90
90
|
attached_file_context=query_files,
|
91
91
|
)
|
@@ -218,7 +218,7 @@ def converse_anthropic(
|
|
218
218
|
tokenizer_name=tokenizer_name,
|
219
219
|
query_images=query_images,
|
220
220
|
vision_enabled=vision_available,
|
221
|
-
model_type=
|
221
|
+
model_type=ChatModel.ModelType.ANTHROPIC,
|
222
222
|
query_files=query_files,
|
223
223
|
generated_files=generated_files,
|
224
224
|
generated_asset_results=generated_asset_results,
|
@@ -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.google.utils import (
|
11
11
|
format_messages_for_gemini,
|
@@ -86,7 +86,7 @@ def extract_questions_gemini(
|
|
86
86
|
prompt = construct_structured_message(
|
87
87
|
message=prompt,
|
88
88
|
images=query_images,
|
89
|
-
model_type=
|
89
|
+
model_type=ChatModel.ModelType.GOOGLE,
|
90
90
|
vision_enabled=vision_enabled,
|
91
91
|
attached_file_context=query_files,
|
92
92
|
)
|
@@ -229,7 +229,7 @@ def converse_gemini(
|
|
229
229
|
tokenizer_name=tokenizer_name,
|
230
230
|
query_images=query_images,
|
231
231
|
vision_enabled=vision_available,
|
232
|
-
model_type=
|
232
|
+
model_type=ChatModel.ModelType.GOOGLE,
|
233
233
|
query_files=query_files,
|
234
234
|
generated_files=generated_files,
|
235
235
|
generated_asset_results=generated_asset_results,
|
@@ -9,7 +9,7 @@ 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 (
|
@@ -96,7 +96,7 @@ def extract_questions_offline(
|
|
96
96
|
model_name=model,
|
97
97
|
loaded_model=offline_chat_model,
|
98
98
|
max_prompt_size=max_prompt_size,
|
99
|
-
model_type=
|
99
|
+
model_type=ChatModel.ModelType.OFFLINE,
|
100
100
|
query_files=query_files,
|
101
101
|
)
|
102
102
|
|
@@ -105,7 +105,7 @@ def extract_questions_offline(
|
|
105
105
|
response = send_message_to_model_offline(
|
106
106
|
messages,
|
107
107
|
loaded_model=offline_chat_model,
|
108
|
-
|
108
|
+
model_name=model,
|
109
109
|
max_prompt_size=max_prompt_size,
|
110
110
|
temperature=temperature,
|
111
111
|
response_type="json_object",
|
@@ -154,7 +154,7 @@ def converse_offline(
|
|
154
154
|
online_results={},
|
155
155
|
code_results={},
|
156
156
|
conversation_log={},
|
157
|
-
|
157
|
+
model_name: str = "bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
|
158
158
|
loaded_model: Union[Any, None] = None,
|
159
159
|
completion_func=None,
|
160
160
|
conversation_commands=[ConversationCommand.Default],
|
@@ -174,8 +174,8 @@ def converse_offline(
|
|
174
174
|
"""
|
175
175
|
# Initialize Variables
|
176
176
|
assert loaded_model is None or isinstance(loaded_model, Llama), "loaded_model must be of type Llama, if configured"
|
177
|
-
offline_chat_model = loaded_model or download_model(
|
178
|
-
tracer["chat_model"] =
|
177
|
+
offline_chat_model = loaded_model or download_model(model_name, max_tokens=max_prompt_size)
|
178
|
+
tracer["chat_model"] = model_name
|
179
179
|
current_date = datetime.now()
|
180
180
|
|
181
181
|
if agent and agent.personality:
|
@@ -228,18 +228,18 @@ def converse_offline(
|
|
228
228
|
system_prompt,
|
229
229
|
conversation_log,
|
230
230
|
context_message=context_message,
|
231
|
-
model_name=
|
231
|
+
model_name=model_name,
|
232
232
|
loaded_model=offline_chat_model,
|
233
233
|
max_prompt_size=max_prompt_size,
|
234
234
|
tokenizer_name=tokenizer_name,
|
235
|
-
model_type=
|
235
|
+
model_type=ChatModel.ModelType.OFFLINE,
|
236
236
|
query_files=query_files,
|
237
237
|
generated_files=generated_files,
|
238
238
|
generated_asset_results=generated_asset_results,
|
239
239
|
program_execution_context=additional_context,
|
240
240
|
)
|
241
241
|
|
242
|
-
logger.debug(f"Conversation Context for {
|
242
|
+
logger.debug(f"Conversation Context for {model_name}: {messages_to_print(messages)}")
|
243
243
|
|
244
244
|
g = ThreadedGenerator(references, online_results, completion_func=completion_func)
|
245
245
|
t = Thread(target=llm_thread, args=(g, messages, offline_chat_model, max_prompt_size, tracer))
|
@@ -273,7 +273,7 @@ def llm_thread(g, messages: List[ChatMessage], model: Any, max_prompt_size: int
|
|
273
273
|
def send_message_to_model_offline(
|
274
274
|
messages: List[ChatMessage],
|
275
275
|
loaded_model=None,
|
276
|
-
|
276
|
+
model_name="bartowski/Meta-Llama-3.1-8B-Instruct-GGUF",
|
277
277
|
temperature: float = 0.2,
|
278
278
|
streaming=False,
|
279
279
|
stop=[],
|
@@ -282,7 +282,7 @@ def send_message_to_model_offline(
|
|
282
282
|
tracer: dict = {},
|
283
283
|
):
|
284
284
|
assert loaded_model is None or isinstance(loaded_model, Llama), "loaded_model must be of type Llama, if configured"
|
285
|
-
offline_chat_model = loaded_model or download_model(
|
285
|
+
offline_chat_model = loaded_model or download_model(model_name, max_tokens=max_prompt_size)
|
286
286
|
messages_dict = [{"role": message.role, "content": message.content} for message in messages]
|
287
287
|
seed = int(os.getenv("KHOJ_LLM_SEED")) if os.getenv("KHOJ_LLM_SEED") else None
|
288
288
|
response = offline_chat_model.create_chat_completion(
|
@@ -301,7 +301,7 @@ def send_message_to_model_offline(
|
|
301
301
|
|
302
302
|
# Save conversation trace for non-streaming responses
|
303
303
|
# Streamed responses need to be saved by the calling function
|
304
|
-
tracer["chat_model"] =
|
304
|
+
tracer["chat_model"] = model_name
|
305
305
|
tracer["temperature"] = temperature
|
306
306
|
if is_promptrace_enabled():
|
307
307
|
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,
|
@@ -220,7 +220,7 @@ def converse_openai(
|
|
220
220
|
tokenizer_name=tokenizer_name,
|
221
221
|
query_images=query_images,
|
222
222
|
vision_enabled=vision_available,
|
223
|
-
model_type=
|
223
|
+
model_type=ChatModel.ModelType.OPENAI,
|
224
224
|
query_files=query_files,
|
225
225
|
generated_files=generated_files,
|
226
226
|
generated_asset_results=generated_asset_results,
|
@@ -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)
|
@@ -56,7 +62,7 @@ def completion_with_backoff(
|
|
56
62
|
|
57
63
|
# Update request parameters for compatability with o1 model series
|
58
64
|
# Refer: https://platform.openai.com/docs/guides/reasoning/beta-limitations
|
59
|
-
if
|
65
|
+
if model_name.startswith("o1"):
|
60
66
|
temperature = 1
|
61
67
|
model_kwargs.pop("stop", None)
|
62
68
|
model_kwargs.pop("response_format", None)
|
@@ -66,12 +72,12 @@ def completion_with_backoff(
|
|
66
72
|
|
67
73
|
chat: ChatCompletion | openai.Stream[ChatCompletionChunk] = client.chat.completions.create(
|
68
74
|
messages=formatted_messages, # type: ignore
|
69
|
-
model=
|
75
|
+
model=model_name, # type: ignore
|
70
76
|
stream=stream,
|
71
77
|
stream_options={"include_usage": True} if stream else {},
|
72
78
|
temperature=temperature,
|
73
79
|
timeout=20,
|
74
|
-
**
|
80
|
+
**model_kwargs,
|
75
81
|
)
|
76
82
|
|
77
83
|
aggregated_response = ""
|
@@ -91,10 +97,11 @@ def completion_with_backoff(
|
|
91
97
|
# Calculate cost of chat
|
92
98
|
input_tokens = chunk.usage.prompt_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
93
99
|
output_tokens = chunk.usage.completion_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
94
|
-
|
100
|
+
cost = chunk.usage.model_extra.get("estimated_cost") or 0 # Estimated costs returned by DeepInfra API
|
101
|
+
tracer["usage"] = get_chat_usage_metrics(model_name, input_tokens, output_tokens, tracer.get("usage"), cost)
|
95
102
|
|
96
103
|
# Save conversation trace
|
97
|
-
tracer["chat_model"] =
|
104
|
+
tracer["chat_model"] = model_name
|
98
105
|
tracer["temperature"] = temperature
|
99
106
|
if is_promptrace_enabled():
|
100
107
|
commit_conversation_trace(messages, aggregated_response, tracer)
|
@@ -139,11 +146,11 @@ def chat_completion_with_backoff(
|
|
139
146
|
def llm_thread(
|
140
147
|
g,
|
141
148
|
messages,
|
142
|
-
model_name,
|
149
|
+
model_name: str,
|
143
150
|
temperature,
|
144
151
|
openai_api_key=None,
|
145
152
|
api_base_url=None,
|
146
|
-
model_kwargs=
|
153
|
+
model_kwargs: dict = {},
|
147
154
|
tracer: dict = {},
|
148
155
|
):
|
149
156
|
try:
|
@@ -177,7 +184,7 @@ def llm_thread(
|
|
177
184
|
stream_options={"include_usage": True} if stream else {},
|
178
185
|
temperature=temperature,
|
179
186
|
timeout=20,
|
180
|
-
**
|
187
|
+
**model_kwargs,
|
181
188
|
)
|
182
189
|
|
183
190
|
aggregated_response = ""
|
@@ -202,7 +209,8 @@ def llm_thread(
|
|
202
209
|
# Calculate cost of chat
|
203
210
|
input_tokens = chunk.usage.prompt_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
204
211
|
output_tokens = chunk.usage.completion_tokens if hasattr(chunk, "usage") and chunk.usage else 0
|
205
|
-
|
212
|
+
cost = chunk.usage.model_extra.get("estimated_cost") or 0 # Estimated costs returned by DeepInfra API
|
213
|
+
tracer["usage"] = get_chat_usage_metrics(model_name, input_tokens, output_tokens, tracer.get("usage"), cost)
|
206
214
|
|
207
215
|
# Save conversation trace
|
208
216
|
tracer["chat_model"] = model_name
|
@@ -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
|
@@ -330,9 +330,9 @@ def construct_structured_message(
|
|
330
330
|
Format messages into appropriate multimedia format for supported chat model types
|
331
331
|
"""
|
332
332
|
if model_type in [
|
333
|
-
|
334
|
-
|
335
|
-
|
333
|
+
ChatModel.ModelType.OPENAI,
|
334
|
+
ChatModel.ModelType.GOOGLE,
|
335
|
+
ChatModel.ModelType.ANTHROPIC,
|
336
336
|
]:
|
337
337
|
if not attached_file_context and not (vision_enabled and images):
|
338
338
|
return message
|
@@ -102,8 +102,14 @@ async def search_online(
|
|
102
102
|
async for event in send_status_func(f"**Searching the Internet for**: {subqueries_str}"):
|
103
103
|
yield {ChatEvent.STATUS: event}
|
104
104
|
|
105
|
+
if SERPER_DEV_API_KEY:
|
106
|
+
search_func = search_with_serper
|
107
|
+
elif JINA_API_KEY:
|
108
|
+
search_func = search_with_jina
|
109
|
+
else:
|
110
|
+
search_func = search_with_searxng
|
111
|
+
|
105
112
|
with timer(f"Internet searches for {subqueries} took", logger):
|
106
|
-
search_func = search_with_google if SERPER_DEV_API_KEY else search_with_jina
|
107
113
|
search_tasks = [search_func(subquery, location) for subquery in subqueries]
|
108
114
|
search_results = await asyncio.gather(*search_tasks)
|
109
115
|
response_dict = {subquery: search_result for subquery, search_result in search_results}
|
@@ -148,7 +154,48 @@ async def search_online(
|
|
148
154
|
yield response_dict
|
149
155
|
|
150
156
|
|
151
|
-
async def
|
157
|
+
async def search_with_searxng(query: str, location: LocationData) -> Tuple[str, Dict[str, List[Dict]]]:
|
158
|
+
"""Search using local SearXNG instance."""
|
159
|
+
# Use environment variable or default to localhost
|
160
|
+
searxng_url = os.getenv("KHOJ_SEARXNG_URL", "http://localhost:42113")
|
161
|
+
search_url = f"{searxng_url}/search"
|
162
|
+
country_code = location.country_code.lower() if location and location.country_code else "us"
|
163
|
+
|
164
|
+
params = {"q": query, "format": "html", "language": "en", "country": country_code, "categories": "general"}
|
165
|
+
|
166
|
+
async with aiohttp.ClientSession() as session:
|
167
|
+
try:
|
168
|
+
async with session.get(search_url, params=params) as response:
|
169
|
+
if response.status != 200:
|
170
|
+
logger.error(f"SearXNG search failed to call {searxng_url}: {await response.text()}")
|
171
|
+
return query, {}
|
172
|
+
|
173
|
+
html_content = await response.text()
|
174
|
+
|
175
|
+
soup = BeautifulSoup(html_content, "html.parser")
|
176
|
+
organic_results = []
|
177
|
+
|
178
|
+
for result in soup.find_all("article", class_="result"):
|
179
|
+
title_elem = result.find("a", rel="noreferrer")
|
180
|
+
if title_elem:
|
181
|
+
title = title_elem.text.strip()
|
182
|
+
link = title_elem["href"]
|
183
|
+
|
184
|
+
description_elem = result.find("p", class_="content")
|
185
|
+
description = description_elem.text.strip() if description_elem else None
|
186
|
+
|
187
|
+
organic_results.append({"title": title, "link": link, "description": description})
|
188
|
+
|
189
|
+
extracted_search_result = {"organic": organic_results}
|
190
|
+
|
191
|
+
return query, extracted_search_result
|
192
|
+
|
193
|
+
except Exception as e:
|
194
|
+
logger.error(f"Error searching with SearXNG: {str(e)}")
|
195
|
+
return query, {}
|
196
|
+
|
197
|
+
|
198
|
+
async def search_with_serper(query: str, location: LocationData) -> Tuple[str, Dict[str, List[Dict]]]:
|
152
199
|
country_code = location.country_code.lower() if location and location.country_code else "us"
|
153
200
|
payload = json.dumps({"q": query, "gl": country_code})
|
154
201
|
headers = {"X-API-KEY": SERPER_DEV_API_KEY, "Content-Type": "application/json"}
|
khoj/routers/api.py
CHANGED
@@ -28,12 +28,7 @@ from khoj.database.adapters import (
|
|
28
28
|
get_default_search_model,
|
29
29
|
get_user_photo,
|
30
30
|
)
|
31
|
-
from khoj.database.models import
|
32
|
-
Agent,
|
33
|
-
ChatModelOptions,
|
34
|
-
KhojUser,
|
35
|
-
SpeechToTextModelOptions,
|
36
|
-
)
|
31
|
+
from khoj.database.models import Agent, ChatModel, KhojUser, SpeechToTextModelOptions
|
37
32
|
from khoj.processor.conversation import prompts
|
38
33
|
from khoj.processor.conversation.anthropic.anthropic_chat import (
|
39
34
|
extract_questions_anthropic,
|
@@ -404,15 +399,15 @@ async def extract_references_and_questions(
|
|
404
399
|
# Infer search queries from user message
|
405
400
|
with timer("Extracting search queries took", logger):
|
406
401
|
# If we've reached here, either the user has enabled offline chat or the openai model is enabled.
|
407
|
-
|
408
|
-
vision_enabled =
|
402
|
+
chat_model = await ConversationAdapters.aget_default_chat_model(user)
|
403
|
+
vision_enabled = chat_model.vision_enabled
|
409
404
|
|
410
|
-
if
|
405
|
+
if chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
411
406
|
using_offline_chat = True
|
412
|
-
|
413
|
-
max_tokens =
|
407
|
+
chat_model_name = chat_model.name
|
408
|
+
max_tokens = chat_model.max_prompt_size
|
414
409
|
if state.offline_chat_processor_config is None:
|
415
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
410
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
416
411
|
|
417
412
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
418
413
|
|
@@ -424,18 +419,18 @@ async def extract_references_and_questions(
|
|
424
419
|
should_extract_questions=True,
|
425
420
|
location_data=location_data,
|
426
421
|
user=user,
|
427
|
-
max_prompt_size=
|
422
|
+
max_prompt_size=chat_model.max_prompt_size,
|
428
423
|
personality_context=personality_context,
|
429
424
|
query_files=query_files,
|
430
425
|
tracer=tracer,
|
431
426
|
)
|
432
|
-
elif
|
433
|
-
api_key =
|
434
|
-
base_url =
|
435
|
-
|
427
|
+
elif chat_model.model_type == ChatModel.ModelType.OPENAI:
|
428
|
+
api_key = chat_model.ai_model_api.api_key
|
429
|
+
base_url = chat_model.ai_model_api.api_base_url
|
430
|
+
chat_model_name = chat_model.name
|
436
431
|
inferred_queries = extract_questions(
|
437
432
|
defiltered_query,
|
438
|
-
model=
|
433
|
+
model=chat_model_name,
|
439
434
|
api_key=api_key,
|
440
435
|
api_base_url=base_url,
|
441
436
|
conversation_log=meta_log,
|
@@ -447,13 +442,13 @@ async def extract_references_and_questions(
|
|
447
442
|
query_files=query_files,
|
448
443
|
tracer=tracer,
|
449
444
|
)
|
450
|
-
elif
|
451
|
-
api_key =
|
452
|
-
|
445
|
+
elif chat_model.model_type == ChatModel.ModelType.ANTHROPIC:
|
446
|
+
api_key = chat_model.ai_model_api.api_key
|
447
|
+
chat_model_name = chat_model.name
|
453
448
|
inferred_queries = extract_questions_anthropic(
|
454
449
|
defiltered_query,
|
455
450
|
query_images=query_images,
|
456
|
-
model=
|
451
|
+
model=chat_model_name,
|
457
452
|
api_key=api_key,
|
458
453
|
conversation_log=meta_log,
|
459
454
|
location_data=location_data,
|
@@ -463,17 +458,17 @@ async def extract_references_and_questions(
|
|
463
458
|
query_files=query_files,
|
464
459
|
tracer=tracer,
|
465
460
|
)
|
466
|
-
elif
|
467
|
-
api_key =
|
468
|
-
|
461
|
+
elif chat_model.model_type == ChatModel.ModelType.GOOGLE:
|
462
|
+
api_key = chat_model.ai_model_api.api_key
|
463
|
+
chat_model_name = chat_model.name
|
469
464
|
inferred_queries = extract_questions_gemini(
|
470
465
|
defiltered_query,
|
471
466
|
query_images=query_images,
|
472
|
-
model=
|
467
|
+
model=chat_model_name,
|
473
468
|
api_key=api_key,
|
474
469
|
conversation_log=meta_log,
|
475
470
|
location_data=location_data,
|
476
|
-
max_tokens=
|
471
|
+
max_tokens=chat_model.max_prompt_size,
|
477
472
|
user=user,
|
478
473
|
vision_enabled=vision_enabled,
|
479
474
|
personality_context=personality_context,
|
khoj/routers/api_agents.py
CHANGED
@@ -62,7 +62,7 @@ async def all_agents(
|
|
62
62
|
"color": agent.style_color,
|
63
63
|
"icon": agent.style_icon,
|
64
64
|
"privacy_level": agent.privacy_level,
|
65
|
-
"chat_model": agent.chat_model.
|
65
|
+
"chat_model": agent.chat_model.name,
|
66
66
|
"files": file_names,
|
67
67
|
"input_tools": agent.input_tools,
|
68
68
|
"output_modes": agent.output_modes,
|
@@ -150,7 +150,7 @@ async def get_agent(
|
|
150
150
|
"color": agent.style_color,
|
151
151
|
"icon": agent.style_icon,
|
152
152
|
"privacy_level": agent.privacy_level,
|
153
|
-
"chat_model": agent.chat_model.
|
153
|
+
"chat_model": agent.chat_model.name,
|
154
154
|
"files": file_names,
|
155
155
|
"input_tools": agent.input_tools,
|
156
156
|
"output_modes": agent.output_modes,
|
@@ -225,7 +225,7 @@ async def create_agent(
|
|
225
225
|
"color": agent.style_color,
|
226
226
|
"icon": agent.style_icon,
|
227
227
|
"privacy_level": agent.privacy_level,
|
228
|
-
"chat_model": agent.chat_model.
|
228
|
+
"chat_model": agent.chat_model.name,
|
229
229
|
"files": body.files,
|
230
230
|
"input_tools": agent.input_tools,
|
231
231
|
"output_modes": agent.output_modes,
|
@@ -286,7 +286,7 @@ async def update_agent(
|
|
286
286
|
"color": agent.style_color,
|
287
287
|
"icon": agent.style_icon,
|
288
288
|
"privacy_level": agent.privacy_level,
|
289
|
-
"chat_model": agent.chat_model.
|
289
|
+
"chat_model": agent.chat_model.name,
|
290
290
|
"files": body.files,
|
291
291
|
"input_tools": agent.input_tools,
|
292
292
|
"output_modes": agent.output_modes,
|
khoj/routers/api_chat.py
CHANGED
@@ -58,7 +58,7 @@ from khoj.routers.helpers import (
|
|
58
58
|
is_ready_to_chat,
|
59
59
|
read_chat_stream,
|
60
60
|
update_telemetry_state,
|
61
|
-
|
61
|
+
validate_chat_model,
|
62
62
|
)
|
63
63
|
from khoj.routers.research import (
|
64
64
|
InformationCollectionIteration,
|
@@ -205,7 +205,7 @@ def chat_history(
|
|
205
205
|
n: Optional[int] = None,
|
206
206
|
):
|
207
207
|
user = request.user.object
|
208
|
-
|
208
|
+
validate_chat_model(user)
|
209
209
|
|
210
210
|
# Load Conversation History
|
211
211
|
conversation = ConversationAdapters.get_conversation_by_user(
|
@@ -724,7 +724,16 @@ async def chat(
|
|
724
724
|
yield result
|
725
725
|
return
|
726
726
|
|
727
|
-
|
727
|
+
# Automated tasks are handled before to allow mixing them with other conversation commands
|
728
|
+
cmds_to_rate_limit = []
|
729
|
+
is_automated_task = False
|
730
|
+
if q.startswith("/automated_task"):
|
731
|
+
is_automated_task = True
|
732
|
+
q = q.replace("/automated_task", "").lstrip()
|
733
|
+
cmds_to_rate_limit += [ConversationCommand.AutomatedTask]
|
734
|
+
|
735
|
+
# Extract conversation command from query
|
736
|
+
conversation_commands = [get_conversation_command(query=q)]
|
728
737
|
|
729
738
|
conversation = await ConversationAdapters.aget_conversation_by_user(
|
730
739
|
user,
|
@@ -757,11 +766,8 @@ async def chat(
|
|
757
766
|
location = None
|
758
767
|
if city or region or country or country_code:
|
759
768
|
location = LocationData(city=city, region=region, country=country, country_code=country_code)
|
760
|
-
|
761
769
|
user_message_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
762
|
-
|
763
770
|
meta_log = conversation.conversation_log
|
764
|
-
is_automated_task = conversation_commands == [ConversationCommand.AutomatedTask]
|
765
771
|
|
766
772
|
researched_results = ""
|
767
773
|
online_results: Dict = dict()
|
@@ -778,7 +784,7 @@ async def chat(
|
|
778
784
|
generated_excalidraw_diagram: str = None
|
779
785
|
program_execution_context: List[str] = []
|
780
786
|
|
781
|
-
if conversation_commands == [ConversationCommand.Default]
|
787
|
+
if conversation_commands == [ConversationCommand.Default]:
|
782
788
|
chosen_io = await aget_data_sources_and_output_format(
|
783
789
|
q,
|
784
790
|
meta_log,
|
@@ -799,7 +805,8 @@ async def chat(
|
|
799
805
|
async for result in send_event(ChatEvent.STATUS, f"**Selected Tools:** {conversation_commands_str}"):
|
800
806
|
yield result
|
801
807
|
|
802
|
-
|
808
|
+
cmds_to_rate_limit += conversation_commands
|
809
|
+
for cmd in cmds_to_rate_limit:
|
803
810
|
try:
|
804
811
|
await conversation_command_rate_limiter.update_and_check_if_valid(request, cmd)
|
805
812
|
q = q.replace(f"/{cmd.value}", "").strip()
|
@@ -898,10 +905,10 @@ async def chat(
|
|
898
905
|
custom_filters = []
|
899
906
|
if conversation_commands == [ConversationCommand.Help]:
|
900
907
|
if not q:
|
901
|
-
|
902
|
-
if
|
903
|
-
|
904
|
-
model_type =
|
908
|
+
chat_model = await ConversationAdapters.aget_user_chat_model(user)
|
909
|
+
if chat_model == None:
|
910
|
+
chat_model = await ConversationAdapters.aget_default_chat_model(user)
|
911
|
+
model_type = chat_model.model_type
|
905
912
|
formatted_help = help_message.format(model=model_type, version=state.khoj_version, device=get_device())
|
906
913
|
async for result in send_llm_response(formatted_help, tracer.get("usage")):
|
907
914
|
yield result
|
khoj/routers/api_model.py
CHANGED
@@ -24,7 +24,7 @@ def get_chat_model_options(
|
|
24
24
|
|
25
25
|
all_conversation_options = list()
|
26
26
|
for conversation_option in conversation_options:
|
27
|
-
all_conversation_options.append({"chat_model": conversation_option.
|
27
|
+
all_conversation_options.append({"chat_model": conversation_option.name, "id": conversation_option.id})
|
28
28
|
|
29
29
|
return Response(content=json.dumps(all_conversation_options), media_type="application/json", status_code=200)
|
30
30
|
|
@@ -37,12 +37,12 @@ def get_user_chat_model(
|
|
37
37
|
):
|
38
38
|
user = request.user.object
|
39
39
|
|
40
|
-
chat_model = ConversationAdapters.
|
40
|
+
chat_model = ConversationAdapters.get_chat_model(user)
|
41
41
|
|
42
42
|
if chat_model is None:
|
43
|
-
chat_model = ConversationAdapters.
|
43
|
+
chat_model = ConversationAdapters.get_default_chat_model(user)
|
44
44
|
|
45
|
-
return Response(status_code=200, content=json.dumps({"id": chat_model.id, "chat_model": chat_model.
|
45
|
+
return Response(status_code=200, content=json.dumps({"id": chat_model.id, "chat_model": chat_model.name}))
|
46
46
|
|
47
47
|
|
48
48
|
@api_model.post("/chat", status_code=200)
|