khoj 1.26.4.dev2__py3-none-any.whl → 1.26.5.dev29__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/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1210.132a7e1910006bbb.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1279-f37ee4a388ebf544.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1459.690bf20e7d7b7090.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1603-b9d95833e0e025e8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/1970-1d6d0c1b00b4f343.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2697-61fcba89fd87eab4.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3423-aad88d6c1f029135.js +1 -0
- khoj/interface/compiled/_next/static/chunks/394-6bcb8c429f168f21.js +3 -0
- khoj/interface/compiled/_next/static/chunks/4602-8eeb4b76385ad159.js +1 -0
- khoj/interface/compiled/_next/static/chunks/5512-94c7c2bbcf58c19d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/7113-f2e114d7034a0835.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{4086-2c74808ba38a5a0f.js → 8840-b8d7b9f0923c6651.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/9417-759984ad62caa3dc.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9479-4b443fdcc99141c9.js +1 -0
- khoj/interface/compiled/_next/static/chunks/94ca1967.5584df65931cfe83.js +1 -0
- khoj/interface/compiled/_next/static/chunks/964ecbae.ea4eab2a3a835ffe.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-5ae1e540bb5be8a9.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-5480731341f34450.js → page-774ae3e033f938cd.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-97f5b61aaf46d364.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/{page-e7b34316ec6f44de.js → page-d82403db2866bad8.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-10a5aad6e04f3cf8.js → page-75bbfb564884054b.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/{page-d56541c746fded7d.js → page-9b64f61caa5bd7f9.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/{page-e044a999468a7c5d.js → page-989cf38b87b19427.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-eb9e282691858f2e.js +1 -0
- khoj/interface/compiled/_next/static/chunks/webpack-8f4afe09848e24e1.js +1 -0
- khoj/interface/compiled/_next/static/css/{c808691c459e3887.css → 3cf13271869a4aeb.css} +1 -1
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +1 -0
- khoj/interface/compiled/_next/static/css/76d55eb435962b19.css +25 -0
- khoj/interface/compiled/_next/static/css/{3e1f1fdd70775091.css → 80bd6301fc657983.css} +1 -1
- khoj/interface/compiled/_next/static/css/ddcc0cf73e062476.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/factchecker/index.html +1 -1
- khoj/interface/compiled/factchecker/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/processor/conversation/google/gemini_chat.py +28 -13
- khoj/processor/conversation/google/utils.py +34 -12
- khoj/processor/conversation/openai/gpt.py +4 -4
- khoj/processor/conversation/prompts.py +144 -0
- khoj/processor/conversation/utils.py +22 -13
- khoj/processor/image/generate.py +5 -5
- khoj/processor/tools/online_search.py +4 -4
- khoj/routers/api.py +4 -2
- khoj/routers/api_agents.py +41 -20
- khoj/routers/api_chat.py +85 -46
- khoj/routers/helpers.py +225 -29
- khoj/routers/web_client.py +0 -11
- khoj/utils/helpers.py +7 -3
- {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/METADATA +1 -1
- {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/RECORD +67 -62
- khoj/interface/compiled/_next/static/chunks/121-7024f479c297aef0.js +0 -1
- khoj/interface/compiled/_next/static/chunks/1603-fa3ee48860b9dc5c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2697-a38d01981ad3bdf8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/4051-2cf66369d6ca0f1d.js +0 -3
- khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7762-79f2205740622b5c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9178-899fe9a6b754ecfe.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9417-46ed3aaa639c85ef.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9479-ea776e73f549090c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-88aa3042711107b7.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-702057ccbcf27881.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-fbbd66a4d4633438.js +0 -1
- khoj/interface/compiled/_next/static/chunks/webpack-2651a68f46ac3cb7.js +0 -1
- khoj/interface/compiled/_next/static/css/2de69f0be774c768.css +0 -1
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +0 -1
- khoj/interface/compiled/_next/static/css/b9a6bf04305d98d7.css +0 -25
- /khoj/interface/compiled/_next/static/{wyjqS7cuSX-u62BTNYqhU → ZLHCGFLxZSUj0jEJSc99T}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{wyjqS7cuSX-u62BTNYqhU → ZLHCGFLxZSUj0jEJSc99T}/_ssgManifest.js +0 -0
- {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/WHEEL +0 -0
- {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/entry_points.txt +0 -0
- {khoj-1.26.4.dev2.dist-info → khoj-1.26.5.dev29.dist-info}/licenses/LICENSE +0 -0
@@ -176,6 +176,150 @@ Improved Prompt:
|
|
176
176
|
""".strip()
|
177
177
|
)
|
178
178
|
|
179
|
+
## Diagram Generation
|
180
|
+
## --
|
181
|
+
|
182
|
+
improve_diagram_description_prompt = PromptTemplate.from_template(
|
183
|
+
"""
|
184
|
+
you are an architect working with a novice artist using a diagramming tool.
|
185
|
+
{personality_context}
|
186
|
+
|
187
|
+
you need to convert the user's query to a description format that the novice artist can use very well. you are allowed to use primitives like
|
188
|
+
- text
|
189
|
+
- rectangle
|
190
|
+
- diamond
|
191
|
+
- ellipse
|
192
|
+
- line
|
193
|
+
- arrow
|
194
|
+
- frame
|
195
|
+
|
196
|
+
use these primitives to describe what sort of diagram the drawer should create. the artist must recreate the diagram every time, so include all relevant prior information in your description.
|
197
|
+
|
198
|
+
use simple, concise language.
|
199
|
+
|
200
|
+
Today's Date: {current_date}
|
201
|
+
User's Location: {location}
|
202
|
+
|
203
|
+
User's Notes:
|
204
|
+
{references}
|
205
|
+
|
206
|
+
Online References:
|
207
|
+
{online_results}
|
208
|
+
|
209
|
+
Conversation Log:
|
210
|
+
{chat_history}
|
211
|
+
|
212
|
+
Query: {query}
|
213
|
+
|
214
|
+
|
215
|
+
""".strip()
|
216
|
+
)
|
217
|
+
|
218
|
+
excalidraw_diagram_generation_prompt = PromptTemplate.from_template(
|
219
|
+
"""
|
220
|
+
You are a program manager with the ability to describe diagrams to compose in professional, fine detail.
|
221
|
+
{personality_context}
|
222
|
+
|
223
|
+
You need to create a declarative description of the diagram and relevant components, using this base schema. Use the `label` property to specify the text to be rendered in the respective elements. Always use light colors for the `backgroundColor` property, like white, or light blue, green, red. "type", "x", "y", "id", are required properties for all elements.
|
224
|
+
|
225
|
+
{{
|
226
|
+
type: string,
|
227
|
+
x: number,
|
228
|
+
y: number,
|
229
|
+
strokeColor: string,
|
230
|
+
backgroundColor: string,
|
231
|
+
width: number,
|
232
|
+
height: number,
|
233
|
+
id: string,
|
234
|
+
label: {{
|
235
|
+
text: string,
|
236
|
+
}}
|
237
|
+
}}
|
238
|
+
|
239
|
+
Valid types:
|
240
|
+
- text
|
241
|
+
- rectangle
|
242
|
+
- diamond
|
243
|
+
- ellipse
|
244
|
+
- line
|
245
|
+
- arrow
|
246
|
+
|
247
|
+
For arrows and lines, you can use the `points` property to specify the start and end points of the arrow. You may also use the `label` property to specify the text to be rendered. You may use the `start` and `end` properties to connect the linear elements to other elements. The start and end point can either be the ID to map to an existing object, or the `type` to create a new object. Mapping to an existing object is useful if you want to connect it to multiple objects. Lines and arrows can only start and end at rectangle, text, diamond, or ellipse elements.
|
248
|
+
|
249
|
+
{{
|
250
|
+
type: "arrow",
|
251
|
+
id: string,
|
252
|
+
x: number,
|
253
|
+
y: number,
|
254
|
+
width: number,
|
255
|
+
height: number,
|
256
|
+
strokeColor: string,
|
257
|
+
start: {{
|
258
|
+
id: string,
|
259
|
+
type: string,
|
260
|
+
}},
|
261
|
+
end: {{
|
262
|
+
id: string,
|
263
|
+
type: string,
|
264
|
+
}},
|
265
|
+
label: {{
|
266
|
+
text: string,
|
267
|
+
}}
|
268
|
+
points: [
|
269
|
+
[number, number],
|
270
|
+
[number, number],
|
271
|
+
]
|
272
|
+
}}
|
273
|
+
|
274
|
+
For text, you must use the `text` property to specify the text to be rendered. You may also use `fontSize` property to specify the font size of the text. Only use the `text` element for titles, subtitles, and overviews. For labels, use the `label` property in the respective elements.
|
275
|
+
|
276
|
+
{{
|
277
|
+
type: "text",
|
278
|
+
id: string,
|
279
|
+
x: number,
|
280
|
+
y: number,
|
281
|
+
fontSize: number,
|
282
|
+
text: string,
|
283
|
+
}}
|
284
|
+
|
285
|
+
For frames, use the `children` property to specify the elements that are inside the frame by their ids.
|
286
|
+
|
287
|
+
{{
|
288
|
+
type: "frame",
|
289
|
+
id: string,
|
290
|
+
x: number,
|
291
|
+
y: number,
|
292
|
+
width: number,
|
293
|
+
height: number,
|
294
|
+
name: string,
|
295
|
+
children: [
|
296
|
+
string
|
297
|
+
]
|
298
|
+
}}
|
299
|
+
|
300
|
+
Here's an example of a valid diagram:
|
301
|
+
|
302
|
+
Design Description: Create a diagram describing a circular development process with 3 stages: design, implementation and feedback. The design stage is connected to the implementation stage and the implementation stage is connected to the feedback stage and the feedback stage is connected to the design stage. Each stage should be labeled with the stage name.
|
303
|
+
|
304
|
+
Response:
|
305
|
+
|
306
|
+
[
|
307
|
+
{{"type":"text","x":-150,"y":50,"width":300,"height":40,"id":"title_text","text":"Circular Development Process","fontSize":24}},
|
308
|
+
{{"type":"ellipse","x":-169,"y":113,"width":188,"height":202,"id":"design_ellipse", "label": {{"text": "Design"}}}},
|
309
|
+
{{"type":"ellipse","x":62,"y":394,"width":186,"height":188,"id":"implement_ellipse", "label": {{"text": "Implement"}}}},
|
310
|
+
{{"type":"ellipse","x":-348,"y":430,"width":184,"height":170,"id":"feedback_ellipse", "label": {{"text": "Feedback"}}}},
|
311
|
+
{{"type":"arrow","x":21,"y":273,"id":"design_to_implement_arrow","points":[[0,0],[86,105]],"start":{{"id":"design_ellipse"}}, "end":{{"id":"implement_ellipse"}}}},
|
312
|
+
{{"type":"arrow","x":50,"y":519,"id":"implement_to_feedback_arrow","points":[[0,0],[-198,-6]],"start":{{"id":"implement_ellipse"}}, "end":{{"id":"feedback_ellipse"}}}},
|
313
|
+
{{"type":"arrow","x":-228,"y":417,"id":"feedback_to_design_arrow","points":[[0,0],[85,-123]],"start":{{"id":"feedback_ellipse"}}, "end":{{"id":"design_ellipse"}}}},
|
314
|
+
]
|
315
|
+
|
316
|
+
Create a detailed diagram from the provided context and user prompt below. Return a valid JSON object:
|
317
|
+
|
318
|
+
Diagram Description: {query}
|
319
|
+
|
320
|
+
""".strip()
|
321
|
+
)
|
322
|
+
|
179
323
|
## Online Search Conversation
|
180
324
|
## --
|
181
325
|
online_search_conversation = PromptTemplate.from_template(
|
@@ -109,7 +109,7 @@ def save_to_conversation_log(
|
|
109
109
|
client_application: ClientApplication = None,
|
110
110
|
conversation_id: str = None,
|
111
111
|
automation_id: str = None,
|
112
|
-
|
112
|
+
query_images: List[str] = None,
|
113
113
|
):
|
114
114
|
user_message_time = user_message_time or datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
115
115
|
updated_conversation = message_to_log(
|
@@ -117,7 +117,7 @@ def save_to_conversation_log(
|
|
117
117
|
chat_response=chat_response,
|
118
118
|
user_message_metadata={
|
119
119
|
"created": user_message_time,
|
120
|
-
"
|
120
|
+
"images": query_images,
|
121
121
|
},
|
122
122
|
khoj_message_metadata={
|
123
123
|
"context": compiled_references,
|
@@ -145,10 +145,18 @@ Khoj: "{inferred_queries if ("text-to-image" in intent_type) else chat_response}
|
|
145
145
|
)
|
146
146
|
|
147
147
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
148
|
+
def construct_structured_message(message: str, images: list[str], model_type: str, vision_enabled: bool):
|
149
|
+
"""
|
150
|
+
Format messages into appropriate multimedia format for supported chat model types
|
151
|
+
"""
|
152
|
+
if not images or not vision_enabled:
|
153
|
+
return message
|
154
|
+
|
155
|
+
if model_type in [ChatModelOptions.ModelType.OPENAI, ChatModelOptions.ModelType.GOOGLE]:
|
156
|
+
return [
|
157
|
+
{"type": "text", "text": message},
|
158
|
+
*[{"type": "image_url", "image_url": {"url": image}} for image in images],
|
159
|
+
]
|
152
160
|
return message
|
153
161
|
|
154
162
|
|
@@ -160,7 +168,7 @@ def generate_chatml_messages_with_context(
|
|
160
168
|
loaded_model: Optional[Llama] = None,
|
161
169
|
max_prompt_size=None,
|
162
170
|
tokenizer_name=None,
|
163
|
-
|
171
|
+
query_images=None,
|
164
172
|
vision_enabled=False,
|
165
173
|
model_type="",
|
166
174
|
):
|
@@ -181,11 +189,12 @@ def generate_chatml_messages_with_context(
|
|
181
189
|
message_notes = f'\n\n Notes:\n{chat.get("context")}' if chat.get("context") else "\n"
|
182
190
|
role = "user" if chat["by"] == "you" else "assistant"
|
183
191
|
|
184
|
-
|
192
|
+
if chat["by"] == "khoj" and "excalidraw" in chat["intent"].get("type"):
|
193
|
+
message_content = chat.get("intent").get("inferred-queries")[0] + message_notes
|
194
|
+
else:
|
195
|
+
message_content = chat["message"] + message_notes
|
185
196
|
|
186
|
-
message_content = construct_structured_message(
|
187
|
-
message_content, chat.get("uploadedImageData"), model_type, vision_enabled
|
188
|
-
)
|
197
|
+
message_content = construct_structured_message(message_content, chat.get("images"), model_type, vision_enabled)
|
189
198
|
|
190
199
|
reconstructed_message = ChatMessage(content=message_content, role=role)
|
191
200
|
|
@@ -198,7 +207,7 @@ def generate_chatml_messages_with_context(
|
|
198
207
|
if not is_none_or_empty(user_message):
|
199
208
|
messages.append(
|
200
209
|
ChatMessage(
|
201
|
-
content=construct_structured_message(user_message,
|
210
|
+
content=construct_structured_message(user_message, query_images, model_type, vision_enabled),
|
202
211
|
role="user",
|
203
212
|
)
|
204
213
|
)
|
@@ -222,7 +231,6 @@ def truncate_messages(
|
|
222
231
|
tokenizer_name=None,
|
223
232
|
) -> list[ChatMessage]:
|
224
233
|
"""Truncate messages to fit within max prompt size supported by model"""
|
225
|
-
|
226
234
|
default_tokenizer = "gpt-4o"
|
227
235
|
|
228
236
|
try:
|
@@ -252,6 +260,7 @@ def truncate_messages(
|
|
252
260
|
system_message = messages.pop(idx)
|
253
261
|
break
|
254
262
|
|
263
|
+
# TODO: Handle truncation of multi-part message.content, i.e when message.content is a list[dict] rather than a string
|
255
264
|
system_message_tokens = (
|
256
265
|
len(encoder.encode(system_message.content)) if system_message and type(system_message.content) == str else 0
|
257
266
|
)
|
khoj/processor/image/generate.py
CHANGED
@@ -26,7 +26,7 @@ async def text_to_image(
|
|
26
26
|
references: List[Dict[str, Any]],
|
27
27
|
online_results: Dict[str, Any],
|
28
28
|
send_status_func: Optional[Callable] = None,
|
29
|
-
|
29
|
+
query_images: Optional[List[str]] = None,
|
30
30
|
agent: Agent = None,
|
31
31
|
):
|
32
32
|
status_code = 200
|
@@ -65,7 +65,7 @@ async def text_to_image(
|
|
65
65
|
note_references=references,
|
66
66
|
online_results=online_results,
|
67
67
|
model_type=text_to_image_config.model_type,
|
68
|
-
|
68
|
+
query_images=query_images,
|
69
69
|
user=user,
|
70
70
|
agent=agent,
|
71
71
|
)
|
@@ -87,18 +87,18 @@ async def text_to_image(
|
|
87
87
|
if "content_policy_violation" in e.message:
|
88
88
|
logger.error(f"Image Generation blocked by OpenAI: {e}")
|
89
89
|
status_code = e.status_code # type: ignore
|
90
|
-
message = f"Image generation blocked by OpenAI
|
90
|
+
message = f"Image generation blocked by OpenAI due to policy violation" # type: ignore
|
91
91
|
yield image_url or image, status_code, message, intent_type.value
|
92
92
|
return
|
93
93
|
else:
|
94
94
|
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
95
|
-
message = f"Image generation failed
|
95
|
+
message = f"Image generation failed using OpenAI" # type: ignore
|
96
96
|
status_code = e.status_code # type: ignore
|
97
97
|
yield image_url or image, status_code, message, intent_type.value
|
98
98
|
return
|
99
99
|
except requests.RequestException as e:
|
100
100
|
logger.error(f"Image Generation failed with {e}", exc_info=True)
|
101
|
-
message = f"Image generation using {text2image_model} via {text_to_image_config.model_type} failed
|
101
|
+
message = f"Image generation using {text2image_model} via {text_to_image_config.model_type} failed due to a network error."
|
102
102
|
status_code = 502
|
103
103
|
yield image_url or image, status_code, message, intent_type.value
|
104
104
|
return
|
@@ -62,7 +62,7 @@ async def search_online(
|
|
62
62
|
user: KhojUser,
|
63
63
|
send_status_func: Optional[Callable] = None,
|
64
64
|
custom_filters: List[str] = [],
|
65
|
-
|
65
|
+
query_images: List[str] = None,
|
66
66
|
agent: Agent = None,
|
67
67
|
):
|
68
68
|
query += " ".join(custom_filters)
|
@@ -73,7 +73,7 @@ async def search_online(
|
|
73
73
|
|
74
74
|
# Breakdown the query into subqueries to get the correct answer
|
75
75
|
subqueries = await generate_online_subqueries(
|
76
|
-
query, conversation_history, location, user,
|
76
|
+
query, conversation_history, location, user, query_images=query_images, agent=agent
|
77
77
|
)
|
78
78
|
response_dict = {}
|
79
79
|
|
@@ -151,7 +151,7 @@ async def read_webpages(
|
|
151
151
|
location: LocationData,
|
152
152
|
user: KhojUser,
|
153
153
|
send_status_func: Optional[Callable] = None,
|
154
|
-
|
154
|
+
query_images: List[str] = None,
|
155
155
|
agent: Agent = None,
|
156
156
|
):
|
157
157
|
"Infer web pages to read from the query and extract relevant information from them"
|
@@ -159,7 +159,7 @@ async def read_webpages(
|
|
159
159
|
if send_status_func:
|
160
160
|
async for event in send_status_func(f"**Inferring web pages to read**"):
|
161
161
|
yield {ChatEvent.STATUS: event}
|
162
|
-
urls = await infer_webpage_urls(query, conversation_history, location, user,
|
162
|
+
urls = await infer_webpage_urls(query, conversation_history, location, user, query_images)
|
163
163
|
|
164
164
|
logger.info(f"Reading web pages at: {urls}")
|
165
165
|
if send_status_func:
|
khoj/routers/api.py
CHANGED
@@ -347,7 +347,7 @@ async def extract_references_and_questions(
|
|
347
347
|
conversation_commands: List[ConversationCommand] = [ConversationCommand.Default],
|
348
348
|
location_data: LocationData = None,
|
349
349
|
send_status_func: Optional[Callable] = None,
|
350
|
-
|
350
|
+
query_images: Optional[List[str]] = None,
|
351
351
|
agent: Agent = None,
|
352
352
|
):
|
353
353
|
user = request.user.object if request.user.is_authenticated else None
|
@@ -438,7 +438,7 @@ async def extract_references_and_questions(
|
|
438
438
|
conversation_log=meta_log,
|
439
439
|
location_data=location_data,
|
440
440
|
user=user,
|
441
|
-
|
441
|
+
query_images=query_images,
|
442
442
|
vision_enabled=vision_enabled,
|
443
443
|
personality_context=personality_context,
|
444
444
|
)
|
@@ -459,12 +459,14 @@ async def extract_references_and_questions(
|
|
459
459
|
chat_model = conversation_config.chat_model
|
460
460
|
inferred_queries = extract_questions_gemini(
|
461
461
|
defiltered_query,
|
462
|
+
query_images=query_images,
|
462
463
|
model=chat_model,
|
463
464
|
api_key=api_key,
|
464
465
|
conversation_log=meta_log,
|
465
466
|
location_data=location_data,
|
466
467
|
max_tokens=conversation_config.max_prompt_size,
|
467
468
|
user=user,
|
469
|
+
vision_enabled=vision_enabled,
|
468
470
|
personality_context=personality_context,
|
469
471
|
)
|
470
472
|
|
khoj/routers/api_agents.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
+
import random
|
4
|
+
from datetime import datetime, timedelta, timezone
|
3
5
|
from typing import Dict, List, Optional
|
4
6
|
|
5
7
|
from asgiref.sync import sync_to_async
|
@@ -9,8 +11,8 @@ from fastapi.responses import Response
|
|
9
11
|
from pydantic import BaseModel
|
10
12
|
from starlette.authentication import requires
|
11
13
|
|
12
|
-
from khoj.database.adapters import AgentAdapters
|
13
|
-
from khoj.database.models import Agent, KhojUser
|
14
|
+
from khoj.database.adapters import AgentAdapters, ConversationAdapters
|
15
|
+
from khoj.database.models import Agent, Conversation, KhojUser
|
14
16
|
from khoj.routers.helpers import CommonQueryParams, acheck_if_safe_prompt
|
15
17
|
from khoj.utils.helpers import (
|
16
18
|
ConversationCommand,
|
@@ -45,30 +47,49 @@ async def all_agents(
|
|
45
47
|
) -> Response:
|
46
48
|
user: KhojUser = request.user.object if request.user.is_authenticated else None
|
47
49
|
agents = await AgentAdapters.aget_all_accessible_agents(user)
|
50
|
+
default_agent = await AgentAdapters.aget_default_agent()
|
51
|
+
default_agent_packet = None
|
48
52
|
agents_packet = list()
|
49
53
|
for agent in agents:
|
50
54
|
files = agent.fileobject_set.all()
|
51
55
|
file_names = [file.file_name for file in files]
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
56
|
+
agent_packet = {
|
57
|
+
"slug": agent.slug,
|
58
|
+
"name": agent.name,
|
59
|
+
"persona": agent.personality,
|
60
|
+
"creator": agent.creator.username if agent.creator else None,
|
61
|
+
"managed_by_admin": agent.managed_by_admin,
|
62
|
+
"color": agent.style_color,
|
63
|
+
"icon": agent.style_icon,
|
64
|
+
"privacy_level": agent.privacy_level,
|
65
|
+
"chat_model": agent.chat_model.chat_model,
|
66
|
+
"files": file_names,
|
67
|
+
"input_tools": agent.input_tools,
|
68
|
+
"output_modes": agent.output_modes,
|
69
|
+
}
|
70
|
+
if agent.slug == default_agent.slug:
|
71
|
+
default_agent_packet = agent_packet
|
72
|
+
else:
|
73
|
+
agents_packet.append(agent_packet)
|
74
|
+
|
75
|
+
# Load recent conversation sessions
|
76
|
+
min_date = datetime.min.replace(tzinfo=timezone.utc)
|
77
|
+
two_weeks_ago = datetime.today() - timedelta(weeks=2)
|
78
|
+
conversations = []
|
79
|
+
if user:
|
80
|
+
conversations = await sync_to_async(list[Conversation])(
|
81
|
+
ConversationAdapters.get_conversation_sessions(user, request.user.client_app)
|
82
|
+
.filter(updated_at__gte=two_weeks_ago)
|
83
|
+
.order_by("-updated_at")[:50]
|
67
84
|
)
|
85
|
+
conversation_times = {conv.agent.slug: conv.updated_at for conv in conversations if conv.agent}
|
86
|
+
|
87
|
+
# Put default agent first, then sort by mru and finally shuffle unused randomly
|
88
|
+
random.shuffle(agents_packet)
|
89
|
+
agents_packet.sort(key=lambda x: conversation_times.get(x["slug"]) or min_date, reverse=True)
|
90
|
+
if default_agent_packet:
|
91
|
+
agents_packet.insert(0, default_agent_packet)
|
68
92
|
|
69
|
-
# Make sure that the agent named 'khoj' is first in the list. Everything else is sorted by name.
|
70
|
-
agents_packet.sort(key=lambda x: x["name"])
|
71
|
-
agents_packet.sort(key=lambda x: x["slug"] == "khoj", reverse=True)
|
72
93
|
return Response(content=json.dumps(agents_packet), media_type="application/json", status_code=200)
|
73
94
|
|
74
95
|
|