khoj 1.16.1.dev25__py3-none-any.whl → 1.17.1.dev216__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.
Files changed (47) hide show
  1. khoj/configure.py +6 -6
  2. khoj/database/adapters/__init__.py +55 -26
  3. khoj/database/migrations/0053_agent_style_color_agent_style_icon.py +61 -0
  4. khoj/database/models/__init__.py +35 -0
  5. khoj/interface/web/assets/icons/favicon-128x128.png +0 -0
  6. khoj/interface/web/assets/icons/favicon-256x256.png +0 -0
  7. khoj/interface/web/assets/icons/khoj-logo-sideways-200.png +0 -0
  8. khoj/interface/web/assets/icons/khoj-logo-sideways-500.png +0 -0
  9. khoj/interface/web/assets/icons/khoj-logo-sideways.svg +31 -5384
  10. khoj/interface/web/assets/icons/khoj.svg +26 -0
  11. khoj/interface/web/chat.html +191 -301
  12. khoj/interface/web/content_source_computer_input.html +3 -3
  13. khoj/interface/web/content_source_github_input.html +1 -1
  14. khoj/interface/web/content_source_notion_input.html +1 -1
  15. khoj/interface/web/public_conversation.html +1 -1
  16. khoj/interface/web/search.html +2 -2
  17. khoj/interface/web/{config.html → settings.html} +30 -30
  18. khoj/interface/web/utils.html +1 -1
  19. khoj/processor/content/docx/docx_to_entries.py +4 -9
  20. khoj/processor/content/github/github_to_entries.py +1 -3
  21. khoj/processor/content/images/image_to_entries.py +4 -9
  22. khoj/processor/content/markdown/markdown_to_entries.py +4 -9
  23. khoj/processor/content/notion/notion_to_entries.py +1 -3
  24. khoj/processor/content/org_mode/org_to_entries.py +4 -9
  25. khoj/processor/content/pdf/pdf_to_entries.py +4 -9
  26. khoj/processor/content/plaintext/plaintext_to_entries.py +4 -9
  27. khoj/processor/content/text_to_entries.py +1 -3
  28. khoj/processor/conversation/utils.py +0 -4
  29. khoj/processor/tools/online_search.py +13 -7
  30. khoj/routers/api.py +58 -9
  31. khoj/routers/api_agents.py +3 -1
  32. khoj/routers/api_chat.py +335 -562
  33. khoj/routers/api_content.py +538 -0
  34. khoj/routers/api_model.py +156 -0
  35. khoj/routers/helpers.py +338 -23
  36. khoj/routers/notion.py +2 -8
  37. khoj/routers/web_client.py +43 -256
  38. khoj/search_type/text_search.py +5 -4
  39. khoj/utils/fs_syncer.py +4 -2
  40. khoj/utils/rawconfig.py +6 -1
  41. {khoj-1.16.1.dev25.dist-info → khoj-1.17.1.dev216.dist-info}/METADATA +2 -2
  42. {khoj-1.16.1.dev25.dist-info → khoj-1.17.1.dev216.dist-info}/RECORD +45 -43
  43. khoj/routers/api_config.py +0 -434
  44. khoj/routers/indexer.py +0 -349
  45. {khoj-1.16.1.dev25.dist-info → khoj-1.17.1.dev216.dist-info}/WHEEL +0 -0
  46. {khoj-1.16.1.dev25.dist-info → khoj-1.17.1.dev216.dist-info}/entry_points.txt +0 -0
  47. {khoj-1.16.1.dev25.dist-info → khoj-1.17.1.dev216.dist-info}/licenses/LICENSE +0 -0
khoj/routers/api.py CHANGED
@@ -6,7 +6,6 @@ import os
6
6
  import threading
7
7
  import time
8
8
  import uuid
9
- from random import random
10
9
  from typing import Any, Callable, List, Optional, Union
11
10
 
12
11
  import cron_descriptor
@@ -20,6 +19,7 @@ from fastapi.responses import Response
20
19
  from starlette.authentication import has_required_scope, requires
21
20
 
22
21
  from khoj.configure import initialize_content
22
+ from khoj.database import adapters
23
23
  from khoj.database.adapters import (
24
24
  AutomationAdapters,
25
25
  ConversationAdapters,
@@ -37,9 +37,11 @@ from khoj.processor.conversation.openai.gpt import extract_questions
37
37
  from khoj.processor.conversation.openai.whisper import transcribe_audio
38
38
  from khoj.routers.helpers import (
39
39
  ApiUserRateLimiter,
40
+ ChatEvent,
40
41
  CommonQueryParams,
41
42
  ConversationCommandRateLimiter,
42
43
  acreate_title_from_query,
44
+ get_user_config,
43
45
  schedule_automation,
44
46
  update_telemetry_state,
45
47
  )
@@ -190,7 +192,7 @@ def update(
190
192
  ):
191
193
  user = request.user.object
192
194
  if not state.config:
193
- error_msg = f"🚨 Khoj is not configured.\nConfigure it via http://localhost:42110/config, plugins or by editing {state.config_file}."
195
+ error_msg = f"🚨 Khoj is not configured.\nConfigure it via http://localhost:42110/settings, plugins or by editing {state.config_file}."
194
196
  logger.warning(error_msg)
195
197
  raise HTTPException(status_code=500, detail=error_msg)
196
198
  try:
@@ -223,10 +225,10 @@ async def transcribe(
223
225
  common: CommonQueryParams,
224
226
  file: UploadFile = File(...),
225
227
  rate_limiter_per_minute=Depends(
226
- ApiUserRateLimiter(requests=1, subscribed_requests=10, window=60, slug="transcribe_minute")
228
+ ApiUserRateLimiter(requests=20, subscribed_requests=20, window=60, slug="transcribe_minute")
227
229
  ),
228
230
  rate_limiter_per_day=Depends(
229
- ApiUserRateLimiter(requests=10, subscribed_requests=600, window=60 * 60 * 24, slug="transcribe_day")
231
+ ApiUserRateLimiter(requests=60, subscribed_requests=600, window=60 * 60 * 24, slug="transcribe_day")
230
232
  ),
231
233
  ):
232
234
  user: KhojUser = request.user.object
@@ -277,6 +279,49 @@ async def transcribe(
277
279
  return Response(content=content, media_type="application/json", status_code=200)
278
280
 
279
281
 
282
+ @api.get("/settings", response_class=Response)
283
+ @requires(["authenticated"])
284
+ def get_settings(request: Request, detailed: Optional[bool] = False) -> Response:
285
+ user = request.user.object
286
+ user_config = get_user_config(user, request, is_detailed=detailed)
287
+ del user_config["request"]
288
+
289
+ # Return config data as a JSON response
290
+ return Response(content=json.dumps(user_config), media_type="application/json", status_code=200)
291
+
292
+
293
+ @api.patch("/user/name", status_code=200)
294
+ @requires(["authenticated"])
295
+ def set_user_name(
296
+ request: Request,
297
+ name: str,
298
+ client: Optional[str] = None,
299
+ ):
300
+ user = request.user.object
301
+
302
+ split_name = name.split(" ")
303
+
304
+ if len(split_name) > 2:
305
+ raise HTTPException(status_code=400, detail="Name must be in the format: Firstname Lastname")
306
+
307
+ if len(split_name) == 1:
308
+ first_name = split_name[0]
309
+ last_name = ""
310
+ else:
311
+ first_name, last_name = split_name[0], split_name[-1]
312
+
313
+ adapters.set_user_name(user, first_name, last_name)
314
+
315
+ update_telemetry_state(
316
+ request=request,
317
+ telemetry_type="api",
318
+ api="set_user_name",
319
+ client=client,
320
+ )
321
+
322
+ return {"status": "ok"}
323
+
324
+
280
325
  async def extract_references_and_questions(
281
326
  request: Request,
282
327
  meta_log: dict,
@@ -298,11 +343,13 @@ async def extract_references_and_questions(
298
343
  not ConversationCommand.Notes in conversation_commands
299
344
  and not ConversationCommand.Default in conversation_commands
300
345
  ):
301
- return compiled_references, inferred_queries, q
346
+ yield compiled_references, inferred_queries, q
347
+ return
302
348
 
303
349
  if not await sync_to_async(EntryAdapters.user_has_entries)(user=user):
304
350
  logger.debug("No documents in knowledge base. Use a Khoj client to sync and chat with your docs.")
305
- return compiled_references, inferred_queries, q
351
+ yield compiled_references, inferred_queries, q
352
+ return
306
353
 
307
354
  # Extract filter terms from user message
308
355
  defiltered_query = q
@@ -313,7 +360,8 @@ async def extract_references_and_questions(
313
360
 
314
361
  if not conversation:
315
362
  logger.error(f"Conversation with id {conversation_id} not found.")
316
- return compiled_references, inferred_queries, defiltered_query
363
+ yield compiled_references, inferred_queries, defiltered_query
364
+ return
317
365
 
318
366
  filters_in_query += " ".join([f'file:"{filter}"' for filter in conversation.file_filters])
319
367
  using_offline_chat = False
@@ -373,7 +421,8 @@ async def extract_references_and_questions(
373
421
  logger.info(f"🔍 Searching knowledge base with queries: {inferred_queries}")
374
422
  if send_status_func:
375
423
  inferred_queries_str = "\n- " + "\n- ".join(inferred_queries)
376
- await send_status_func(f"**🔍 Searching Documents for:** {inferred_queries_str}")
424
+ async for event in send_status_func(f"**Searching Documents for:** {inferred_queries_str}"):
425
+ yield {ChatEvent.STATUS: event}
377
426
  for query in inferred_queries:
378
427
  n_items = min(n, 3) if using_offline_chat else n
379
428
  search_results.extend(
@@ -392,7 +441,7 @@ async def extract_references_and_questions(
392
441
  {"compiled": item.additional["compiled"], "file": item.additional["file"]} for item in search_results
393
442
  ]
394
443
 
395
- return compiled_references, inferred_queries, defiltered_query
444
+ yield compiled_references, inferred_queries, defiltered_query
396
445
 
397
446
 
398
447
  @api.get("/health", response_class=Response)
@@ -30,10 +30,12 @@ async def all_agents(
30
30
  "slug": agent.slug,
31
31
  "avatar": agent.avatar,
32
32
  "name": agent.name,
33
- "personality": agent.personality,
33
+ "persona": agent.personality,
34
34
  "public": agent.public,
35
35
  "creator": agent.creator.username if agent.creator else None,
36
36
  "managed_by_admin": agent.managed_by_admin,
37
+ "color": agent.style_color,
38
+ "icon": agent.style_icon,
37
39
  }
38
40
  )
39
41