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
khoj/routers/helpers.py
CHANGED
@@ -49,6 +49,7 @@ from khoj.database.adapters import (
|
|
49
49
|
ais_user_subscribed,
|
50
50
|
create_khoj_token,
|
51
51
|
get_khoj_tokens,
|
52
|
+
get_user_by_email,
|
52
53
|
get_user_name,
|
53
54
|
get_user_notion_config,
|
54
55
|
get_user_subscription_state,
|
@@ -56,7 +57,7 @@ from khoj.database.adapters import (
|
|
56
57
|
)
|
57
58
|
from khoj.database.models import (
|
58
59
|
Agent,
|
59
|
-
|
60
|
+
ChatModel,
|
60
61
|
ClientApplication,
|
61
62
|
Conversation,
|
62
63
|
GithubConfig,
|
@@ -133,40 +134,40 @@ def is_query_empty(query: str) -> bool:
|
|
133
134
|
return is_none_or_empty(query.strip())
|
134
135
|
|
135
136
|
|
136
|
-
def
|
137
|
-
|
137
|
+
def validate_chat_model(user: KhojUser):
|
138
|
+
default_chat_model = ConversationAdapters.get_default_chat_model(user)
|
138
139
|
|
139
|
-
if
|
140
|
+
if default_chat_model is None:
|
140
141
|
raise HTTPException(status_code=500, detail="Contact the server administrator to add a chat model.")
|
141
142
|
|
142
|
-
if
|
143
|
+
if default_chat_model.model_type == "openai" and not default_chat_model.ai_model_api:
|
143
144
|
raise HTTPException(status_code=500, detail="Contact the server administrator to add a chat model.")
|
144
145
|
|
145
146
|
|
146
147
|
async def is_ready_to_chat(user: KhojUser):
|
147
|
-
|
148
|
-
if
|
149
|
-
|
148
|
+
user_chat_model = await ConversationAdapters.aget_user_chat_model(user)
|
149
|
+
if user_chat_model == None:
|
150
|
+
user_chat_model = await ConversationAdapters.aget_default_chat_model(user)
|
150
151
|
|
151
|
-
if
|
152
|
-
|
153
|
-
max_tokens =
|
152
|
+
if user_chat_model and user_chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
153
|
+
chat_model_name = user_chat_model.name
|
154
|
+
max_tokens = user_chat_model.max_prompt_size
|
154
155
|
if state.offline_chat_processor_config is None:
|
155
156
|
logger.info("Loading Offline Chat Model...")
|
156
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
157
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
157
158
|
return True
|
158
159
|
|
159
160
|
if (
|
160
|
-
|
161
|
+
user_chat_model
|
161
162
|
and (
|
162
|
-
|
163
|
+
user_chat_model.model_type
|
163
164
|
in [
|
164
|
-
|
165
|
-
|
166
|
-
|
165
|
+
ChatModel.ModelType.OPENAI,
|
166
|
+
ChatModel.ModelType.ANTHROPIC,
|
167
|
+
ChatModel.ModelType.GOOGLE,
|
167
168
|
]
|
168
169
|
)
|
169
|
-
and
|
170
|
+
and user_chat_model.ai_model_api
|
170
171
|
):
|
171
172
|
return True
|
172
173
|
|
@@ -230,7 +231,7 @@ def get_next_url(request: Request) -> str:
|
|
230
231
|
return urljoin(str(request.base_url).rstrip("/"), next_path)
|
231
232
|
|
232
233
|
|
233
|
-
def get_conversation_command(query: str
|
234
|
+
def get_conversation_command(query: str) -> ConversationCommand:
|
234
235
|
if query.startswith("/notes"):
|
235
236
|
return ConversationCommand.Notes
|
236
237
|
elif query.startswith("/help"):
|
@@ -253,9 +254,6 @@ def get_conversation_command(query: str, any_references: bool = False) -> Conver
|
|
253
254
|
return ConversationCommand.Code
|
254
255
|
elif query.startswith("/research"):
|
255
256
|
return ConversationCommand.Research
|
256
|
-
# If no relevant notes found for the given query
|
257
|
-
elif not any_references:
|
258
|
-
return ConversationCommand.General
|
259
257
|
else:
|
260
258
|
return ConversationCommand.Default
|
261
259
|
|
@@ -407,42 +405,39 @@ async def aget_data_sources_and_output_format(
|
|
407
405
|
response = clean_json(response)
|
408
406
|
response = json.loads(response)
|
409
407
|
|
410
|
-
|
411
|
-
|
408
|
+
chosen_sources = [s.strip() for s in response.get("source", []) if s.strip()]
|
409
|
+
chosen_output = response.get("output", "text").strip() # Default to text output
|
412
410
|
|
413
|
-
if
|
411
|
+
if is_none_or_empty(chosen_sources) or not isinstance(chosen_sources, list):
|
414
412
|
raise ValueError(
|
415
|
-
f"Invalid response for determining relevant tools: {
|
413
|
+
f"Invalid response for determining relevant tools: {chosen_sources}. Raw Response: {response}"
|
416
414
|
)
|
417
415
|
|
418
|
-
|
419
|
-
for
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
if is_none_or_empty(result):
|
416
|
+
output_mode = ConversationCommand.Text
|
417
|
+
# Verify selected output mode is enabled for the agent, as the LLM can sometimes get confused by the tool options.
|
418
|
+
if chosen_output in output_options.keys() and (len(agent_outputs) == 0 or chosen_output in agent_outputs):
|
419
|
+
# Ensure that the chosen output mode exists as a valid ConversationCommand
|
420
|
+
output_mode = ConversationCommand(chosen_output)
|
421
|
+
|
422
|
+
data_sources = []
|
423
|
+
# Verify selected data sources are enabled for the agent, as the LLM can sometimes get confused by the tool options.
|
424
|
+
for chosen_source in chosen_sources:
|
425
|
+
# Ensure that the chosen data source exists as a valid ConversationCommand
|
426
|
+
if chosen_source in source_options.keys() and (len(agent_sources) == 0 or chosen_source in agent_sources):
|
427
|
+
data_sources.append(ConversationCommand(chosen_source))
|
428
|
+
|
429
|
+
# Fallback to default sources if the inferred data sources are unset or invalid
|
430
|
+
if is_none_or_empty(data_sources):
|
435
431
|
if len(agent_sources) == 0:
|
436
|
-
|
432
|
+
data_sources = [ConversationCommand.Default]
|
437
433
|
else:
|
438
|
-
|
434
|
+
data_sources = [ConversationCommand.General]
|
439
435
|
except Exception as e:
|
440
436
|
logger.error(f"Invalid response for determining relevant tools: {response}. Error: {e}", exc_info=True)
|
441
|
-
|
442
|
-
|
443
|
-
result = {"sources": sources, "output": output}
|
437
|
+
data_sources = agent_sources if len(agent_sources) > 0 else [ConversationCommand.Default]
|
438
|
+
output_mode = agent_outputs[0] if len(agent_outputs) > 0 else ConversationCommand.Text
|
444
439
|
|
445
|
-
return
|
440
|
+
return {"sources": data_sources, "output": output_mode}
|
446
441
|
|
447
442
|
|
448
443
|
async def infer_webpage_urls(
|
@@ -942,120 +937,124 @@ async def send_message_to_model_wrapper(
|
|
942
937
|
query_files: str = None,
|
943
938
|
tracer: dict = {},
|
944
939
|
):
|
945
|
-
|
946
|
-
vision_available =
|
940
|
+
chat_model: ChatModel = await ConversationAdapters.aget_default_chat_model(user)
|
941
|
+
vision_available = chat_model.vision_enabled
|
947
942
|
if not vision_available and query_images:
|
948
|
-
logger.warning(f"Vision is not enabled for default model: {
|
943
|
+
logger.warning(f"Vision is not enabled for default model: {chat_model.name}.")
|
949
944
|
vision_enabled_config = await ConversationAdapters.aget_vision_enabled_config()
|
950
945
|
if vision_enabled_config:
|
951
|
-
|
946
|
+
chat_model = vision_enabled_config
|
952
947
|
vision_available = True
|
953
948
|
if vision_available and query_images:
|
954
|
-
logger.info(f"Using {
|
949
|
+
logger.info(f"Using {chat_model.name} model to understand {len(query_images)} images.")
|
955
950
|
|
956
951
|
subscribed = await ais_user_subscribed(user)
|
957
|
-
|
952
|
+
chat_model_name = chat_model.name
|
958
953
|
max_tokens = (
|
959
|
-
|
960
|
-
if subscribed and
|
961
|
-
else
|
954
|
+
chat_model.subscribed_max_prompt_size
|
955
|
+
if subscribed and chat_model.subscribed_max_prompt_size
|
956
|
+
else chat_model.max_prompt_size
|
962
957
|
)
|
963
|
-
tokenizer =
|
964
|
-
model_type =
|
965
|
-
vision_available =
|
958
|
+
tokenizer = chat_model.tokenizer
|
959
|
+
model_type = chat_model.model_type
|
960
|
+
vision_available = chat_model.vision_enabled
|
966
961
|
|
967
|
-
if model_type ==
|
962
|
+
if model_type == ChatModel.ModelType.OFFLINE:
|
968
963
|
if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
|
969
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
964
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
970
965
|
|
971
966
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
972
967
|
truncated_messages = generate_chatml_messages_with_context(
|
973
968
|
user_message=query,
|
974
969
|
context_message=context,
|
975
970
|
system_message=system_message,
|
976
|
-
model_name=
|
971
|
+
model_name=chat_model_name,
|
977
972
|
loaded_model=loaded_model,
|
978
973
|
tokenizer_name=tokenizer,
|
979
974
|
max_prompt_size=max_tokens,
|
980
975
|
vision_enabled=vision_available,
|
981
|
-
model_type=
|
976
|
+
model_type=chat_model.model_type,
|
982
977
|
query_files=query_files,
|
983
978
|
)
|
984
979
|
|
985
980
|
return send_message_to_model_offline(
|
986
981
|
messages=truncated_messages,
|
987
982
|
loaded_model=loaded_model,
|
988
|
-
|
983
|
+
model_name=chat_model_name,
|
989
984
|
max_prompt_size=max_tokens,
|
990
985
|
streaming=False,
|
991
986
|
response_type=response_type,
|
992
987
|
tracer=tracer,
|
993
988
|
)
|
994
989
|
|
995
|
-
elif model_type ==
|
996
|
-
openai_chat_config =
|
990
|
+
elif model_type == ChatModel.ModelType.OPENAI:
|
991
|
+
openai_chat_config = chat_model.ai_model_api
|
997
992
|
api_key = openai_chat_config.api_key
|
998
993
|
api_base_url = openai_chat_config.api_base_url
|
999
994
|
truncated_messages = generate_chatml_messages_with_context(
|
1000
995
|
user_message=query,
|
1001
996
|
context_message=context,
|
1002
997
|
system_message=system_message,
|
1003
|
-
model_name=
|
998
|
+
model_name=chat_model_name,
|
1004
999
|
max_prompt_size=max_tokens,
|
1005
1000
|
tokenizer_name=tokenizer,
|
1006
1001
|
vision_enabled=vision_available,
|
1007
1002
|
query_images=query_images,
|
1008
|
-
model_type=
|
1003
|
+
model_type=chat_model.model_type,
|
1009
1004
|
query_files=query_files,
|
1010
1005
|
)
|
1011
1006
|
|
1012
1007
|
return send_message_to_model(
|
1013
1008
|
messages=truncated_messages,
|
1014
1009
|
api_key=api_key,
|
1015
|
-
model=
|
1010
|
+
model=chat_model_name,
|
1016
1011
|
response_type=response_type,
|
1017
1012
|
api_base_url=api_base_url,
|
1018
1013
|
tracer=tracer,
|
1019
1014
|
)
|
1020
|
-
elif model_type ==
|
1021
|
-
api_key =
|
1015
|
+
elif model_type == ChatModel.ModelType.ANTHROPIC:
|
1016
|
+
api_key = chat_model.ai_model_api.api_key
|
1022
1017
|
truncated_messages = generate_chatml_messages_with_context(
|
1023
1018
|
user_message=query,
|
1024
1019
|
context_message=context,
|
1025
1020
|
system_message=system_message,
|
1026
|
-
model_name=
|
1021
|
+
model_name=chat_model_name,
|
1027
1022
|
max_prompt_size=max_tokens,
|
1028
1023
|
tokenizer_name=tokenizer,
|
1029
1024
|
vision_enabled=vision_available,
|
1030
1025
|
query_images=query_images,
|
1031
|
-
model_type=
|
1026
|
+
model_type=chat_model.model_type,
|
1032
1027
|
query_files=query_files,
|
1033
1028
|
)
|
1034
1029
|
|
1035
1030
|
return anthropic_send_message_to_model(
|
1036
1031
|
messages=truncated_messages,
|
1037
1032
|
api_key=api_key,
|
1038
|
-
model=
|
1033
|
+
model=chat_model_name,
|
1039
1034
|
response_type=response_type,
|
1040
1035
|
tracer=tracer,
|
1041
1036
|
)
|
1042
|
-
elif model_type ==
|
1043
|
-
api_key =
|
1037
|
+
elif model_type == ChatModel.ModelType.GOOGLE:
|
1038
|
+
api_key = chat_model.ai_model_api.api_key
|
1044
1039
|
truncated_messages = generate_chatml_messages_with_context(
|
1045
1040
|
user_message=query,
|
1046
1041
|
context_message=context,
|
1047
1042
|
system_message=system_message,
|
1048
|
-
model_name=
|
1043
|
+
model_name=chat_model_name,
|
1049
1044
|
max_prompt_size=max_tokens,
|
1050
1045
|
tokenizer_name=tokenizer,
|
1051
1046
|
vision_enabled=vision_available,
|
1052
1047
|
query_images=query_images,
|
1053
|
-
model_type=
|
1048
|
+
model_type=chat_model.model_type,
|
1054
1049
|
query_files=query_files,
|
1055
1050
|
)
|
1056
1051
|
|
1057
1052
|
return gemini_send_message_to_model(
|
1058
|
-
messages=truncated_messages,
|
1053
|
+
messages=truncated_messages,
|
1054
|
+
api_key=api_key,
|
1055
|
+
model=chat_model_name,
|
1056
|
+
response_type=response_type,
|
1057
|
+
tracer=tracer,
|
1059
1058
|
)
|
1060
1059
|
else:
|
1061
1060
|
raise HTTPException(status_code=500, detail="Invalid conversation config")
|
@@ -1069,99 +1068,99 @@ def send_message_to_model_wrapper_sync(
|
|
1069
1068
|
query_files: str = "",
|
1070
1069
|
tracer: dict = {},
|
1071
1070
|
):
|
1072
|
-
|
1071
|
+
chat_model: ChatModel = ConversationAdapters.get_default_chat_model(user)
|
1073
1072
|
|
1074
|
-
if
|
1073
|
+
if chat_model is None:
|
1075
1074
|
raise HTTPException(status_code=500, detail="Contact the server administrator to set a default chat model.")
|
1076
1075
|
|
1077
|
-
|
1078
|
-
max_tokens =
|
1079
|
-
vision_available =
|
1076
|
+
chat_model_name = chat_model.name
|
1077
|
+
max_tokens = chat_model.max_prompt_size
|
1078
|
+
vision_available = chat_model.vision_enabled
|
1080
1079
|
|
1081
|
-
if
|
1080
|
+
if chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
1082
1081
|
if state.offline_chat_processor_config is None or state.offline_chat_processor_config.loaded_model is None:
|
1083
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
1082
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
1084
1083
|
|
1085
1084
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
1086
1085
|
truncated_messages = generate_chatml_messages_with_context(
|
1087
1086
|
user_message=message,
|
1088
1087
|
system_message=system_message,
|
1089
|
-
model_name=
|
1088
|
+
model_name=chat_model_name,
|
1090
1089
|
loaded_model=loaded_model,
|
1091
1090
|
max_prompt_size=max_tokens,
|
1092
1091
|
vision_enabled=vision_available,
|
1093
|
-
model_type=
|
1092
|
+
model_type=chat_model.model_type,
|
1094
1093
|
query_files=query_files,
|
1095
1094
|
)
|
1096
1095
|
|
1097
1096
|
return send_message_to_model_offline(
|
1098
1097
|
messages=truncated_messages,
|
1099
1098
|
loaded_model=loaded_model,
|
1100
|
-
|
1099
|
+
model_name=chat_model_name,
|
1101
1100
|
max_prompt_size=max_tokens,
|
1102
1101
|
streaming=False,
|
1103
1102
|
response_type=response_type,
|
1104
1103
|
tracer=tracer,
|
1105
1104
|
)
|
1106
1105
|
|
1107
|
-
elif
|
1108
|
-
api_key =
|
1106
|
+
elif chat_model.model_type == ChatModel.ModelType.OPENAI:
|
1107
|
+
api_key = chat_model.ai_model_api.api_key
|
1109
1108
|
truncated_messages = generate_chatml_messages_with_context(
|
1110
1109
|
user_message=message,
|
1111
1110
|
system_message=system_message,
|
1112
|
-
model_name=
|
1111
|
+
model_name=chat_model_name,
|
1113
1112
|
max_prompt_size=max_tokens,
|
1114
1113
|
vision_enabled=vision_available,
|
1115
|
-
model_type=
|
1114
|
+
model_type=chat_model.model_type,
|
1116
1115
|
query_files=query_files,
|
1117
1116
|
)
|
1118
1117
|
|
1119
1118
|
openai_response = send_message_to_model(
|
1120
1119
|
messages=truncated_messages,
|
1121
1120
|
api_key=api_key,
|
1122
|
-
model=
|
1121
|
+
model=chat_model_name,
|
1123
1122
|
response_type=response_type,
|
1124
1123
|
tracer=tracer,
|
1125
1124
|
)
|
1126
1125
|
|
1127
1126
|
return openai_response
|
1128
1127
|
|
1129
|
-
elif
|
1130
|
-
api_key =
|
1128
|
+
elif chat_model.model_type == ChatModel.ModelType.ANTHROPIC:
|
1129
|
+
api_key = chat_model.ai_model_api.api_key
|
1131
1130
|
truncated_messages = generate_chatml_messages_with_context(
|
1132
1131
|
user_message=message,
|
1133
1132
|
system_message=system_message,
|
1134
|
-
model_name=
|
1133
|
+
model_name=chat_model_name,
|
1135
1134
|
max_prompt_size=max_tokens,
|
1136
1135
|
vision_enabled=vision_available,
|
1137
|
-
model_type=
|
1136
|
+
model_type=chat_model.model_type,
|
1138
1137
|
query_files=query_files,
|
1139
1138
|
)
|
1140
1139
|
|
1141
1140
|
return anthropic_send_message_to_model(
|
1142
1141
|
messages=truncated_messages,
|
1143
1142
|
api_key=api_key,
|
1144
|
-
model=
|
1143
|
+
model=chat_model_name,
|
1145
1144
|
response_type=response_type,
|
1146
1145
|
tracer=tracer,
|
1147
1146
|
)
|
1148
1147
|
|
1149
|
-
elif
|
1150
|
-
api_key =
|
1148
|
+
elif chat_model.model_type == ChatModel.ModelType.GOOGLE:
|
1149
|
+
api_key = chat_model.ai_model_api.api_key
|
1151
1150
|
truncated_messages = generate_chatml_messages_with_context(
|
1152
1151
|
user_message=message,
|
1153
1152
|
system_message=system_message,
|
1154
|
-
model_name=
|
1153
|
+
model_name=chat_model_name,
|
1155
1154
|
max_prompt_size=max_tokens,
|
1156
1155
|
vision_enabled=vision_available,
|
1157
|
-
model_type=
|
1156
|
+
model_type=chat_model.model_type,
|
1158
1157
|
query_files=query_files,
|
1159
1158
|
)
|
1160
1159
|
|
1161
1160
|
return gemini_send_message_to_model(
|
1162
1161
|
messages=truncated_messages,
|
1163
1162
|
api_key=api_key,
|
1164
|
-
model=
|
1163
|
+
model=chat_model_name,
|
1165
1164
|
response_type=response_type,
|
1166
1165
|
tracer=tracer,
|
1167
1166
|
)
|
@@ -1229,15 +1228,15 @@ def generate_chat_response(
|
|
1229
1228
|
online_results = {}
|
1230
1229
|
code_results = {}
|
1231
1230
|
|
1232
|
-
|
1233
|
-
vision_available =
|
1231
|
+
chat_model = ConversationAdapters.get_valid_chat_model(user, conversation)
|
1232
|
+
vision_available = chat_model.vision_enabled
|
1234
1233
|
if not vision_available and query_images:
|
1235
1234
|
vision_enabled_config = ConversationAdapters.get_vision_enabled_config()
|
1236
1235
|
if vision_enabled_config:
|
1237
|
-
|
1236
|
+
chat_model = vision_enabled_config
|
1238
1237
|
vision_available = True
|
1239
1238
|
|
1240
|
-
if
|
1239
|
+
if chat_model.model_type == "offline":
|
1241
1240
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
1242
1241
|
chat_response = converse_offline(
|
1243
1242
|
user_query=query_to_run,
|
@@ -1247,9 +1246,9 @@ def generate_chat_response(
|
|
1247
1246
|
conversation_log=meta_log,
|
1248
1247
|
completion_func=partial_completion,
|
1249
1248
|
conversation_commands=conversation_commands,
|
1250
|
-
|
1251
|
-
max_prompt_size=
|
1252
|
-
tokenizer_name=
|
1249
|
+
model_name=chat_model.name,
|
1250
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1251
|
+
tokenizer_name=chat_model.tokenizer,
|
1253
1252
|
location_data=location_data,
|
1254
1253
|
user_name=user_name,
|
1255
1254
|
agent=agent,
|
@@ -1259,10 +1258,10 @@ def generate_chat_response(
|
|
1259
1258
|
tracer=tracer,
|
1260
1259
|
)
|
1261
1260
|
|
1262
|
-
elif
|
1263
|
-
openai_chat_config =
|
1261
|
+
elif chat_model.model_type == ChatModel.ModelType.OPENAI:
|
1262
|
+
openai_chat_config = chat_model.ai_model_api
|
1264
1263
|
api_key = openai_chat_config.api_key
|
1265
|
-
|
1264
|
+
chat_model_name = chat_model.name
|
1266
1265
|
chat_response = converse_openai(
|
1267
1266
|
compiled_references,
|
1268
1267
|
query_to_run,
|
@@ -1270,13 +1269,13 @@ def generate_chat_response(
|
|
1270
1269
|
online_results=online_results,
|
1271
1270
|
code_results=code_results,
|
1272
1271
|
conversation_log=meta_log,
|
1273
|
-
model=
|
1272
|
+
model=chat_model_name,
|
1274
1273
|
api_key=api_key,
|
1275
1274
|
api_base_url=openai_chat_config.api_base_url,
|
1276
1275
|
completion_func=partial_completion,
|
1277
1276
|
conversation_commands=conversation_commands,
|
1278
|
-
max_prompt_size=
|
1279
|
-
tokenizer_name=
|
1277
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1278
|
+
tokenizer_name=chat_model.tokenizer,
|
1280
1279
|
location_data=location_data,
|
1281
1280
|
user_name=user_name,
|
1282
1281
|
agent=agent,
|
@@ -1288,8 +1287,8 @@ def generate_chat_response(
|
|
1288
1287
|
tracer=tracer,
|
1289
1288
|
)
|
1290
1289
|
|
1291
|
-
elif
|
1292
|
-
api_key =
|
1290
|
+
elif chat_model.model_type == ChatModel.ModelType.ANTHROPIC:
|
1291
|
+
api_key = chat_model.ai_model_api.api_key
|
1293
1292
|
chat_response = converse_anthropic(
|
1294
1293
|
compiled_references,
|
1295
1294
|
query_to_run,
|
@@ -1297,12 +1296,12 @@ def generate_chat_response(
|
|
1297
1296
|
online_results=online_results,
|
1298
1297
|
code_results=code_results,
|
1299
1298
|
conversation_log=meta_log,
|
1300
|
-
model=
|
1299
|
+
model=chat_model.name,
|
1301
1300
|
api_key=api_key,
|
1302
1301
|
completion_func=partial_completion,
|
1303
1302
|
conversation_commands=conversation_commands,
|
1304
|
-
max_prompt_size=
|
1305
|
-
tokenizer_name=
|
1303
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1304
|
+
tokenizer_name=chat_model.tokenizer,
|
1306
1305
|
location_data=location_data,
|
1307
1306
|
user_name=user_name,
|
1308
1307
|
agent=agent,
|
@@ -1313,20 +1312,20 @@ def generate_chat_response(
|
|
1313
1312
|
program_execution_context=program_execution_context,
|
1314
1313
|
tracer=tracer,
|
1315
1314
|
)
|
1316
|
-
elif
|
1317
|
-
api_key =
|
1315
|
+
elif chat_model.model_type == ChatModel.ModelType.GOOGLE:
|
1316
|
+
api_key = chat_model.ai_model_api.api_key
|
1318
1317
|
chat_response = converse_gemini(
|
1319
1318
|
compiled_references,
|
1320
1319
|
query_to_run,
|
1321
1320
|
online_results,
|
1322
1321
|
code_results,
|
1323
1322
|
meta_log,
|
1324
|
-
model=
|
1323
|
+
model=chat_model.name,
|
1325
1324
|
api_key=api_key,
|
1326
1325
|
completion_func=partial_completion,
|
1327
1326
|
conversation_commands=conversation_commands,
|
1328
|
-
max_prompt_size=
|
1329
|
-
tokenizer_name=
|
1327
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1328
|
+
tokenizer_name=chat_model.tokenizer,
|
1330
1329
|
location_data=location_data,
|
1331
1330
|
user_name=user_name,
|
1332
1331
|
agent=agent,
|
@@ -1339,7 +1338,7 @@ def generate_chat_response(
|
|
1339
1338
|
tracer=tracer,
|
1340
1339
|
)
|
1341
1340
|
|
1342
|
-
metadata.update({"chat_model":
|
1341
|
+
metadata.update({"chat_model": chat_model.name})
|
1343
1342
|
|
1344
1343
|
except Exception as e:
|
1345
1344
|
logger.error(e, exc_info=True)
|
@@ -1359,6 +1358,49 @@ class FeedbackData(BaseModel):
|
|
1359
1358
|
sentiment: str
|
1360
1359
|
|
1361
1360
|
|
1361
|
+
class EmailVerificationApiRateLimiter:
|
1362
|
+
def __init__(self, requests: int, window: int, slug: str):
|
1363
|
+
self.requests = requests
|
1364
|
+
self.window = window
|
1365
|
+
self.slug = slug
|
1366
|
+
|
1367
|
+
def __call__(self, request: Request):
|
1368
|
+
# Rate limiting disabled if billing is disabled
|
1369
|
+
if state.billing_enabled is False:
|
1370
|
+
return
|
1371
|
+
|
1372
|
+
# Extract the email query parameter
|
1373
|
+
email = request.query_params.get("email")
|
1374
|
+
|
1375
|
+
if email:
|
1376
|
+
logger.info(f"Email query parameter: {email}")
|
1377
|
+
|
1378
|
+
user: KhojUser = get_user_by_email(email)
|
1379
|
+
|
1380
|
+
if not user:
|
1381
|
+
raise HTTPException(
|
1382
|
+
status_code=404,
|
1383
|
+
detail="User not found.",
|
1384
|
+
)
|
1385
|
+
|
1386
|
+
# Remove requests outside of the time window
|
1387
|
+
cutoff = datetime.now(tz=timezone.utc) - timedelta(seconds=self.window)
|
1388
|
+
count_requests = UserRequests.objects.filter(user=user, created_at__gte=cutoff, slug=self.slug).count()
|
1389
|
+
|
1390
|
+
# Check if the user has exceeded the rate limit
|
1391
|
+
if count_requests >= self.requests:
|
1392
|
+
logger.info(
|
1393
|
+
f"Rate limit: {count_requests}/{self.requests} requests not allowed in {self.window} seconds for email: {email}."
|
1394
|
+
)
|
1395
|
+
raise HTTPException(
|
1396
|
+
status_code=429,
|
1397
|
+
detail="Ran out of login attempts",
|
1398
|
+
)
|
1399
|
+
|
1400
|
+
# Add the current request to the db
|
1401
|
+
UserRequests.objects.create(user=user, slug=self.slug)
|
1402
|
+
|
1403
|
+
|
1362
1404
|
class ApiUserRateLimiter:
|
1363
1405
|
def __init__(self, requests: int, subscribed_requests: int, window: int, slug: str):
|
1364
1406
|
self.requests = requests
|
@@ -1638,7 +1680,7 @@ def scheduled_chat(
|
|
1638
1680
|
last_run_time = datetime.strptime(last_run_time, "%Y-%m-%d %I:%M %p %Z").replace(tzinfo=timezone.utc)
|
1639
1681
|
|
1640
1682
|
# If the last run time was within the last 6 hours, don't run it again. This helps avoid multithreading issues and rate limits.
|
1641
|
-
if (datetime.now(timezone.utc) - last_run_time).total_seconds() <
|
1683
|
+
if (datetime.now(timezone.utc) - last_run_time).total_seconds() < 6 * 60 * 60:
|
1642
1684
|
logger.info(f"Skipping scheduled chat {job_id} as the next run time is in the future.")
|
1643
1685
|
return
|
1644
1686
|
|
@@ -1939,13 +1981,13 @@ def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False)
|
|
1939
1981
|
current_notion_config = get_user_notion_config(user)
|
1940
1982
|
notion_token = current_notion_config.token if current_notion_config else ""
|
1941
1983
|
|
1942
|
-
selected_chat_model_config = ConversationAdapters.
|
1984
|
+
selected_chat_model_config = ConversationAdapters.get_chat_model(
|
1943
1985
|
user
|
1944
|
-
) or ConversationAdapters.
|
1986
|
+
) or ConversationAdapters.get_default_chat_model(user)
|
1945
1987
|
chat_models = ConversationAdapters.get_conversation_processor_options().all()
|
1946
1988
|
chat_model_options = list()
|
1947
1989
|
for chat_model in chat_models:
|
1948
|
-
chat_model_options.append({"name": chat_model.
|
1990
|
+
chat_model_options.append({"name": chat_model.name, "id": chat_model.id})
|
1949
1991
|
|
1950
1992
|
selected_paint_model_config = ConversationAdapters.get_user_text_to_image_model_config(user)
|
1951
1993
|
paint_model_options = ConversationAdapters.get_text_to_image_model_options().all()
|
khoj/routers/web_client.py
CHANGED
@@ -57,7 +57,7 @@ def login_page(request: Request):
|
|
57
57
|
if request.user.is_authenticated:
|
58
58
|
return RedirectResponse(url=next_url)
|
59
59
|
google_client_id = os.environ.get("GOOGLE_CLIENT_ID")
|
60
|
-
redirect_uri = str(request.app.url_path_for("
|
60
|
+
redirect_uri = str(request.app.url_path_for("auth_post"))
|
61
61
|
return templates.TemplateResponse(
|
62
62
|
"login.html",
|
63
63
|
context={
|