khoj 1.31.0__py3-none-any.whl → 1.31.1.dev13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- khoj/database/adapters/__init__.py +51 -51
- khoj/database/admin.py +8 -8
- khoj/database/migrations/0077_chatmodel_alter_agent_chat_model_and_more.py +62 -0
- khoj/database/models/__init__.py +7 -7
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/{page-379949e11f084cf5.js → page-2a0b821cf69bdf06.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-ca10c1cf79ae54bb.js → page-ffa30be1dda97643.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-9219a85f3477e722.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/{page-8a87c5de878f4f44.js → page-c2c62ae6f013443c.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-b09139cb91859cd7.js → page-083f798a7562cda5.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-2ca475462c0b2176.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/{page-bd47c769b7700d1d.js → page-845fe099f1f4375e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-2a2679b6e10dbac1.js → page-3257ef0146ab18da.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-592e8c470f2c2084.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{page-751695d28116e626.js → page-f625859c1a122441.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/{webpack-4bd818a6399690ae.js → webpack-062298330010d2aa.js} +1 -1
- khoj/interface/compiled/_next/static/css/80bd6301fc657983.css +1 -0
- khoj/interface/compiled/_next/static/css/f172a0fb3eb177e1.css +1 -0
- khoj/interface/compiled/agents/index.html +1 -1
- khoj/interface/compiled/agents/index.txt +2 -2
- khoj/interface/compiled/automations/index.html +1 -1
- khoj/interface/compiled/automations/index.txt +2 -2
- khoj/interface/compiled/chat/index.html +1 -1
- khoj/interface/compiled/chat/index.txt +2 -2
- khoj/interface/compiled/index.html +1 -1
- khoj/interface/compiled/index.txt +2 -2
- khoj/interface/compiled/search/index.html +1 -1
- khoj/interface/compiled/search/index.txt +2 -2
- khoj/interface/compiled/settings/index.html +1 -1
- khoj/interface/compiled/settings/index.txt +2 -2
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/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/routers/api.py +22 -27
- khoj/routers/api_agents.py +4 -4
- khoj/routers/api_chat.py +6 -6
- khoj/routers/api_model.py +4 -4
- khoj/routers/helpers.py +106 -102
- khoj/utils/helpers.py +5 -3
- khoj/utils/initialization.py +28 -26
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev13.dist-info}/METADATA +1 -1
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev13.dist-info}/RECORD +53 -52
- khoj/interface/compiled/_next/static/chunks/app/chat/layout-1072c3b0ab136e74.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/layout-cae84c87073877f0.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/layout-3b0c60bc13a963db.js +0 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
- khoj/interface/compiled/_next/static/css/e8fb39147bff7bb4.css +0 -1
- /khoj/interface/compiled/_next/static/{SHDrv3iet5TKNwccvVt6m → 7K5OLB23CafMhZBFHh4NG}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{SHDrv3iet5TKNwccvVt6m → 7K5OLB23CafMhZBFHh4NG}/_ssgManifest.js +0 -0
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev13.dist-info}/WHEEL +0 -0
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev13.dist-info}/entry_points.txt +0 -0
- {khoj-1.31.0.dist-info → khoj-1.31.1.dev13.dist-info}/licenses/LICENSE +0 -0
khoj/routers/helpers.py
CHANGED
@@ -56,7 +56,7 @@ from khoj.database.adapters import (
|
|
56
56
|
)
|
57
57
|
from khoj.database.models import (
|
58
58
|
Agent,
|
59
|
-
|
59
|
+
ChatModel,
|
60
60
|
ClientApplication,
|
61
61
|
Conversation,
|
62
62
|
GithubConfig,
|
@@ -133,40 +133,40 @@ def is_query_empty(query: str) -> bool:
|
|
133
133
|
return is_none_or_empty(query.strip())
|
134
134
|
|
135
135
|
|
136
|
-
def
|
137
|
-
|
136
|
+
def validate_chat_model(user: KhojUser):
|
137
|
+
default_chat_model = ConversationAdapters.get_default_chat_model(user)
|
138
138
|
|
139
|
-
if
|
139
|
+
if default_chat_model is None:
|
140
140
|
raise HTTPException(status_code=500, detail="Contact the server administrator to add a chat model.")
|
141
141
|
|
142
|
-
if
|
142
|
+
if default_chat_model.model_type == "openai" and not default_chat_model.ai_model_api:
|
143
143
|
raise HTTPException(status_code=500, detail="Contact the server administrator to add a chat model.")
|
144
144
|
|
145
145
|
|
146
146
|
async def is_ready_to_chat(user: KhojUser):
|
147
|
-
|
148
|
-
if
|
149
|
-
|
147
|
+
user_chat_model = await ConversationAdapters.aget_user_chat_model(user)
|
148
|
+
if user_chat_model == None:
|
149
|
+
user_chat_model = await ConversationAdapters.aget_default_chat_model(user)
|
150
150
|
|
151
|
-
if
|
152
|
-
|
153
|
-
max_tokens =
|
151
|
+
if user_chat_model and user_chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
152
|
+
chat_model_name = user_chat_model.name
|
153
|
+
max_tokens = user_chat_model.max_prompt_size
|
154
154
|
if state.offline_chat_processor_config is None:
|
155
155
|
logger.info("Loading Offline Chat Model...")
|
156
|
-
state.offline_chat_processor_config = OfflineChatProcessorModel(
|
156
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
157
157
|
return True
|
158
158
|
|
159
159
|
if (
|
160
|
-
|
160
|
+
user_chat_model
|
161
161
|
and (
|
162
|
-
|
162
|
+
user_chat_model.model_type
|
163
163
|
in [
|
164
|
-
|
165
|
-
|
166
|
-
|
164
|
+
ChatModel.ModelType.OPENAI,
|
165
|
+
ChatModel.ModelType.ANTHROPIC,
|
166
|
+
ChatModel.ModelType.GOOGLE,
|
167
167
|
]
|
168
168
|
)
|
169
|
-
and
|
169
|
+
and user_chat_model.ai_model_api
|
170
170
|
):
|
171
171
|
return True
|
172
172
|
|
@@ -942,120 +942,124 @@ async def send_message_to_model_wrapper(
|
|
942
942
|
query_files: str = None,
|
943
943
|
tracer: dict = {},
|
944
944
|
):
|
945
|
-
|
946
|
-
vision_available =
|
945
|
+
chat_model: ChatModel = await ConversationAdapters.aget_default_chat_model(user)
|
946
|
+
vision_available = chat_model.vision_enabled
|
947
947
|
if not vision_available and query_images:
|
948
|
-
logger.warning(f"Vision is not enabled for default model: {
|
948
|
+
logger.warning(f"Vision is not enabled for default model: {chat_model.name}.")
|
949
949
|
vision_enabled_config = await ConversationAdapters.aget_vision_enabled_config()
|
950
950
|
if vision_enabled_config:
|
951
|
-
|
951
|
+
chat_model = vision_enabled_config
|
952
952
|
vision_available = True
|
953
953
|
if vision_available and query_images:
|
954
|
-
logger.info(f"Using {
|
954
|
+
logger.info(f"Using {chat_model.name} model to understand {len(query_images)} images.")
|
955
955
|
|
956
956
|
subscribed = await ais_user_subscribed(user)
|
957
|
-
|
957
|
+
chat_model_name = chat_model.name
|
958
958
|
max_tokens = (
|
959
|
-
|
960
|
-
if subscribed and
|
961
|
-
else
|
959
|
+
chat_model.subscribed_max_prompt_size
|
960
|
+
if subscribed and chat_model.subscribed_max_prompt_size
|
961
|
+
else chat_model.max_prompt_size
|
962
962
|
)
|
963
|
-
tokenizer =
|
964
|
-
model_type =
|
965
|
-
vision_available =
|
963
|
+
tokenizer = chat_model.tokenizer
|
964
|
+
model_type = chat_model.model_type
|
965
|
+
vision_available = chat_model.vision_enabled
|
966
966
|
|
967
|
-
if model_type ==
|
967
|
+
if model_type == ChatModel.ModelType.OFFLINE:
|
968
968
|
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(
|
969
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
970
970
|
|
971
971
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
972
972
|
truncated_messages = generate_chatml_messages_with_context(
|
973
973
|
user_message=query,
|
974
974
|
context_message=context,
|
975
975
|
system_message=system_message,
|
976
|
-
model_name=
|
976
|
+
model_name=chat_model_name,
|
977
977
|
loaded_model=loaded_model,
|
978
978
|
tokenizer_name=tokenizer,
|
979
979
|
max_prompt_size=max_tokens,
|
980
980
|
vision_enabled=vision_available,
|
981
|
-
model_type=
|
981
|
+
model_type=chat_model.model_type,
|
982
982
|
query_files=query_files,
|
983
983
|
)
|
984
984
|
|
985
985
|
return send_message_to_model_offline(
|
986
986
|
messages=truncated_messages,
|
987
987
|
loaded_model=loaded_model,
|
988
|
-
|
988
|
+
model_name=chat_model_name,
|
989
989
|
max_prompt_size=max_tokens,
|
990
990
|
streaming=False,
|
991
991
|
response_type=response_type,
|
992
992
|
tracer=tracer,
|
993
993
|
)
|
994
994
|
|
995
|
-
elif model_type ==
|
996
|
-
openai_chat_config =
|
995
|
+
elif model_type == ChatModel.ModelType.OPENAI:
|
996
|
+
openai_chat_config = chat_model.ai_model_api
|
997
997
|
api_key = openai_chat_config.api_key
|
998
998
|
api_base_url = openai_chat_config.api_base_url
|
999
999
|
truncated_messages = generate_chatml_messages_with_context(
|
1000
1000
|
user_message=query,
|
1001
1001
|
context_message=context,
|
1002
1002
|
system_message=system_message,
|
1003
|
-
model_name=
|
1003
|
+
model_name=chat_model_name,
|
1004
1004
|
max_prompt_size=max_tokens,
|
1005
1005
|
tokenizer_name=tokenizer,
|
1006
1006
|
vision_enabled=vision_available,
|
1007
1007
|
query_images=query_images,
|
1008
|
-
model_type=
|
1008
|
+
model_type=chat_model.model_type,
|
1009
1009
|
query_files=query_files,
|
1010
1010
|
)
|
1011
1011
|
|
1012
1012
|
return send_message_to_model(
|
1013
1013
|
messages=truncated_messages,
|
1014
1014
|
api_key=api_key,
|
1015
|
-
model=
|
1015
|
+
model=chat_model_name,
|
1016
1016
|
response_type=response_type,
|
1017
1017
|
api_base_url=api_base_url,
|
1018
1018
|
tracer=tracer,
|
1019
1019
|
)
|
1020
|
-
elif model_type ==
|
1021
|
-
api_key =
|
1020
|
+
elif model_type == ChatModel.ModelType.ANTHROPIC:
|
1021
|
+
api_key = chat_model.ai_model_api.api_key
|
1022
1022
|
truncated_messages = generate_chatml_messages_with_context(
|
1023
1023
|
user_message=query,
|
1024
1024
|
context_message=context,
|
1025
1025
|
system_message=system_message,
|
1026
|
-
model_name=
|
1026
|
+
model_name=chat_model_name,
|
1027
1027
|
max_prompt_size=max_tokens,
|
1028
1028
|
tokenizer_name=tokenizer,
|
1029
1029
|
vision_enabled=vision_available,
|
1030
1030
|
query_images=query_images,
|
1031
|
-
model_type=
|
1031
|
+
model_type=chat_model.model_type,
|
1032
1032
|
query_files=query_files,
|
1033
1033
|
)
|
1034
1034
|
|
1035
1035
|
return anthropic_send_message_to_model(
|
1036
1036
|
messages=truncated_messages,
|
1037
1037
|
api_key=api_key,
|
1038
|
-
model=
|
1038
|
+
model=chat_model_name,
|
1039
1039
|
response_type=response_type,
|
1040
1040
|
tracer=tracer,
|
1041
1041
|
)
|
1042
|
-
elif model_type ==
|
1043
|
-
api_key =
|
1042
|
+
elif model_type == ChatModel.ModelType.GOOGLE:
|
1043
|
+
api_key = chat_model.ai_model_api.api_key
|
1044
1044
|
truncated_messages = generate_chatml_messages_with_context(
|
1045
1045
|
user_message=query,
|
1046
1046
|
context_message=context,
|
1047
1047
|
system_message=system_message,
|
1048
|
-
model_name=
|
1048
|
+
model_name=chat_model_name,
|
1049
1049
|
max_prompt_size=max_tokens,
|
1050
1050
|
tokenizer_name=tokenizer,
|
1051
1051
|
vision_enabled=vision_available,
|
1052
1052
|
query_images=query_images,
|
1053
|
-
model_type=
|
1053
|
+
model_type=chat_model.model_type,
|
1054
1054
|
query_files=query_files,
|
1055
1055
|
)
|
1056
1056
|
|
1057
1057
|
return gemini_send_message_to_model(
|
1058
|
-
messages=truncated_messages,
|
1058
|
+
messages=truncated_messages,
|
1059
|
+
api_key=api_key,
|
1060
|
+
model=chat_model_name,
|
1061
|
+
response_type=response_type,
|
1062
|
+
tracer=tracer,
|
1059
1063
|
)
|
1060
1064
|
else:
|
1061
1065
|
raise HTTPException(status_code=500, detail="Invalid conversation config")
|
@@ -1069,99 +1073,99 @@ def send_message_to_model_wrapper_sync(
|
|
1069
1073
|
query_files: str = "",
|
1070
1074
|
tracer: dict = {},
|
1071
1075
|
):
|
1072
|
-
|
1076
|
+
chat_model: ChatModel = ConversationAdapters.get_default_chat_model(user)
|
1073
1077
|
|
1074
|
-
if
|
1078
|
+
if chat_model is None:
|
1075
1079
|
raise HTTPException(status_code=500, detail="Contact the server administrator to set a default chat model.")
|
1076
1080
|
|
1077
|
-
|
1078
|
-
max_tokens =
|
1079
|
-
vision_available =
|
1081
|
+
chat_model_name = chat_model.name
|
1082
|
+
max_tokens = chat_model.max_prompt_size
|
1083
|
+
vision_available = chat_model.vision_enabled
|
1080
1084
|
|
1081
|
-
if
|
1085
|
+
if chat_model.model_type == ChatModel.ModelType.OFFLINE:
|
1082
1086
|
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(
|
1087
|
+
state.offline_chat_processor_config = OfflineChatProcessorModel(chat_model_name, max_tokens)
|
1084
1088
|
|
1085
1089
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
1086
1090
|
truncated_messages = generate_chatml_messages_with_context(
|
1087
1091
|
user_message=message,
|
1088
1092
|
system_message=system_message,
|
1089
|
-
model_name=
|
1093
|
+
model_name=chat_model_name,
|
1090
1094
|
loaded_model=loaded_model,
|
1091
1095
|
max_prompt_size=max_tokens,
|
1092
1096
|
vision_enabled=vision_available,
|
1093
|
-
model_type=
|
1097
|
+
model_type=chat_model.model_type,
|
1094
1098
|
query_files=query_files,
|
1095
1099
|
)
|
1096
1100
|
|
1097
1101
|
return send_message_to_model_offline(
|
1098
1102
|
messages=truncated_messages,
|
1099
1103
|
loaded_model=loaded_model,
|
1100
|
-
|
1104
|
+
model_name=chat_model_name,
|
1101
1105
|
max_prompt_size=max_tokens,
|
1102
1106
|
streaming=False,
|
1103
1107
|
response_type=response_type,
|
1104
1108
|
tracer=tracer,
|
1105
1109
|
)
|
1106
1110
|
|
1107
|
-
elif
|
1108
|
-
api_key =
|
1111
|
+
elif chat_model.model_type == ChatModel.ModelType.OPENAI:
|
1112
|
+
api_key = chat_model.ai_model_api.api_key
|
1109
1113
|
truncated_messages = generate_chatml_messages_with_context(
|
1110
1114
|
user_message=message,
|
1111
1115
|
system_message=system_message,
|
1112
|
-
model_name=
|
1116
|
+
model_name=chat_model_name,
|
1113
1117
|
max_prompt_size=max_tokens,
|
1114
1118
|
vision_enabled=vision_available,
|
1115
|
-
model_type=
|
1119
|
+
model_type=chat_model.model_type,
|
1116
1120
|
query_files=query_files,
|
1117
1121
|
)
|
1118
1122
|
|
1119
1123
|
openai_response = send_message_to_model(
|
1120
1124
|
messages=truncated_messages,
|
1121
1125
|
api_key=api_key,
|
1122
|
-
model=
|
1126
|
+
model=chat_model_name,
|
1123
1127
|
response_type=response_type,
|
1124
1128
|
tracer=tracer,
|
1125
1129
|
)
|
1126
1130
|
|
1127
1131
|
return openai_response
|
1128
1132
|
|
1129
|
-
elif
|
1130
|
-
api_key =
|
1133
|
+
elif chat_model.model_type == ChatModel.ModelType.ANTHROPIC:
|
1134
|
+
api_key = chat_model.ai_model_api.api_key
|
1131
1135
|
truncated_messages = generate_chatml_messages_with_context(
|
1132
1136
|
user_message=message,
|
1133
1137
|
system_message=system_message,
|
1134
|
-
model_name=
|
1138
|
+
model_name=chat_model_name,
|
1135
1139
|
max_prompt_size=max_tokens,
|
1136
1140
|
vision_enabled=vision_available,
|
1137
|
-
model_type=
|
1141
|
+
model_type=chat_model.model_type,
|
1138
1142
|
query_files=query_files,
|
1139
1143
|
)
|
1140
1144
|
|
1141
1145
|
return anthropic_send_message_to_model(
|
1142
1146
|
messages=truncated_messages,
|
1143
1147
|
api_key=api_key,
|
1144
|
-
model=
|
1148
|
+
model=chat_model_name,
|
1145
1149
|
response_type=response_type,
|
1146
1150
|
tracer=tracer,
|
1147
1151
|
)
|
1148
1152
|
|
1149
|
-
elif
|
1150
|
-
api_key =
|
1153
|
+
elif chat_model.model_type == ChatModel.ModelType.GOOGLE:
|
1154
|
+
api_key = chat_model.ai_model_api.api_key
|
1151
1155
|
truncated_messages = generate_chatml_messages_with_context(
|
1152
1156
|
user_message=message,
|
1153
1157
|
system_message=system_message,
|
1154
|
-
model_name=
|
1158
|
+
model_name=chat_model_name,
|
1155
1159
|
max_prompt_size=max_tokens,
|
1156
1160
|
vision_enabled=vision_available,
|
1157
|
-
model_type=
|
1161
|
+
model_type=chat_model.model_type,
|
1158
1162
|
query_files=query_files,
|
1159
1163
|
)
|
1160
1164
|
|
1161
1165
|
return gemini_send_message_to_model(
|
1162
1166
|
messages=truncated_messages,
|
1163
1167
|
api_key=api_key,
|
1164
|
-
model=
|
1168
|
+
model=chat_model_name,
|
1165
1169
|
response_type=response_type,
|
1166
1170
|
tracer=tracer,
|
1167
1171
|
)
|
@@ -1229,15 +1233,15 @@ def generate_chat_response(
|
|
1229
1233
|
online_results = {}
|
1230
1234
|
code_results = {}
|
1231
1235
|
|
1232
|
-
|
1233
|
-
vision_available =
|
1236
|
+
chat_model = ConversationAdapters.get_valid_chat_model(user, conversation)
|
1237
|
+
vision_available = chat_model.vision_enabled
|
1234
1238
|
if not vision_available and query_images:
|
1235
1239
|
vision_enabled_config = ConversationAdapters.get_vision_enabled_config()
|
1236
1240
|
if vision_enabled_config:
|
1237
|
-
|
1241
|
+
chat_model = vision_enabled_config
|
1238
1242
|
vision_available = True
|
1239
1243
|
|
1240
|
-
if
|
1244
|
+
if chat_model.model_type == "offline":
|
1241
1245
|
loaded_model = state.offline_chat_processor_config.loaded_model
|
1242
1246
|
chat_response = converse_offline(
|
1243
1247
|
user_query=query_to_run,
|
@@ -1247,9 +1251,9 @@ def generate_chat_response(
|
|
1247
1251
|
conversation_log=meta_log,
|
1248
1252
|
completion_func=partial_completion,
|
1249
1253
|
conversation_commands=conversation_commands,
|
1250
|
-
|
1251
|
-
max_prompt_size=
|
1252
|
-
tokenizer_name=
|
1254
|
+
model_name=chat_model.name,
|
1255
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1256
|
+
tokenizer_name=chat_model.tokenizer,
|
1253
1257
|
location_data=location_data,
|
1254
1258
|
user_name=user_name,
|
1255
1259
|
agent=agent,
|
@@ -1259,10 +1263,10 @@ def generate_chat_response(
|
|
1259
1263
|
tracer=tracer,
|
1260
1264
|
)
|
1261
1265
|
|
1262
|
-
elif
|
1263
|
-
openai_chat_config =
|
1266
|
+
elif chat_model.model_type == ChatModel.ModelType.OPENAI:
|
1267
|
+
openai_chat_config = chat_model.ai_model_api
|
1264
1268
|
api_key = openai_chat_config.api_key
|
1265
|
-
|
1269
|
+
chat_model_name = chat_model.name
|
1266
1270
|
chat_response = converse_openai(
|
1267
1271
|
compiled_references,
|
1268
1272
|
query_to_run,
|
@@ -1270,13 +1274,13 @@ def generate_chat_response(
|
|
1270
1274
|
online_results=online_results,
|
1271
1275
|
code_results=code_results,
|
1272
1276
|
conversation_log=meta_log,
|
1273
|
-
model=
|
1277
|
+
model=chat_model_name,
|
1274
1278
|
api_key=api_key,
|
1275
1279
|
api_base_url=openai_chat_config.api_base_url,
|
1276
1280
|
completion_func=partial_completion,
|
1277
1281
|
conversation_commands=conversation_commands,
|
1278
|
-
max_prompt_size=
|
1279
|
-
tokenizer_name=
|
1282
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1283
|
+
tokenizer_name=chat_model.tokenizer,
|
1280
1284
|
location_data=location_data,
|
1281
1285
|
user_name=user_name,
|
1282
1286
|
agent=agent,
|
@@ -1288,8 +1292,8 @@ def generate_chat_response(
|
|
1288
1292
|
tracer=tracer,
|
1289
1293
|
)
|
1290
1294
|
|
1291
|
-
elif
|
1292
|
-
api_key =
|
1295
|
+
elif chat_model.model_type == ChatModel.ModelType.ANTHROPIC:
|
1296
|
+
api_key = chat_model.ai_model_api.api_key
|
1293
1297
|
chat_response = converse_anthropic(
|
1294
1298
|
compiled_references,
|
1295
1299
|
query_to_run,
|
@@ -1297,12 +1301,12 @@ def generate_chat_response(
|
|
1297
1301
|
online_results=online_results,
|
1298
1302
|
code_results=code_results,
|
1299
1303
|
conversation_log=meta_log,
|
1300
|
-
model=
|
1304
|
+
model=chat_model.name,
|
1301
1305
|
api_key=api_key,
|
1302
1306
|
completion_func=partial_completion,
|
1303
1307
|
conversation_commands=conversation_commands,
|
1304
|
-
max_prompt_size=
|
1305
|
-
tokenizer_name=
|
1308
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1309
|
+
tokenizer_name=chat_model.tokenizer,
|
1306
1310
|
location_data=location_data,
|
1307
1311
|
user_name=user_name,
|
1308
1312
|
agent=agent,
|
@@ -1313,20 +1317,20 @@ def generate_chat_response(
|
|
1313
1317
|
program_execution_context=program_execution_context,
|
1314
1318
|
tracer=tracer,
|
1315
1319
|
)
|
1316
|
-
elif
|
1317
|
-
api_key =
|
1320
|
+
elif chat_model.model_type == ChatModel.ModelType.GOOGLE:
|
1321
|
+
api_key = chat_model.ai_model_api.api_key
|
1318
1322
|
chat_response = converse_gemini(
|
1319
1323
|
compiled_references,
|
1320
1324
|
query_to_run,
|
1321
1325
|
online_results,
|
1322
1326
|
code_results,
|
1323
1327
|
meta_log,
|
1324
|
-
model=
|
1328
|
+
model=chat_model.name,
|
1325
1329
|
api_key=api_key,
|
1326
1330
|
completion_func=partial_completion,
|
1327
1331
|
conversation_commands=conversation_commands,
|
1328
|
-
max_prompt_size=
|
1329
|
-
tokenizer_name=
|
1332
|
+
max_prompt_size=chat_model.max_prompt_size,
|
1333
|
+
tokenizer_name=chat_model.tokenizer,
|
1330
1334
|
location_data=location_data,
|
1331
1335
|
user_name=user_name,
|
1332
1336
|
agent=agent,
|
@@ -1339,7 +1343,7 @@ def generate_chat_response(
|
|
1339
1343
|
tracer=tracer,
|
1340
1344
|
)
|
1341
1345
|
|
1342
|
-
metadata.update({"chat_model":
|
1346
|
+
metadata.update({"chat_model": chat_model.name})
|
1343
1347
|
|
1344
1348
|
except Exception as e:
|
1345
1349
|
logger.error(e, exc_info=True)
|
@@ -1939,13 +1943,13 @@ def get_user_config(user: KhojUser, request: Request, is_detailed: bool = False)
|
|
1939
1943
|
current_notion_config = get_user_notion_config(user)
|
1940
1944
|
notion_token = current_notion_config.token if current_notion_config else ""
|
1941
1945
|
|
1942
|
-
selected_chat_model_config = ConversationAdapters.
|
1946
|
+
selected_chat_model_config = ConversationAdapters.get_chat_model(
|
1943
1947
|
user
|
1944
|
-
) or ConversationAdapters.
|
1948
|
+
) or ConversationAdapters.get_default_chat_model(user)
|
1945
1949
|
chat_models = ConversationAdapters.get_conversation_processor_options().all()
|
1946
1950
|
chat_model_options = list()
|
1947
1951
|
for chat_model in chat_models:
|
1948
|
-
chat_model_options.append({"name": chat_model.
|
1952
|
+
chat_model_options.append({"name": chat_model.name, "id": chat_model.id})
|
1949
1953
|
|
1950
1954
|
selected_paint_model_config = ConversationAdapters.get_user_text_to_image_model_config(user)
|
1951
1955
|
paint_model_options = ConversationAdapters.get_text_to_image_model_options().all()
|
khoj/utils/helpers.py
CHANGED
@@ -584,13 +584,15 @@ def get_cost_of_chat_message(model_name: str, input_tokens: int = 0, output_toke
|
|
584
584
|
return input_cost + output_cost + prev_cost
|
585
585
|
|
586
586
|
|
587
|
-
def get_chat_usage_metrics(
|
587
|
+
def get_chat_usage_metrics(
|
588
|
+
model_name: str, input_tokens: int = 0, output_tokens: int = 0, usage: dict = {}, cost: float = None
|
589
|
+
):
|
588
590
|
"""
|
589
|
-
Get usage metrics for chat message based on input and output tokens
|
591
|
+
Get usage metrics for chat message based on input and output tokens and cost
|
590
592
|
"""
|
591
593
|
prev_usage = usage or {"input_tokens": 0, "output_tokens": 0, "cost": 0.0}
|
592
594
|
return {
|
593
595
|
"input_tokens": prev_usage["input_tokens"] + input_tokens,
|
594
596
|
"output_tokens": prev_usage["output_tokens"] + output_tokens,
|
595
|
-
"cost": get_cost_of_chat_message(model_name, input_tokens, output_tokens, prev_cost=prev_usage["cost"]),
|
597
|
+
"cost": cost or get_cost_of_chat_message(model_name, input_tokens, output_tokens, prev_cost=prev_usage["cost"]),
|
596
598
|
}
|
khoj/utils/initialization.py
CHANGED
@@ -7,7 +7,7 @@ import openai
|
|
7
7
|
from khoj.database.adapters import ConversationAdapters
|
8
8
|
from khoj.database.models import (
|
9
9
|
AiModelApi,
|
10
|
-
|
10
|
+
ChatModel,
|
11
11
|
KhojUser,
|
12
12
|
SpeechToTextModelOptions,
|
13
13
|
TextToImageModelConfig,
|
@@ -56,12 +56,14 @@ def initialization(interactive: bool = True):
|
|
56
56
|
valid_default_models = [model for model in default_openai_chat_models if model in default_chat_models]
|
57
57
|
other_available_models = [model for model in default_chat_models if model not in valid_default_models]
|
58
58
|
default_chat_models = valid_default_models + other_available_models
|
59
|
-
except Exception:
|
60
|
-
logger.warning(
|
59
|
+
except Exception as e:
|
60
|
+
logger.warning(
|
61
|
+
f"⚠️ Failed to fetch {provider} chat models. Fallback to default models. Error: {str(e)}"
|
62
|
+
)
|
61
63
|
|
62
64
|
# Set up OpenAI's online chat models
|
63
65
|
openai_configured, openai_provider = _setup_chat_model_provider(
|
64
|
-
|
66
|
+
ChatModel.ModelType.OPENAI,
|
65
67
|
default_chat_models,
|
66
68
|
default_api_key=openai_api_key,
|
67
69
|
api_base_url=openai_api_base,
|
@@ -103,7 +105,7 @@ def initialization(interactive: bool = True):
|
|
103
105
|
|
104
106
|
# Set up Google's Gemini online chat models
|
105
107
|
_setup_chat_model_provider(
|
106
|
-
|
108
|
+
ChatModel.ModelType.GOOGLE,
|
107
109
|
default_gemini_chat_models,
|
108
110
|
default_api_key=os.getenv("GEMINI_API_KEY"),
|
109
111
|
vision_enabled=True,
|
@@ -114,7 +116,7 @@ def initialization(interactive: bool = True):
|
|
114
116
|
|
115
117
|
# Set up Anthropic's online chat models
|
116
118
|
_setup_chat_model_provider(
|
117
|
-
|
119
|
+
ChatModel.ModelType.ANTHROPIC,
|
118
120
|
default_anthropic_chat_models,
|
119
121
|
default_api_key=os.getenv("ANTHROPIC_API_KEY"),
|
120
122
|
vision_enabled=True,
|
@@ -124,7 +126,7 @@ def initialization(interactive: bool = True):
|
|
124
126
|
|
125
127
|
# Set up offline chat models
|
126
128
|
_setup_chat_model_provider(
|
127
|
-
|
129
|
+
ChatModel.ModelType.OFFLINE,
|
128
130
|
default_offline_chat_models,
|
129
131
|
default_api_key=None,
|
130
132
|
vision_enabled=False,
|
@@ -133,9 +135,9 @@ def initialization(interactive: bool = True):
|
|
133
135
|
)
|
134
136
|
|
135
137
|
# Explicitly set default chat model
|
136
|
-
chat_models_configured =
|
138
|
+
chat_models_configured = ChatModel.objects.count()
|
137
139
|
if chat_models_configured > 0:
|
138
|
-
default_chat_model_name =
|
140
|
+
default_chat_model_name = ChatModel.objects.first().name
|
139
141
|
# If there are multiple chat models, ask the user to choose the default chat model
|
140
142
|
if chat_models_configured > 1 and interactive:
|
141
143
|
user_chat_model_name = input(
|
@@ -145,7 +147,7 @@ def initialization(interactive: bool = True):
|
|
145
147
|
user_chat_model_name = None
|
146
148
|
|
147
149
|
# If the user's choice is valid, set it as the default chat model
|
148
|
-
if user_chat_model_name and
|
150
|
+
if user_chat_model_name and ChatModel.objects.filter(name=user_chat_model_name).exists():
|
149
151
|
default_chat_model_name = user_chat_model_name
|
150
152
|
|
151
153
|
logger.info("🗣️ Chat model configuration complete")
|
@@ -169,7 +171,7 @@ def initialization(interactive: bool = True):
|
|
169
171
|
logger.info(f"🗣️ Offline speech to text model configured to {offline_speech2text_model}")
|
170
172
|
|
171
173
|
def _setup_chat_model_provider(
|
172
|
-
model_type:
|
174
|
+
model_type: ChatModel.ModelType,
|
173
175
|
default_chat_models: list,
|
174
176
|
default_api_key: str,
|
175
177
|
interactive: bool,
|
@@ -202,10 +204,10 @@ def initialization(interactive: bool = True):
|
|
202
204
|
ai_model_api = AiModelApi.objects.create(api_key=api_key, name=provider_name, api_base_url=api_base_url)
|
203
205
|
|
204
206
|
if interactive:
|
205
|
-
|
207
|
+
user_chat_models = input(
|
206
208
|
f"Enter the {provider_name} chat models you want to use (default: {','.join(default_chat_models)}): "
|
207
209
|
)
|
208
|
-
chat_models =
|
210
|
+
chat_models = user_chat_models.split(",") if user_chat_models != "" else default_chat_models
|
209
211
|
chat_models = [model.strip() for model in chat_models]
|
210
212
|
else:
|
211
213
|
chat_models = default_chat_models
|
@@ -216,7 +218,7 @@ def initialization(interactive: bool = True):
|
|
216
218
|
vision_enabled = vision_enabled and chat_model in supported_vision_models
|
217
219
|
|
218
220
|
chat_model_options = {
|
219
|
-
"
|
221
|
+
"name": chat_model,
|
220
222
|
"model_type": model_type,
|
221
223
|
"max_prompt_size": default_max_tokens,
|
222
224
|
"vision_enabled": vision_enabled,
|
@@ -224,7 +226,7 @@ def initialization(interactive: bool = True):
|
|
224
226
|
"ai_model_api": ai_model_api,
|
225
227
|
}
|
226
228
|
|
227
|
-
|
229
|
+
ChatModel.objects.create(**chat_model_options)
|
228
230
|
|
229
231
|
logger.info(f"🗣️ {provider_name} chat model configuration complete")
|
230
232
|
return True, ai_model_api
|
@@ -248,19 +250,19 @@ def initialization(interactive: bool = True):
|
|
248
250
|
available_models = [model.id for model in openai_client.models.list()]
|
249
251
|
|
250
252
|
# Get existing chat model options for this config
|
251
|
-
existing_models =
|
252
|
-
ai_model_api=config, model_type=
|
253
|
+
existing_models = ChatModel.objects.filter(
|
254
|
+
ai_model_api=config, model_type=ChatModel.ModelType.OPENAI
|
253
255
|
)
|
254
256
|
|
255
257
|
# Add new models
|
256
|
-
for
|
257
|
-
if not existing_models.filter(
|
258
|
-
|
259
|
-
|
260
|
-
model_type=
|
261
|
-
max_prompt_size=model_to_prompt_size.get(
|
262
|
-
vision_enabled=
|
263
|
-
tokenizer=model_to_tokenizer.get(
|
258
|
+
for model_name in available_models:
|
259
|
+
if not existing_models.filter(name=model_name).exists():
|
260
|
+
ChatModel.objects.create(
|
261
|
+
name=model_name,
|
262
|
+
model_type=ChatModel.ModelType.OPENAI,
|
263
|
+
max_prompt_size=model_to_prompt_size.get(model_name),
|
264
|
+
vision_enabled=model_name in default_openai_chat_models,
|
265
|
+
tokenizer=model_to_tokenizer.get(model_name),
|
264
266
|
ai_model_api=config,
|
265
267
|
)
|
266
268
|
|
@@ -282,7 +284,7 @@ def initialization(interactive: bool = True):
|
|
282
284
|
except Exception as e:
|
283
285
|
logger.error(f"🚨 Failed to create admin user: {e}", exc_info=True)
|
284
286
|
|
285
|
-
chat_config = ConversationAdapters.
|
287
|
+
chat_config = ConversationAdapters.get_default_chat_model()
|
286
288
|
if admin_user is None and chat_config is None:
|
287
289
|
while True:
|
288
290
|
try:
|