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.
- khoj/configure.py +13 -4
- khoj/database/adapters/__init__.py +289 -52
- khoj/database/admin.py +20 -1
- khoj/database/migrations/0065_remove_agent_avatar_remove_agent_public_and_more.py +49 -0
- khoj/database/migrations/0066_remove_agent_tools_agent_input_tools_and_more.py +69 -0
- khoj/database/migrations/0067_alter_agent_style_icon.py +50 -0
- khoj/database/migrations/0068_alter_agent_output_modes.py +24 -0
- khoj/database/migrations/0069_webscraper_serverchatsettings_web_scraper.py +89 -0
- khoj/database/models/__init__.py +136 -18
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1603-fa3ee48860b9dc5c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/2697-a38d01981ad3bdf8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/3110-ef2cacd1b8d79ad8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/4086-2c74808ba38a5a0f.js +1 -0
- khoj/interface/compiled/_next/static/chunks/477-ec86e93db10571c1.js +1 -0
- khoj/interface/compiled/_next/static/chunks/51-e8f5bdb69b5ea421.js +1 -0
- khoj/interface/compiled/_next/static/chunks/7762-79f2205740622b5c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9178-899fe9a6b754ecfe.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9417-29502e39c3e7d60c.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9479-7eed36fc954ef804.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/{layout-e71c8e913cccf792.js → layout-75636ab3a413fa8e.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-fa282831808ee536.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/page-5480731341f34450.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/chat/{layout-8102549127db3067.js → layout-96fcf62857bf8f30.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-702057ccbcf27881.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-e7b34316ec6f44de.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/{layout-f3e40d346da53112.js → layout-d0f0a9067427fb20.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/page-10a5aad6e04f3cf8.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/search/page-d56541c746fded7d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/{layout-6f9314b0d7a26046.js → layout-a8f33dfe92f997fb.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-e044a999468a7c5d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/{layout-39f03f9e32399f0f.js → layout-2df56074e42adaa0.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-fbbd66a4d4633438.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{webpack-d4781cada9b58e75.js → webpack-c0cd5a6afb1f0798.js} +1 -1
- khoj/interface/compiled/_next/static/css/2de69f0be774c768.css +1 -0
- khoj/interface/compiled/_next/static/css/467a524c75e7d7c0.css +1 -0
- khoj/interface/compiled/_next/static/css/592ca99f5122e75a.css +1 -0
- khoj/interface/compiled/_next/static/css/b9a6bf04305d98d7.css +25 -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 +3 -3
- khoj/interface/compiled/share/chat/index.html +1 -1
- khoj/interface/compiled/share/chat/index.txt +2 -2
- khoj/interface/web/assets/icons/agents.svg +1 -0
- khoj/interface/web/assets/icons/automation.svg +1 -0
- khoj/interface/web/assets/icons/chat.svg +24 -0
- khoj/interface/web/login.html +11 -22
- khoj/processor/content/notion/notion_to_entries.py +2 -1
- khoj/processor/conversation/anthropic/anthropic_chat.py +2 -0
- khoj/processor/conversation/google/gemini_chat.py +6 -19
- khoj/processor/conversation/google/utils.py +33 -15
- khoj/processor/conversation/offline/chat_model.py +3 -1
- khoj/processor/conversation/openai/gpt.py +2 -0
- khoj/processor/conversation/prompts.py +67 -5
- khoj/processor/conversation/utils.py +3 -7
- khoj/processor/embeddings.py +6 -3
- khoj/processor/image/generate.py +4 -3
- khoj/processor/tools/online_search.py +139 -44
- khoj/routers/api.py +35 -6
- khoj/routers/api_agents.py +235 -4
- khoj/routers/api_chat.py +102 -530
- khoj/routers/api_content.py +14 -0
- khoj/routers/api_model.py +1 -1
- khoj/routers/auth.py +9 -1
- khoj/routers/helpers.py +181 -68
- khoj/routers/subscription.py +18 -4
- khoj/search_type/text_search.py +11 -3
- khoj/utils/helpers.py +64 -8
- khoj/utils/initialization.py +0 -3
- {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/METADATA +19 -21
- {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/RECORD +87 -81
- khoj/interface/compiled/_next/static/chunks/1603-3e2e1528e3b6ea1d.js +0 -1
- khoj/interface/compiled/_next/static/chunks/2697-a29cb9191a9e339c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/6648-ee109f4ea33a74e2.js +0 -1
- khoj/interface/compiled/_next/static/chunks/7071-b4711cecca6619a8.js +0 -1
- khoj/interface/compiled/_next/static/chunks/743-1a64254447cda71f.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9162-0be016519a18568b.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9178-7e815211edcb3657.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9417-5d14ac74aaab2c66.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9984-e410179c6fac7cf1.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-d302911777a3e027.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/automations/page-0a5de8c254c29a1c.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-d96bf6a84bb05290.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/factchecker/page-32e61af29e6b431d.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/page-96cab08c985716f4.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-b3193d46c65571c5.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-0db9b708366606ec.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-f06ac16cfe5b5a16.js +0 -1
- khoj/interface/compiled/_next/static/css/1538cedb321e3a97.css +0 -1
- khoj/interface/compiled/_next/static/css/24f141a6e37cd204.css +0 -25
- khoj/interface/compiled/_next/static/css/4cae6c0e5c72fb2d.css +0 -1
- khoj/interface/compiled/_next/static/css/f768dddada62459d.css +0 -1
- /khoj/interface/compiled/_next/static/{_29ceahp81LhuIHo5QgOD → Jid9q6Qg851ioDaaO_fth}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{_29ceahp81LhuIHo5QgOD → Jid9q6Qg851ioDaaO_fth}/_ssgManifest.js +0 -0
- {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/WHEEL +0 -0
- {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/entry_points.txt +0 -0
- {khoj-1.24.2.dev3.dist-info → khoj-1.25.1.dev34.dist-info}/licenses/LICENSE +0 -0
khoj/routers/subscription.py
CHANGED
@@ -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(
|
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
|
-
|
67
|
-
success =
|
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
|
|
khoj/search_type/text_search.py
CHANGED
@@ -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
|
-
|
232
|
-
|
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
|
181
|
+
self.logger(f"{self.message}: {elapsed:.3f} seconds")
|
180
182
|
else:
|
181
|
-
self.logger
|
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
|
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: "
|
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
|
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
|
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)
|
khoj/utils/initialization.py
CHANGED
@@ -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.
|
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.
|
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
|
[](https://github.com/khoj-ai/khoj/actions/workflows/test.yml)
|
106
|
-
[](https://github.com/khoj-ai/khoj/pkgs/container/khoj)
|
107
107
|
[](https://pypi.org/project/khoj/)
|
108
|
-
](https://discord.gg/BDgyabRM6e)
|
109
109
|
|
110
110
|
</div>
|
111
111
|
|
112
112
|
<div align="center">
|
113
|
-
<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> • </span>
|
122
|
-
[
|
122
|
+
[🌐 Web](https://khoj.dev)
|
123
|
+
<span> • </span>
|
124
|
+
[🔥 App](https://app.khoj.dev)
|
123
125
|
<span> • </span>
|
124
126
|
[💬 Discord](https://discord.gg/BDgyabRM6e)
|
125
127
|
<span> • </span>
|
126
|
-
[
|
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
|
135
|
-
|
136
|
-
-
|
137
|
-
-
|
138
|
-
-
|
139
|
-
- Create
|
140
|
-
-
|
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>
|