khoj 1.24.2.dev3__py3-none-any.whl → 1.25.1.dev34__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 (109) hide show
  1. khoj/configure.py +13 -4
  2. khoj/database/adapters/__init__.py +289 -52
  3. khoj/database/admin.py +20 -1
  4. khoj/database/migrations/0065_remove_agent_avatar_remove_agent_public_and_more.py +49 -0
  5. khoj/database/migrations/0066_remove_agent_tools_agent_input_tools_and_more.py +69 -0
  6. khoj/database/migrations/0067_alter_agent_style_icon.py +50 -0
  7. khoj/database/migrations/0068_alter_agent_output_modes.py +24 -0
  8. khoj/database/migrations/0069_webscraper_serverchatsettings_web_scraper.py +89 -0
  9. khoj/database/models/__init__.py +136 -18
  10. khoj/interface/compiled/404/index.html +1 -1
  11. khoj/interface/compiled/_next/static/chunks/1603-fa3ee48860b9dc5c.js +1 -0
  12. khoj/interface/compiled/_next/static/chunks/2697-a38d01981ad3bdf8.js +1 -0
  13. khoj/interface/compiled/_next/static/chunks/3110-ef2cacd1b8d79ad8.js +1 -0
  14. khoj/interface/compiled/_next/static/chunks/4086-2c74808ba38a5a0f.js +1 -0
  15. khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +1 -0
  16. khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +1 -0
  17. khoj/interface/compiled/_next/static/chunks/7762-79f2205740622b5c.js +1 -0
  18. khoj/interface/compiled/_next/static/chunks/9178-899fe9a6b754ecfe.js +1 -0
  19. khoj/interface/compiled/_next/static/chunks/9417-29502e39c3e7d60c.js +1 -0
  20. khoj/interface/compiled/_next/static/chunks/9479-7eed36fc954ef804.js +1 -0
  21. khoj/interface/compiled/_next/static/chunks/app/agents/{layout-e71c8e913cccf792.js → layout-75636ab3a413fa8e.js} +1 -1
  22. khoj/interface/compiled/_next/static/chunks/app/agents/page-fa282831808ee536.js +1 -0
  23. khoj/interface/compiled/_next/static/chunks/app/automations/page-5480731341f34450.js +1 -0
  24. khoj/interface/compiled/_next/static/chunks/app/chat/{layout-8102549127db3067.js → layout-96fcf62857bf8f30.js} +1 -1
  25. khoj/interface/compiled/_next/static/chunks/app/chat/page-702057ccbcf27881.js +1 -0
  26. khoj/interface/compiled/_next/static/chunks/app/factchecker/page-e7b34316ec6f44de.js +1 -0
  27. khoj/interface/compiled/_next/static/chunks/app/{layout-f3e40d346da53112.js → layout-d0f0a9067427fb20.js} +1 -1
  28. khoj/interface/compiled/_next/static/chunks/app/page-10a5aad6e04f3cf8.js +1 -0
  29. khoj/interface/compiled/_next/static/chunks/app/search/page-d56541c746fded7d.js +1 -0
  30. khoj/interface/compiled/_next/static/chunks/app/settings/{layout-6f9314b0d7a26046.js → layout-a8f33dfe92f997fb.js} +1 -1
  31. khoj/interface/compiled/_next/static/chunks/app/settings/page-e044a999468a7c5d.js +1 -0
  32. khoj/interface/compiled/_next/static/chunks/app/share/chat/{layout-39f03f9e32399f0f.js → layout-2df56074e42adaa0.js} +1 -1
  33. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-fbbd66a4d4633438.js +1 -0
  34. khoj/interface/compiled/_next/static/chunks/{webpack-d4781cada9b58e75.js → webpack-c0cd5a6afb1f0798.js} +1 -1
  35. khoj/interface/compiled/_next/static/css/2de69f0be774c768.css +1 -0
  36. khoj/interface/compiled/_next/static/css/467a524c75e7d7c0.css +1 -0
  37. khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +1 -0
  38. khoj/interface/compiled/_next/static/css/b9a6bf04305d98d7.css +25 -0
  39. khoj/interface/compiled/agents/index.html +1 -1
  40. khoj/interface/compiled/agents/index.txt +2 -2
  41. khoj/interface/compiled/automations/index.html +1 -1
  42. khoj/interface/compiled/automations/index.txt +2 -2
  43. khoj/interface/compiled/chat/index.html +1 -1
  44. khoj/interface/compiled/chat/index.txt +2 -2
  45. khoj/interface/compiled/factchecker/index.html +1 -1
  46. khoj/interface/compiled/factchecker/index.txt +2 -2
  47. khoj/interface/compiled/index.html +1 -1
  48. khoj/interface/compiled/index.txt +2 -2
  49. khoj/interface/compiled/search/index.html +1 -1
  50. khoj/interface/compiled/search/index.txt +2 -2
  51. khoj/interface/compiled/settings/index.html +1 -1
  52. khoj/interface/compiled/settings/index.txt +3 -3
  53. khoj/interface/compiled/share/chat/index.html +1 -1
  54. khoj/interface/compiled/share/chat/index.txt +2 -2
  55. khoj/interface/web/assets/icons/agents.svg +1 -0
  56. khoj/interface/web/assets/icons/automation.svg +1 -0
  57. khoj/interface/web/assets/icons/chat.svg +24 -0
  58. khoj/interface/web/login.html +11 -22
  59. khoj/processor/content/notion/notion_to_entries.py +2 -1
  60. khoj/processor/conversation/anthropic/anthropic_chat.py +2 -0
  61. khoj/processor/conversation/google/gemini_chat.py +6 -19
  62. khoj/processor/conversation/google/utils.py +33 -15
  63. khoj/processor/conversation/offline/chat_model.py +3 -1
  64. khoj/processor/conversation/openai/gpt.py +2 -0
  65. khoj/processor/conversation/prompts.py +67 -5
  66. khoj/processor/conversation/utils.py +3 -7
  67. khoj/processor/embeddings.py +6 -3
  68. khoj/processor/image/generate.py +4 -3
  69. khoj/processor/tools/online_search.py +139 -44
  70. khoj/routers/api.py +35 -6
  71. khoj/routers/api_agents.py +235 -4
  72. khoj/routers/api_chat.py +102 -530
  73. khoj/routers/api_content.py +14 -0
  74. khoj/routers/api_model.py +1 -1
  75. khoj/routers/auth.py +9 -1
  76. khoj/routers/helpers.py +181 -68
  77. khoj/routers/subscription.py +18 -4
  78. khoj/search_type/text_search.py +11 -3
  79. khoj/utils/helpers.py +64 -8
  80. khoj/utils/initialization.py +0 -3
  81. {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/METADATA +19 -21
  82. {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/RECORD +87 -81
  83. khoj/interface/compiled/_next/static/chunks/1603-3e2e1528e3b6ea1d.js +0 -1
  84. khoj/interface/compiled/_next/static/chunks/2697-a29cb9191a9e339c.js +0 -1
  85. khoj/interface/compiled/_next/static/chunks/6648-ee109f4ea33a74e2.js +0 -1
  86. khoj/interface/compiled/_next/static/chunks/7071-b4711cecca6619a8.js +0 -1
  87. khoj/interface/compiled/_next/static/chunks/743-1a64254447cda71f.js +0 -1
  88. khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +0 -1
  89. khoj/interface/compiled/_next/static/chunks/9162-0be016519a18568b.js +0 -1
  90. khoj/interface/compiled/_next/static/chunks/9178-7e815211edcb3657.js +0 -1
  91. khoj/interface/compiled/_next/static/chunks/9417-5d14ac74aaab2c66.js +0 -1
  92. khoj/interface/compiled/_next/static/chunks/9984-e410179c6fac7cf1.js +0 -1
  93. khoj/interface/compiled/_next/static/chunks/app/agents/page-d302911777a3e027.js +0 -1
  94. khoj/interface/compiled/_next/static/chunks/app/automations/page-0a5de8c254c29a1c.js +0 -1
  95. khoj/interface/compiled/_next/static/chunks/app/chat/page-d96bf6a84bb05290.js +0 -1
  96. khoj/interface/compiled/_next/static/chunks/app/factchecker/page-32e61af29e6b431d.js +0 -1
  97. khoj/interface/compiled/_next/static/chunks/app/page-96cab08c985716f4.js +0 -1
  98. khoj/interface/compiled/_next/static/chunks/app/search/page-b3193d46c65571c5.js +0 -1
  99. khoj/interface/compiled/_next/static/chunks/app/settings/page-0db9b708366606ec.js +0 -1
  100. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-f06ac16cfe5b5a16.js +0 -1
  101. khoj/interface/compiled/_next/static/css/1538cedb321e3a97.css +0 -1
  102. khoj/interface/compiled/_next/static/css/24f141a6e37cd204.css +0 -25
  103. khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +0 -1
  104. khoj/interface/compiled/_next/static/css/f768dddada62459d.css +0 -1
  105. /khoj/interface/compiled/_next/static/{_29ceahp81LhuIHo5QgOD → Jid9q6Qg851ioDaaO_fth}/_buildManifest.js +0 -0
  106. /khoj/interface/compiled/_next/static/{_29ceahp81LhuIHo5QgOD → Jid9q6Qg851ioDaaO_fth}/_ssgManifest.js +0 -0
  107. {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/WHEEL +0 -0
  108. {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/entry_points.txt +0 -0
  109. {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/licenses/LICENSE +0 -0
@@ -7,6 +7,7 @@ from fastapi import APIRouter, Request
7
7
  from starlette.authentication import requires
8
8
 
9
9
  from khoj.database import adapters
10
+ from khoj.routers.helpers import update_telemetry_state
10
11
  from khoj.utils import state
11
12
 
12
13
  # Stripe integration for Khoj Cloud Subscription
@@ -48,6 +49,8 @@ async def subscribe(request: Request):
48
49
  customer_id = subscription["customer"]
49
50
  customer = stripe.Customer.retrieve(customer_id)
50
51
  customer_email = customer["email"]
52
+ user = None
53
+ is_new = False
51
54
 
52
55
  # Handle valid stripe webhook events
53
56
  success = True
@@ -55,7 +58,9 @@ async def subscribe(request: Request):
55
58
  # Mark the user as subscribed and update the next renewal date on payment
56
59
  subscription = stripe.Subscription.list(customer=customer_id).data[0]
57
60
  renewal_date = datetime.fromtimestamp(subscription["current_period_end"], tz=timezone.utc)
58
- user = await adapters.set_user_subscription(customer_email, is_recurring=True, renewal_date=renewal_date)
61
+ user, is_new = await adapters.set_user_subscription(
62
+ customer_email, is_recurring=True, renewal_date=renewal_date
63
+ )
59
64
  success = user is not None
60
65
  elif event_type in {"customer.subscription.updated"}:
61
66
  user_subscription = await sync_to_async(adapters.get_user_subscription)(customer_email)
@@ -63,15 +68,24 @@ async def subscribe(request: Request):
63
68
  if user_subscription and user_subscription.renewal_date:
64
69
  # Mark user as unsubscribed or resubscribed
65
70
  is_recurring = not subscription["cancel_at_period_end"]
66
- updated_user = await adapters.set_user_subscription(customer_email, is_recurring=is_recurring)
67
- success = updated_user is not None
71
+ user, is_new = await adapters.set_user_subscription(customer_email, is_recurring=is_recurring)
72
+ success = user is not None
68
73
  elif event_type in {"customer.subscription.deleted"}:
69
74
  # Reset the user to trial state
70
- user = await adapters.set_user_subscription(
75
+ user, is_new = await adapters.set_user_subscription(
71
76
  customer_email, is_recurring=False, renewal_date=False, type="trial"
72
77
  )
73
78
  success = user is not None
74
79
 
80
+ if user and is_new:
81
+ update_telemetry_state(
82
+ request=request,
83
+ telemetry_type="api",
84
+ api="create_user",
85
+ metadata={"user_id": str(user.user.uuid)},
86
+ )
87
+ logger.log(logging.INFO, f"🥳 New User Created: {user.user.uuid}")
88
+
75
89
  logger.info(f'Stripe subscription {event["type"]} for {customer_email}')
76
90
  return {"success": success}
77
91
 
@@ -1,13 +1,15 @@
1
1
  import logging
2
2
  import math
3
3
  from pathlib import Path
4
- from typing import List, Tuple, Type, Union
4
+ from typing import List, Optional, Tuple, Type, Union
5
5
 
6
+ import requests
6
7
  import torch
7
8
  from asgiref.sync import sync_to_async
8
9
  from sentence_transformers import util
9
10
 
10
11
  from khoj.database.adapters import EntryAdapters, get_user_search_model_or_default
12
+ from khoj.database.models import Agent
11
13
  from khoj.database.models import Entry as DbEntry
12
14
  from khoj.database.models import KhojUser
13
15
  from khoj.processor.content.text_to_entries import TextToEntries
@@ -101,6 +103,7 @@ async def query(
101
103
  type: SearchType = SearchType.All,
102
104
  question_embedding: Union[torch.Tensor, None] = None,
103
105
  max_distance: float = None,
106
+ agent: Optional[Agent] = None,
104
107
  ) -> Tuple[List[dict], List[Entry]]:
105
108
  "Search for entries that answer the query"
106
109
 
@@ -129,6 +132,7 @@ async def query(
129
132
  file_type_filter=file_type,
130
133
  raw_query=raw_query,
131
134
  max_distance=max_distance,
135
+ agent=agent,
132
136
  ).all()
133
137
  hits = await sync_to_async(list)(hits) # type: ignore[call-arg]
134
138
 
@@ -228,8 +232,12 @@ def setup(
228
232
 
229
233
  def cross_encoder_score(query: str, hits: List[SearchResponse], search_model_name: str) -> List[SearchResponse]:
230
234
  """Score all retrieved entries using the cross-encoder"""
231
- with timer("Cross-Encoder Predict Time", logger, state.device):
232
- cross_scores = state.cross_encoder_model[search_model_name].predict(query, hits)
235
+ try:
236
+ with timer("Cross-Encoder Predict Time", logger, state.device):
237
+ cross_scores = state.cross_encoder_model[search_model_name].predict(query, hits)
238
+ except requests.exceptions.HTTPError as e:
239
+ logger.error(f"Failed to rerank documents using the inference endpoint. Error: {e}.", exc_info=True)
240
+ cross_scores = [0.0] * len(hits)
233
241
 
234
242
  # Convert cross-encoder scores to distances and pass in hits for reranking
235
243
  for idx in range(len(cross_scores)):
khoj/utils/helpers.py CHANGED
@@ -2,10 +2,12 @@ from __future__ import annotations # to avoid quoting type hints
2
2
 
3
3
  import datetime
4
4
  import io
5
+ import ipaddress
5
6
  import logging
6
7
  import os
7
8
  import platform
8
9
  import random
10
+ import urllib.parse
9
11
  import uuid
10
12
  from collections import OrderedDict
11
13
  from enum import Enum
@@ -164,9 +166,9 @@ def get_class_by_name(name: str) -> object:
164
166
  class timer:
165
167
  """Context manager to log time taken for a block of code to run"""
166
168
 
167
- def __init__(self, message: str, logger: logging.Logger, device: torch.device = None):
169
+ def __init__(self, message: str, logger: logging.Logger, device: torch.device = None, log_level=logging.DEBUG):
168
170
  self.message = message
169
- self.logger = logger
171
+ self.logger = logger.debug if log_level == logging.DEBUG else logger.info
170
172
  self.device = device
171
173
 
172
174
  def __enter__(self):
@@ -176,9 +178,9 @@ class timer:
176
178
  def __exit__(self, *_):
177
179
  elapsed = perf_counter() - self.start
178
180
  if self.device is None:
179
- self.logger.debug(f"{self.message}: {elapsed:.3f} seconds")
181
+ self.logger(f"{self.message}: {elapsed:.3f} seconds")
180
182
  else:
181
- self.logger.debug(f"{self.message}: {elapsed:.3f} seconds on device: {self.device}")
183
+ self.logger(f"{self.message}: {elapsed:.3f} seconds on device: {self.device}")
182
184
 
183
185
 
184
186
  class LRU(OrderedDict):
@@ -321,11 +323,19 @@ command_descriptions = {
321
323
  ConversationCommand.Notes: "Only talk about information that is available in your knowledge base.",
322
324
  ConversationCommand.Default: "The default command when no command specified. It intelligently auto-switches between general and notes mode.",
323
325
  ConversationCommand.Online: "Search for information on the internet.",
324
- ConversationCommand.Webpage: "Get information from webpage links provided by you.",
326
+ ConversationCommand.Webpage: "Get information from webpage suggested by you.",
325
327
  ConversationCommand.Image: "Generate images by describing your imagination in words.",
326
328
  ConversationCommand.Automation: "Automatically run your query at a specified time or interval.",
327
329
  ConversationCommand.Help: "Get help with how to use or setup Khoj from the documentation",
328
- ConversationCommand.Summarize: "Create an appropriate summary using provided documents.",
330
+ ConversationCommand.Summarize: "Get help with a question pertaining to an entire document.",
331
+ }
332
+
333
+ command_descriptions_for_agent = {
334
+ ConversationCommand.General: "Agent can use the agents knowledge base and general knowledge.",
335
+ ConversationCommand.Notes: "Agent can search the users knowledge base for information.",
336
+ ConversationCommand.Online: "Agent can search the internet for information.",
337
+ ConversationCommand.Webpage: "Agent can read suggested web pages for information.",
338
+ ConversationCommand.Summarize: "Agent can read an entire document. Agents knowledge base must be a single document.",
329
339
  }
330
340
 
331
341
  tool_descriptions_for_llm = {
@@ -334,15 +344,21 @@ tool_descriptions_for_llm = {
334
344
  ConversationCommand.Notes: "To search the user's personal knowledge base. Especially helpful if the question expects context from the user's notes or documents.",
335
345
  ConversationCommand.Online: "To search for the latest, up-to-date information from the internet. Note: **Questions about Khoj should always use this data source**",
336
346
  ConversationCommand.Webpage: "To use if the user has directly provided the webpage urls or you are certain of the webpage urls to read.",
337
- ConversationCommand.Summarize: "To create a summary of the document provided by the user.",
347
+ ConversationCommand.Summarize: "To retrieve an answer that depends on the entire document or a large text.",
338
348
  }
339
349
 
340
350
  mode_descriptions_for_llm = {
341
351
  ConversationCommand.Image: "Use this if the user is requesting you to generate a picture based on their description.",
342
- ConversationCommand.Automation: "Use this if the user is requesting a response at a scheduled date or time.",
352
+ ConversationCommand.Automation: "Use this if you are confident the user is requesting a response at a scheduled date, time and frequency",
343
353
  ConversationCommand.Text: "Use this if the other response modes don't seem to fit the query.",
344
354
  }
345
355
 
356
+ mode_descriptions_for_agent = {
357
+ ConversationCommand.Image: "Agent can generate image in response.",
358
+ ConversationCommand.Automation: "Agent can schedule a task to run at a scheduled date, time and frequency in response.",
359
+ ConversationCommand.Text: "Agent can generate text in response.",
360
+ }
361
+
346
362
 
347
363
  class ImageIntentType(Enum):
348
364
  """
@@ -422,6 +438,46 @@ def is_internet_connected():
422
438
  return False
423
439
 
424
440
 
441
+ def is_internal_url(url: str) -> bool:
442
+ """
443
+ Check if a URL is likely to be internal/non-public.
444
+
445
+ Args:
446
+ url (str): The URL to check.
447
+
448
+ Returns:
449
+ bool: True if the URL is likely internal, False otherwise.
450
+ """
451
+ try:
452
+ parsed_url = urllib.parse.urlparse(url)
453
+ hostname = parsed_url.hostname
454
+
455
+ # Check for localhost
456
+ if hostname in ["localhost", "127.0.0.1", "::1"]:
457
+ return True
458
+
459
+ # Check for IP addresses in private ranges
460
+ try:
461
+ ip = ipaddress.ip_address(hostname)
462
+ return ip.is_private
463
+ except ValueError:
464
+ pass # Not an IP address, continue with other checks
465
+
466
+ # Check for common internal TLDs
467
+ internal_tlds = [".local", ".internal", ".private", ".corp", ".home", ".lan"]
468
+ if any(hostname.endswith(tld) for tld in internal_tlds):
469
+ return True
470
+
471
+ # Check for URLs without a TLD
472
+ if "." not in hostname:
473
+ return True
474
+
475
+ return False
476
+ except Exception:
477
+ # If we can't parse the URL or something else goes wrong, assume it's not internal
478
+ return False
479
+
480
+
425
481
  def convert_image_to_webp(image_bytes):
426
482
  """Convert image bytes to webp format for faster loading"""
427
483
  image_io = io.BytesIO(image_bytes)
@@ -129,9 +129,6 @@ def initialization(interactive: bool = True):
129
129
  if user_chat_model_name and ChatModelOptions.objects.filter(chat_model=user_chat_model_name).exists():
130
130
  default_chat_model_name = user_chat_model_name
131
131
 
132
- # Create a server chat settings object with the default chat model
133
- default_chat_model = ChatModelOptions.objects.filter(chat_model=default_chat_model_name).first()
134
- ServerChatSettings.objects.create(chat_default=default_chat_model)
135
132
  logger.info("🗣️ Chat model configuration complete")
136
133
 
137
134
  # Set up offline speech to text model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: khoj
3
- Version: 1.24.2.dev3
3
+ Version: 1.25.1.dev34
4
4
  Summary: Your Second Brain
5
5
  Project-URL: Homepage, https://khoj.dev
6
6
  Project-URL: Documentation, https://docs.khoj.dev
@@ -32,7 +32,7 @@ Requires-Dist: dateparser>=1.1.1
32
32
  Requires-Dist: defusedxml==0.7.1
33
33
  Requires-Dist: django-apscheduler==0.6.2
34
34
  Requires-Dist: django-phonenumber-field==7.3.0
35
- Requires-Dist: django==5.0.8
35
+ Requires-Dist: django==5.0.9
36
36
  Requires-Dist: docx2txt==0.8
37
37
  Requires-Dist: einops==0.8.0
38
38
  Requires-Dist: fastapi>=0.110.0
@@ -103,14 +103,14 @@ Description-Content-Type: text/markdown
103
103
  <div align="center">
104
104
 
105
105
  [![test](https://github.com/khoj-ai/khoj/actions/workflows/test.yml/badge.svg)](https://github.com/khoj-ai/khoj/actions/workflows/test.yml)
106
- [![dockerize](https://github.com/khoj-ai/khoj/actions/workflows/dockerize.yml/badge.svg)](https://github.com/khoj-ai/khoj/pkgs/container/khoj)
106
+ [![docker](https://github.com/khoj-ai/khoj/actions/workflows/dockerize.yml/badge.svg)](https://github.com/khoj-ai/khoj/pkgs/container/khoj)
107
107
  [![pypi](https://github.com/khoj-ai/khoj/actions/workflows/pypi.yml/badge.svg)](https://pypi.org/project/khoj/)
108
- ![Discord](https://img.shields.io/discord/1112065956647284756?style=plastic&label=discord)
108
+ [![discord](https://img.shields.io/discord/1112065956647284756?style=plastic&label=discord)](https://discord.gg/BDgyabRM6e)
109
109
 
110
110
  </div>
111
111
 
112
112
  <div align="center">
113
- <b>The open-source, personal AI for your digital brain</b>
113
+ <b>Your AI second brain</b>
114
114
  </div>
115
115
 
116
116
  <br />
@@ -119,11 +119,13 @@ Description-Content-Type: text/markdown
119
119
 
120
120
  [📑 Docs](https://docs.khoj.dev)
121
121
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
122
- [🏮 App](https://khoj.dev)
122
+ [🌐 Web](https://khoj.dev)
123
+ <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
124
+ [🔥 App](https://app.khoj.dev)
123
125
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
124
126
  [💬 Discord](https://discord.gg/BDgyabRM6e)
125
127
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
126
- [📚 Blog](https://blog.khoj.dev)
128
+ [✍🏽 Blog](https://blog.khoj.dev)
127
129
 
128
130
  </div>
129
131
 
@@ -131,14 +133,17 @@ Description-Content-Type: text/markdown
131
133
 
132
134
  ***
133
135
 
134
- [Khoj](https://khoj.dev) is a personal, open-source AI application for you to extend your capabilities.
135
- - Share your documents to extend your digital brain.
136
- - Access the internet, getting fresh information.
137
- - You can share pdf, markdown, org-mode, notion files and github repositories.
138
- - Fast, accurate semantic search on top of your docs.
139
- - Create images, talk out loud, play your messages.
140
- - Available Desktop, Emacs, Obsidian, Web and Whatsapp.
136
+ [Khoj](https://khoj.dev) is a personal AI app to extend your capabilities. It smoothly scales up from an on-device personal AI to a cloud-scale enterprise AI.
137
+
138
+ - Chat with any local or online LLM (e.g llama3, qwen, gemma, mistral, gpt, claude, gemini).
139
+ - Get answers from the internet and your docs (including image, pdf, markdown, org-mode, word, notion files).
140
+ - Access it from your Browser, Obsidian, Emacs, Desktop, Phone or Whatsapp.
141
+ - Create agents with custom knowledge, persona, chat model and tools to take on any role.
142
+ - Automate away repetitive research. Get personal newsletters and smart notifications delivered to your inbox.
143
+ - Find relevant docs quickly and easily using our advanced semantic search.
144
+ - Generate images, talk out loud, play your messages.
141
145
  - Khoj is open-source, self-hostable. Always.
146
+ - Run it privately on [your computer](https://docs.khoj.dev/get-started/setup) or try it on our [cloud app](https://app.khoj.dev).
142
147
 
143
148
  ***
144
149
 
@@ -169,10 +174,3 @@ Made with [contrib.rocks](https://contrib.rocks).
169
174
  ### Interested in Contributing?
170
175
 
171
176
  We are always looking for contributors to help us build new features, improve the project documentation, or fix bugs. If you're interested, please see our [Contributing Guidelines](https://docs.khoj.dev/contributing/development) and check out our [Contributors Project Board](https://github.com/orgs/khoj-ai/projects/4).
172
-
173
- ## [Sponsors](https://github.com/sponsors/khoj-ai)
174
- Shout out to our brilliant sponsors! 🌈
175
-
176
- <a href="http://github.com/beekeeb">
177
- <img src="https://raw.githubusercontent.com/beekeeb/piantor/main/docs/beekeeb.png" width=250/>
178
- </a>