khoj 1.23.1.dev1__py3-none-any.whl → 1.23.4.dev1__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 +8 -8
- khoj/database/migrations/0063_conversation_temp_id.py +36 -0
- khoj/database/migrations/0064_remove_conversation_temp_id_alter_conversation_id.py +86 -0
- khoj/database/models/__init__.py +1 -0
- khoj/interface/compiled/404/index.html +1 -1
- khoj/interface/compiled/_next/static/chunks/1603-d643510c2c0b8871.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{1906-1747a36c336df02c.js → 4051-3dc2df557ccb5213.js} +2 -2
- khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +1 -0
- khoj/interface/compiled/_next/static/chunks/9178-421e47df97ff0213.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/agents/page-f8ad4d2944dbcf91.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/automations/{page-3f4b6ff0261e19b7.js → page-cc875a656df43713.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-3e75b0e0aa3aaaf5.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/factchecker/{page-828cf3c5b8e3af79.js → page-bb320ff7d4dee716.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/{page-2f423a763e2ee0c7.js → page-60193524cf570002.js} +1 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-4bceb5b0df9cfd66.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/settings/page-532ed8b778a0b40d.js +1 -0
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-b5e63aabfd573dba.js +1 -0
- khoj/interface/compiled/_next/static/chunks/{webpack-61a553b6ff44f97c.js → webpack-de28762883e5816d.js} +1 -1
- khoj/interface/compiled/_next/static/css/1105696872e3f20c.css +25 -0
- khoj/interface/compiled/_next/static/css/37a313cb39403a84.css +1 -0
- khoj/interface/compiled/_next/static/css/6bde1f2045622ef7.css +1 -0
- khoj/interface/compiled/_next/static/css/{a3530ec58b0b660f.css → ab57702ed2b98214.css} +1 -1
- khoj/interface/compiled/_next/static/css/{92c48eece0b102b9.css → e41ec62af8ee4e38.css} +1 -1
- 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/interface/email/magic_link.html +1 -1
- khoj/interface/email/task.html +31 -34
- khoj/interface/email/welcome.html +82 -53
- khoj/main.py +1 -1
- khoj/processor/content/images/image_to_entries.py +6 -4
- khoj/processor/conversation/google/utils.py +106 -7
- khoj/processor/conversation/utils.py +13 -8
- khoj/routers/api.py +1 -1
- khoj/routers/api_chat.py +13 -15
- khoj/routers/helpers.py +12 -7
- khoj/utils/cli.py +6 -0
- khoj/utils/constants.py +9 -2
- khoj/utils/initialization.py +158 -71
- {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/METADATA +2 -2
- {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/RECORD +60 -58
- khoj/interface/compiled/_next/static/chunks/1603-fb2d80ae73990df3.js +0 -1
- khoj/interface/compiled/_next/static/chunks/8423-14fc72aec9104ce9.js +0 -1
- khoj/interface/compiled/_next/static/chunks/9178-c153fc402c970365.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/agents/page-989a824c640bc532.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/chat/page-cc71b18feddf80d6.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/search/page-dcd385f03255ef36.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/settings/page-ddcd51147d18c694.js +0 -1
- khoj/interface/compiled/_next/static/chunks/app/share/chat/page-a84001b4724b5463.js +0 -1
- khoj/interface/compiled/_next/static/css/2272c73fc7a3b571.css +0 -1
- khoj/interface/compiled/_next/static/css/3e49e5ee49c6bda1.css +0 -25
- khoj/interface/compiled/_next/static/css/553f9cdcc7a2bcd6.css +0 -1
- /khoj/interface/compiled/_next/static/chunks/{7023-52c1be60135eb057.js → 7023-1074a582ec989284.js} +0 -0
- /khoj/interface/compiled/_next/static/{A47_BQNcqhjWCuNh_iSec → u496AO3jlFBPp2apVnMkP}/_buildManifest.js +0 -0
- /khoj/interface/compiled/_next/static/{A47_BQNcqhjWCuNh_iSec → u496AO3jlFBPp2apVnMkP}/_ssgManifest.js +0 -0
- {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/WHEEL +0 -0
- {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/entry_points.txt +0 -0
- {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/licenses/LICENSE +0 -0
khoj/interface/email/task.html
CHANGED
@@ -1,40 +1,37 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
<
|
7
|
-
|
8
|
-
<
|
9
|
-
<
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
<div>
|
14
|
-
<h1 style="color: #333; font-size: large; font-weight: bold; margin: 0; line-height: 1.5; background-color: #fee285; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.5);">Your Automation, From Your Personal AI (Preview)</h1>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Khoj AI - Automation</title>
|
7
|
+
</head>
|
8
|
+
<body style="font-family: 'Arial', sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f5f5f5;">
|
9
|
+
<div style="background-color: #ffffff; border-radius: 10px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); padding: 30px;">
|
10
|
+
<a href="https://khoj.dev" target="_blank" style="display: block; text-align: center; margin-bottom: 20px; text-decoration: none;">
|
11
|
+
<img src="https://assets.khoj.dev/khoj_logo.png" alt="Khoj Logo" style="width: 120px;">
|
12
|
+
</a>
|
15
13
|
|
16
|
-
<
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
<h1 style="font-size: 24px; color: #2c3e50; margin-bottom: 20px; text-align: center; border-bottom: 2px solid #FFA07A; padding-bottom: 10px;">Your Automation, From Your Personal AI</h1>
|
15
|
+
|
16
|
+
<div style="background-color: #f8f9fa; border-left: 4px solid #FFA07A; padding: 15px; margin-bottom: 20px;">
|
17
|
+
<h3 style="color: #2c3e50; margin-top: 0;">{{ subject }}</h3>
|
18
|
+
<p style="margin-bottom: 0;">{{ result }}</p>
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div style="font-size: 14px; color: #666; margin-bottom: 20px;">
|
22
|
+
<p>The automation I ran on your behalf: {{ query }}</p>
|
23
|
+
<p>You can manage your automations via <a href="https://app.khoj.dev/automations" style="color: #007bff; text-decoration: none;">the settings page</a>.</p>
|
24
|
+
<p>This is an experimental feature. Please share any feedback with <a href="mailto:team@khoj.dev" style="color: #007bff; text-decoration: none;">team@khoj.dev</a>.</p>
|
23
25
|
</div>
|
24
|
-
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">The automation I ran on your behalf: {{query}}</p>
|
25
|
-
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">You can manage your automations via <a href="https://app.khoj.dev/automations">the settings page</a>.</p>
|
26
|
-
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">This is an experimental feature. Please share any feedback with <a href="mailto:team@khoj.dev">team@khoj.dev</a>.</p>
|
27
|
-
</div>
|
28
|
-
</div>
|
29
|
-
<p style="color: #333; font-size: large; margin-top: 20px; padding: 0; line-height: 1.5;">- Khoj</p>
|
30
|
-
<table style="width: 100%; margin-top: 20px;">
|
31
|
-
<tr>
|
32
|
-
<td style="text-align: center;"><a href="https://docs.khoj.dev" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">Docs</a></td>
|
33
|
-
<td style="text-align: center;"><a href="https://github.com/khoj-ai/khoj" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">GitHub</a></td>
|
34
|
-
<td style="text-align: center;"><a href="https://twitter.com/khoj_ai" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">Twitter</a></td>
|
35
|
-
<td style="text-align: center;"><a href="https://www.linkedin.com/company/khoj-ai" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">LinkedIn</a></td>
|
36
|
-
</tr>
|
37
|
-
</table>
|
38
26
|
|
27
|
+
<div style="font-size: 18px; font-weight: bold; margin-top: 30px; text-align: right;">- Khoj</div>
|
28
|
+
|
29
|
+
<div style="margin-top: 30px; text-align: center;">
|
30
|
+
<a href="https://docs.khoj.dev" target="_blank" style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #333; text-decoration: none; border-radius: 5px;">Docs</a>
|
31
|
+
<a href="https://github.com/khoj-ai/khoj" target="_blank" style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #333; text-decoration: none; border-radius: 5px;">GitHub</a>
|
32
|
+
<a href="https://twitter.com/khoj_ai" target="_blank" style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #333; text-decoration: none; border-radius: 5px;">Twitter</a>
|
33
|
+
<a href="https://www.linkedin.com/company/khoj-ai" target="_blank" style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #333; text-decoration: none; border-radius: 5px;">LinkedIn</a>
|
34
|
+
</div>
|
35
|
+
</div>
|
39
36
|
</body>
|
40
37
|
</html>
|
@@ -1,61 +1,90 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
<
|
7
|
-
<
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
<div
|
13
|
-
|
14
|
-
<
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
<
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7
|
+
<title>Welcome to Khoj</title>
|
8
|
+
</head>
|
9
|
+
|
10
|
+
<body
|
11
|
+
style="font-family: 'Arial', sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f5f5f5;">
|
12
|
+
<div
|
13
|
+
style="background-color: #ffffff; border-radius: 10px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); padding: 30px;">
|
14
|
+
<a href="https://khoj.dev" target="_blank"
|
15
|
+
style="display: block; text-align: center; margin-bottom: 20px; text-decoration: none;">
|
16
|
+
<img src="https://assets.khoj.dev/khoj_logo.png" alt="Khoj Logo" style="width: 120px;">
|
17
|
+
</a>
|
18
|
+
|
19
|
+
<h1
|
20
|
+
style="font-size: 24px; color: #2c3e50; margin-bottom: 20px; text-align: center; border-bottom: 2px solid #FFA07A; padding-bottom: 10px;">
|
21
|
+
Merge AI with your brain</h1>
|
22
|
+
|
23
|
+
<p style="font-size: 16px; color: #333; margin-bottom: 20px;">Hi {{name}}! We are psyched to be part of your
|
24
|
+
journey with personal AI. To better help you, we're committed to staying transparent, accessible, and
|
25
|
+
completely open-source.</p>
|
26
|
+
|
27
|
+
<a href="https://app.khoj.dev" target="_blank"
|
28
|
+
style="display: block; width: 200px; text-align: center; padding: 10px; margin: 20px auto; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px; font-weight: bold; font-size: 16px; text-transform: uppercase;">Get
|
29
|
+
Started</a>
|
30
|
+
|
31
|
+
<p style="font-size: 16px; color: #333; margin-bottom: 20px;">You're about to get a whole lot more productive.
|
32
|
+
</p>
|
33
|
+
<a href="https://docs.khoj.dev/features/online_search"
|
34
|
+
style="color: #FFA07A; text-decoration: none; font-weight: bold; font-size: 14px;">
|
35
|
+
<div style="display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px; margin-bottom: 20px;">
|
36
|
+
<div style="background-color: #f8f9fa; border-left: 4px solid #FFA07A; padding: 15px;">
|
37
|
+
<h3 style="color: #2c3e50; margin-top: 0; font-size: 18px;">Ditch the search bar</h3>
|
38
|
+
<p style="font-size: 14px; color: #666; margin-bottom: 0;">You don't need to click around Google
|
39
|
+
results
|
40
|
+
and sift through information yourself, because Khoj is connected to the internet.</p>
|
41
|
+
</div>
|
42
|
+
</a>
|
43
|
+
<a href="https://app.khoj.dev/agents"
|
44
|
+
style="color: #FFA07A; text-decoration: none; font-weight: bold; font-size: 14px;">
|
45
|
+
<div style="background-color: #f8f9fa; border-left: 4px solid #FFA07A; padding: 15px;">
|
46
|
+
<h3 style="color: #2c3e50; margin-top: 0; font-size: 18px;">Get a village, not just an agent</h3>
|
47
|
+
<p style="font-size: 14px; color: #666; margin-bottom: 0;">Khoj can fill the need for more specialized
|
48
|
+
assistance, such as tutoring, with its curated agents. You get a whole team, always available.</p>
|
31
49
|
</div>
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
<
|
50
|
+
</a>
|
51
|
+
<a href="https://docs.khoj.dev/category/clients"
|
52
|
+
style="color: #FFA07A; text-decoration: none; font-weight: bold; font-size: 14px;">
|
53
|
+
<div style="background-color: #f8f9fa; border-left: 4px solid #FFA07A; padding: 15px;">
|
54
|
+
<h3 style="color: #2c3e50; margin-top: 0; font-size: 18px;">Activate your data</h3>
|
55
|
+
<p style="font-size: 14px; color: #666; margin-bottom: 0;">Build on top of your digital brain. Khoj
|
56
|
+
stores whatever data you share with it, so you can get answers from your personal notes and
|
57
|
+
documents in your native language.</p>
|
37
58
|
</div>
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
<
|
59
|
+
</a>
|
60
|
+
<a href="https://blog.khoj.dev/posts/how-khoj-generates-images/"
|
61
|
+
style="color: #FFA07A; text-decoration: none; font-weight: bold; font-size: 14px;">
|
62
|
+
<div style="background-color: #f8f9fa; border-left: 4px solid #FFA07A; padding: 15px;">
|
63
|
+
<h3 style="color: #2c3e50; margin-top: 0; font-size: 18px;">Create rich, contextual images</h3>
|
64
|
+
<p style="font-size: 14px; color: #666; margin-bottom: 0;">With your shared data, Khoj can help you
|
65
|
+
create astoundingly personal images depicting scenes of what's important to you.</p>
|
43
66
|
</div>
|
44
|
-
</
|
67
|
+
</a>
|
45
68
|
</div>
|
46
|
-
</div>
|
47
|
-
<p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">Like something? Dislike something? Searching for some other magical feature? Our inbox is always open for feedback! Reply to this email and say hi to introduce yourself 👋🏽.</p>
|
48
|
-
|
49
|
-
<p style="color: #333; font-size: large; margin-top: 20px; padding: 0; line-height: 1.5;">- The Khoj Team</p>
|
50
|
-
<table style="width: 100%; margin-top: 20px;">
|
51
|
-
<tr>
|
52
|
-
<td style="text-align: center;"><a href="https://docs.khoj.dev" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">Docs</a></td>
|
53
|
-
<td style="text-align: center;"><a href="https://github.com/khoj-ai/khoj" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">GitHub</a></td>
|
54
|
-
<td style="text-align: center;"><a href="https://twitter.com/khoj_ai" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">Twitter</a></td>
|
55
|
-
<td style="text-align: center;"><a href="https://www.linkedin.com/company/khoj-ai" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">LinkedIn</a></td>
|
56
|
-
<td style="text-align: center;"><a href="https://discord.gg/BDgyabRM6e" target="_blank" style="padding: 8px; color: #333; background-color: #fee285; border-radius: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0);">Discord</a></td>
|
57
|
-
</tr>
|
58
|
-
</table>
|
59
69
|
|
70
|
+
<p style="font-size: 16px; color: #333; margin-bottom: 20px;">Like something? Dislike something? Searching for
|
71
|
+
some other magical feature? Our inbox is always open for feedback! Reply to this email and say hi to
|
72
|
+
introduce yourself 👋🏽.</p>
|
73
|
+
<div style="font-size: 18px; font-weight: bold; margin-top: 30px; text-align: right;">- The Khoj Team</div>
|
74
|
+
|
75
|
+
<div style="margin-top: 30px; text-align: center;">
|
76
|
+
<a href="https://docs.khoj.dev" target="_blank"
|
77
|
+
style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px;">Docs</a>
|
78
|
+
<a href="https://github.com/khoj-ai/khoj" target="_blank"
|
79
|
+
style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px;">GitHub</a>
|
80
|
+
<a href="https://twitter.com/khoj_ai" target="_blank"
|
81
|
+
style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px;">Twitter</a>
|
82
|
+
<a href="https://www.linkedin.com/company/khoj-ai" target="_blank"
|
83
|
+
style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px;">LinkedIn</a>
|
84
|
+
<a href="https://discord.gg/BDgyabRM6e" target="_blank"
|
85
|
+
style="display: inline-block; margin: 0 10px; padding: 8px 15px; background-color: #FFA07A; color: #ffffff; text-decoration: none; border-radius: 5px;">Discord</a>
|
86
|
+
</div>
|
87
|
+
</div>
|
60
88
|
</body>
|
89
|
+
|
61
90
|
</html>
|
khoj/main.py
CHANGED
@@ -131,7 +131,7 @@ def run(should_start_server=True):
|
|
131
131
|
logger.info(f"📦 Initializing DB:\n{db_migrate_output.getvalue().strip()}")
|
132
132
|
logger.debug(f"🌍 Initializing Web Client:\n{collectstatic_output.getvalue().strip()}")
|
133
133
|
|
134
|
-
initialization()
|
134
|
+
initialization(not args.non_interactive)
|
135
135
|
|
136
136
|
# Create app directory, if it doesn't exist
|
137
137
|
state.config_file.parent.mkdir(parents=True, exist_ok=True)
|
@@ -4,8 +4,6 @@ import os
|
|
4
4
|
from datetime import datetime
|
5
5
|
from typing import Dict, List, Tuple
|
6
6
|
|
7
|
-
from rapidocr_onnxruntime import RapidOCR
|
8
|
-
|
9
7
|
from khoj.database.models import Entry as DbEntry
|
10
8
|
from khoj.database.models import KhojUser
|
11
9
|
from khoj.processor.content.text_to_entries import TextToEntries
|
@@ -58,7 +56,6 @@ class ImageToEntries(TextToEntries):
|
|
58
56
|
entry_to_location_map: List[Tuple[str, str]] = []
|
59
57
|
for image_file in image_files:
|
60
58
|
try:
|
61
|
-
loader = RapidOCR()
|
62
59
|
bytes = image_files[image_file]
|
63
60
|
# write the image to a temporary file
|
64
61
|
timestamp_now = datetime.utcnow().timestamp()
|
@@ -71,13 +68,18 @@ class ImageToEntries(TextToEntries):
|
|
71
68
|
bytes = image_files[image_file]
|
72
69
|
f.write(bytes)
|
73
70
|
try:
|
71
|
+
from rapidocr_onnxruntime import RapidOCR
|
72
|
+
|
73
|
+
loader = RapidOCR()
|
74
74
|
image_entries_per_file = ""
|
75
75
|
result, _ = loader(tmp_file)
|
76
76
|
if result:
|
77
77
|
expanded_entries = [text[1] for text in result]
|
78
78
|
image_entries_per_file = " ".join(expanded_entries)
|
79
79
|
except ImportError:
|
80
|
-
logger.warning(
|
80
|
+
logger.warning(
|
81
|
+
f"Unable to process image or scanned file for text: {image_file}. This file will not be indexed."
|
82
|
+
)
|
81
83
|
continue
|
82
84
|
entry_to_location_map.append((image_entries_per_file, image_file))
|
83
85
|
entries.extend([image_entries_per_file])
|
@@ -1,7 +1,18 @@
|
|
1
1
|
import logging
|
2
|
+
import random
|
2
3
|
from threading import Thread
|
3
4
|
|
4
5
|
import google.generativeai as genai
|
6
|
+
from google.generativeai.types.answer_types import FinishReason
|
7
|
+
from google.generativeai.types.generation_types import (
|
8
|
+
GenerateContentResponse,
|
9
|
+
StopCandidateException,
|
10
|
+
)
|
11
|
+
from google.generativeai.types.safety_types import (
|
12
|
+
HarmBlockThreshold,
|
13
|
+
HarmCategory,
|
14
|
+
HarmProbability,
|
15
|
+
)
|
5
16
|
from tenacity import (
|
6
17
|
before_sleep_log,
|
7
18
|
retry,
|
@@ -32,14 +43,35 @@ def gemini_completion_with_backoff(
|
|
32
43
|
model_kwargs = model_kwargs or dict()
|
33
44
|
model_kwargs["temperature"] = temperature
|
34
45
|
model_kwargs["max_output_tokens"] = max_tokens
|
35
|
-
model = genai.GenerativeModel(
|
46
|
+
model = genai.GenerativeModel(
|
47
|
+
model_name,
|
48
|
+
generation_config=model_kwargs,
|
49
|
+
system_instruction=system_prompt,
|
50
|
+
safety_settings={
|
51
|
+
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
52
|
+
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
53
|
+
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
54
|
+
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
55
|
+
},
|
56
|
+
)
|
36
57
|
|
37
58
|
formatted_messages = [{"role": message.role, "parts": [message.content]} for message in messages]
|
38
|
-
|
59
|
+
|
60
|
+
# Start chat session. All messages up to the last are considered to be part of the chat history
|
39
61
|
chat_session = model.start_chat(history=formatted_messages[0:-1])
|
40
|
-
|
41
|
-
|
42
|
-
|
62
|
+
|
63
|
+
try:
|
64
|
+
# Generate the response. The last message is considered to be the current prompt
|
65
|
+
aggregated_response = chat_session.send_message(formatted_messages[-1]["parts"][0])
|
66
|
+
return aggregated_response.text
|
67
|
+
except StopCandidateException as e:
|
68
|
+
response_message, _ = handle_gemini_response(e.args)
|
69
|
+
# Respond with reason for stopping
|
70
|
+
logger.warning(
|
71
|
+
f"LLM Response Prevented for {model_name}: {response_message}.\n"
|
72
|
+
+ f"Last Message by {messages[-1].role}: {messages[-1].content}"
|
73
|
+
)
|
74
|
+
return response_message
|
43
75
|
|
44
76
|
|
45
77
|
@retry(
|
@@ -79,15 +111,82 @@ def gemini_llm_thread(
|
|
79
111
|
model_kwargs["temperature"] = temperature
|
80
112
|
model_kwargs["max_output_tokens"] = max_tokens
|
81
113
|
model_kwargs["stop_sequences"] = ["Notes:\n["]
|
82
|
-
model = genai.GenerativeModel(
|
114
|
+
model = genai.GenerativeModel(
|
115
|
+
model_name,
|
116
|
+
generation_config=model_kwargs,
|
117
|
+
system_instruction=system_prompt,
|
118
|
+
safety_settings={
|
119
|
+
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
120
|
+
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
121
|
+
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
122
|
+
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
123
|
+
},
|
124
|
+
)
|
83
125
|
|
84
126
|
formatted_messages = [{"role": message.role, "parts": [message.content]} for message in messages]
|
85
127
|
# all messages up to the last are considered to be part of the chat history
|
86
128
|
chat_session = model.start_chat(history=formatted_messages[0:-1])
|
87
129
|
# the last message is considered to be the current prompt
|
88
130
|
for chunk in chat_session.send_message(formatted_messages[-1]["parts"][0], stream=True):
|
89
|
-
|
131
|
+
message, stopped = handle_gemini_response(chunk.candidates, chunk.prompt_feedback)
|
132
|
+
message = message or chunk.text
|
133
|
+
g.send(message)
|
134
|
+
if stopped:
|
135
|
+
raise StopCandidateException(message)
|
136
|
+
except StopCandidateException as e:
|
137
|
+
logger.warning(
|
138
|
+
f"LLM Response Prevented for {model_name}: {e.args[0]}.\n"
|
139
|
+
+ f"Last Message by {messages[-1].role}: {messages[-1].content}"
|
140
|
+
)
|
90
141
|
except Exception as e:
|
91
142
|
logger.error(f"Error in gemini_llm_thread: {e}", exc_info=True)
|
92
143
|
finally:
|
93
144
|
g.close()
|
145
|
+
|
146
|
+
|
147
|
+
def handle_gemini_response(candidates, prompt_feedback=None):
|
148
|
+
"""Check if Gemini response was blocked and return an explanatory error message."""
|
149
|
+
# Check if the response was blocked due to safety concerns with the prompt
|
150
|
+
if len(candidates) == 0 and prompt_feedback:
|
151
|
+
message = f"\nI'd prefer to not respond to that due to **{prompt_feedback.block_reason.name}** issues with your query."
|
152
|
+
stopped = True
|
153
|
+
# Check if the response was blocked due to safety concerns with the generated content
|
154
|
+
elif candidates[0].finish_reason == FinishReason.SAFETY:
|
155
|
+
message = generate_safety_response(candidates[0].safety_ratings)
|
156
|
+
stopped = True
|
157
|
+
# Check if the response was stopped due to reaching maximum token limit or other reasons
|
158
|
+
elif candidates[0].finish_reason != FinishReason.STOP:
|
159
|
+
message = f"\nI can't talk further about that because of **{candidates[0].finish_reason.name} issue.**"
|
160
|
+
stopped = True
|
161
|
+
# Otherwise, the response is valid and can be used
|
162
|
+
else:
|
163
|
+
message = None
|
164
|
+
stopped = False
|
165
|
+
return message, stopped
|
166
|
+
|
167
|
+
|
168
|
+
def generate_safety_response(safety_ratings):
|
169
|
+
"""Generate a conversational response based on the safety ratings of the response."""
|
170
|
+
# Get the safety rating with the highest probability
|
171
|
+
max_safety_rating = sorted(safety_ratings, key=lambda x: x.probability, reverse=True)[0]
|
172
|
+
# Remove the "HARM_CATEGORY_" prefix and title case the category name
|
173
|
+
max_safety_category = " ".join(max_safety_rating.category.name.split("_")[2:]).title()
|
174
|
+
# Add a bit of variety to the discomfort level based on the safety rating probability
|
175
|
+
discomfort_level = {
|
176
|
+
HarmProbability.HARM_PROBABILITY_UNSPECIFIED: " ",
|
177
|
+
HarmProbability.LOW: "a bit ",
|
178
|
+
HarmProbability.MEDIUM: "moderately ",
|
179
|
+
HarmProbability.HIGH: random.choice(["very ", "quite ", "fairly "]),
|
180
|
+
}[max_safety_rating.probability]
|
181
|
+
# Generate a response using a random response template
|
182
|
+
safety_response_choice = random.choice(
|
183
|
+
[
|
184
|
+
"\nUmm, I'd rather not to respond to that. The conversation has some probability of going into **{category}** territory.",
|
185
|
+
"\nI'd prefer not to talk about **{category}** related topics. It makes me {discomfort_level}uncomfortable.",
|
186
|
+
"\nI feel {discomfort_level}squeamish talking about **{category}** related stuff! Can we talk about something less controversial?",
|
187
|
+
"\nThat sounds {discomfort_level}outside the [Overtone Window](https://en.wikipedia.org/wiki/Overton_window) of acceptable conversation. Should we stick to something less {category} related?",
|
188
|
+
]
|
189
|
+
)
|
190
|
+
return safety_response_choice.format(
|
191
|
+
category=max_safety_category, probability=max_safety_rating.probability.name, discomfort_level=discomfort_level
|
192
|
+
)
|
@@ -18,13 +18,20 @@ from khoj.utils.helpers import is_none_or_empty, merge_dicts
|
|
18
18
|
|
19
19
|
logger = logging.getLogger(__name__)
|
20
20
|
model_to_prompt_size = {
|
21
|
+
# OpenAI Models
|
21
22
|
"gpt-3.5-turbo": 12000,
|
22
|
-
"gpt-3.5-turbo-0125": 12000,
|
23
|
-
"gpt-4-0125-preview": 20000,
|
24
23
|
"gpt-4-turbo-preview": 20000,
|
24
|
+
"gpt-4o": 20000,
|
25
25
|
"gpt-4o-mini": 20000,
|
26
26
|
"o1-preview": 20000,
|
27
27
|
"o1-mini": 20000,
|
28
|
+
# Google Models
|
29
|
+
"gemini-1.5-flash": 20000,
|
30
|
+
"gemini-1.5-pro": 20000,
|
31
|
+
# Anthropic Models
|
32
|
+
"claude-3-5-sonnet-20240620": 20000,
|
33
|
+
"claude-3-opus-20240229": 20000,
|
34
|
+
# Offline Models
|
28
35
|
"TheBloke/Mistral-7B-Instruct-v0.2-GGUF": 3500,
|
29
36
|
"NousResearch/Hermes-2-Pro-Mistral-7B-GGUF": 3500,
|
30
37
|
"bartowski/Meta-Llama-3.1-8B-Instruct-GGUF": 20000,
|
@@ -100,7 +107,7 @@ def save_to_conversation_log(
|
|
100
107
|
inferred_queries: List[str] = [],
|
101
108
|
intent_type: str = "remember",
|
102
109
|
client_application: ClientApplication = None,
|
103
|
-
conversation_id:
|
110
|
+
conversation_id: str = None,
|
104
111
|
automation_id: str = None,
|
105
112
|
uploaded_image_url: str = None,
|
106
113
|
):
|
@@ -163,7 +170,7 @@ def generate_chatml_messages_with_context(
|
|
163
170
|
if loaded_model:
|
164
171
|
max_prompt_size = infer_max_tokens(loaded_model.n_ctx(), model_to_prompt_size.get(model_name, math.inf))
|
165
172
|
else:
|
166
|
-
max_prompt_size = model_to_prompt_size.get(model_name,
|
173
|
+
max_prompt_size = model_to_prompt_size.get(model_name, 10000)
|
167
174
|
|
168
175
|
# Scale lookback turns proportional to max prompt size supported by model
|
169
176
|
lookback_turns = max_prompt_size // 750
|
@@ -291,8 +298,6 @@ def reciprocal_conversation_to_chatml(message_pair):
|
|
291
298
|
return [ChatMessage(content=message, role=role) for message, role in zip(message_pair, ["user", "assistant"])]
|
292
299
|
|
293
300
|
|
294
|
-
def remove_json_codeblock(response):
|
301
|
+
def remove_json_codeblock(response: str):
|
295
302
|
"""Remove any markdown json codeblock formatting if present. Useful for non schema enforceable models"""
|
296
|
-
|
297
|
-
response = response[7:-3]
|
298
|
-
return response
|
303
|
+
return response.removeprefix("```json").removesuffix("```")
|
khoj/routers/api.py
CHANGED
@@ -328,7 +328,7 @@ async def extract_references_and_questions(
|
|
328
328
|
q: str,
|
329
329
|
n: int,
|
330
330
|
d: float,
|
331
|
-
conversation_id:
|
331
|
+
conversation_id: str,
|
332
332
|
conversation_commands: List[ConversationCommand] = [ConversationCommand.Default],
|
333
333
|
location_data: LocationData = None,
|
334
334
|
send_status_func: Optional[Callable] = None,
|
khoj/routers/api_chat.py
CHANGED
@@ -77,9 +77,7 @@ from khoj.routers.email import send_query_feedback
|
|
77
77
|
@api_chat.get("/conversation/file-filters/{conversation_id}", response_class=Response)
|
78
78
|
@requires(["authenticated"])
|
79
79
|
def get_file_filter(request: Request, conversation_id: str) -> Response:
|
80
|
-
conversation = ConversationAdapters.get_conversation_by_user(
|
81
|
-
request.user.object, conversation_id=int(conversation_id)
|
82
|
-
)
|
80
|
+
conversation = ConversationAdapters.get_conversation_by_user(request.user.object, conversation_id=conversation_id)
|
83
81
|
if not conversation:
|
84
82
|
return Response(content=json.dumps({"status": "error", "message": "Conversation not found"}), status_code=404)
|
85
83
|
|
@@ -95,7 +93,7 @@ def get_file_filter(request: Request, conversation_id: str) -> Response:
|
|
95
93
|
@api_chat.delete("/conversation/file-filters/bulk", response_class=Response)
|
96
94
|
@requires(["authenticated"])
|
97
95
|
def remove_files_filter(request: Request, filter: FilesFilterRequest) -> Response:
|
98
|
-
conversation_id =
|
96
|
+
conversation_id = filter.conversation_id
|
99
97
|
files_filter = filter.filenames
|
100
98
|
file_filters = ConversationAdapters.remove_files_from_filter(request.user.object, conversation_id, files_filter)
|
101
99
|
return Response(content=json.dumps(file_filters), media_type="application/json", status_code=200)
|
@@ -105,7 +103,7 @@ def remove_files_filter(request: Request, filter: FilesFilterRequest) -> Respons
|
|
105
103
|
@requires(["authenticated"])
|
106
104
|
def add_files_filter(request: Request, filter: FilesFilterRequest):
|
107
105
|
try:
|
108
|
-
conversation_id =
|
106
|
+
conversation_id = filter.conversation_id
|
109
107
|
files_filter = filter.filenames
|
110
108
|
file_filters = ConversationAdapters.add_files_to_filter(request.user.object, conversation_id, files_filter)
|
111
109
|
return Response(content=json.dumps(file_filters), media_type="application/json", status_code=200)
|
@@ -118,7 +116,7 @@ def add_files_filter(request: Request, filter: FilesFilterRequest):
|
|
118
116
|
@requires(["authenticated"])
|
119
117
|
def add_file_filter(request: Request, filter: FileFilterRequest):
|
120
118
|
try:
|
121
|
-
conversation_id =
|
119
|
+
conversation_id = filter.conversation_id
|
122
120
|
files_filter = [filter.filename]
|
123
121
|
file_filters = ConversationAdapters.add_files_to_filter(request.user.object, conversation_id, files_filter)
|
124
122
|
return Response(content=json.dumps(file_filters), media_type="application/json", status_code=200)
|
@@ -130,7 +128,7 @@ def add_file_filter(request: Request, filter: FileFilterRequest):
|
|
130
128
|
@api_chat.delete("/conversation/file-filters", response_class=Response)
|
131
129
|
@requires(["authenticated"])
|
132
130
|
def remove_file_filter(request: Request, filter: FileFilterRequest) -> Response:
|
133
|
-
conversation_id =
|
131
|
+
conversation_id = filter.conversation_id
|
134
132
|
files_filter = [filter.filename]
|
135
133
|
file_filters = ConversationAdapters.remove_files_from_filter(request.user.object, conversation_id, files_filter)
|
136
134
|
return Response(content=json.dumps(file_filters), media_type="application/json", status_code=200)
|
@@ -189,7 +187,7 @@ async def chat_starters(
|
|
189
187
|
def chat_history(
|
190
188
|
request: Request,
|
191
189
|
common: CommonQueryParams,
|
192
|
-
conversation_id: Optional[
|
190
|
+
conversation_id: Optional[str] = None,
|
193
191
|
n: Optional[int] = None,
|
194
192
|
):
|
195
193
|
user = request.user.object
|
@@ -312,7 +310,7 @@ def get_shared_chat(
|
|
312
310
|
async def clear_chat_history(
|
313
311
|
request: Request,
|
314
312
|
common: CommonQueryParams,
|
315
|
-
conversation_id: Optional[
|
313
|
+
conversation_id: Optional[str] = None,
|
316
314
|
):
|
317
315
|
user = request.user.object
|
318
316
|
|
@@ -375,7 +373,7 @@ def fork_public_conversation(
|
|
375
373
|
def duplicate_chat_history_public_conversation(
|
376
374
|
request: Request,
|
377
375
|
common: CommonQueryParams,
|
378
|
-
conversation_id:
|
376
|
+
conversation_id: str,
|
379
377
|
):
|
380
378
|
user = request.user.object
|
381
379
|
domain = request.headers.get("host")
|
@@ -423,7 +421,7 @@ def chat_sessions(
|
|
423
421
|
|
424
422
|
session_values = [
|
425
423
|
{
|
426
|
-
"conversation_id": session[0],
|
424
|
+
"conversation_id": str(session[0]),
|
427
425
|
"slug": session[2] or session[1],
|
428
426
|
"agent_name": session[4],
|
429
427
|
"agent_avatar": session[5],
|
@@ -455,7 +453,7 @@ async def create_chat_session(
|
|
455
453
|
# Create new Conversation Session
|
456
454
|
conversation = await ConversationAdapters.acreate_conversation_session(user, request.user.client_app, agent_slug)
|
457
455
|
|
458
|
-
response = {"conversation_id": conversation.id}
|
456
|
+
response = {"conversation_id": str(conversation.id)}
|
459
457
|
|
460
458
|
conversation_metadata = {
|
461
459
|
"agent": agent_slug,
|
@@ -497,7 +495,7 @@ async def set_conversation_title(
|
|
497
495
|
request: Request,
|
498
496
|
common: CommonQueryParams,
|
499
497
|
title: str,
|
500
|
-
conversation_id: Optional[
|
498
|
+
conversation_id: Optional[str] = None,
|
501
499
|
) -> Response:
|
502
500
|
user = request.user.object
|
503
501
|
title = title.strip()[:200]
|
@@ -527,7 +525,7 @@ class ChatRequestBody(BaseModel):
|
|
527
525
|
d: Optional[float] = None
|
528
526
|
stream: Optional[bool] = False
|
529
527
|
title: Optional[str] = None
|
530
|
-
conversation_id: Optional[
|
528
|
+
conversation_id: Optional[str] = None
|
531
529
|
city: Optional[str] = None
|
532
530
|
region: Optional[str] = None
|
533
531
|
country: Optional[str] = None
|
@@ -1016,7 +1014,7 @@ async def get_chat(
|
|
1016
1014
|
d: float = None,
|
1017
1015
|
stream: Optional[bool] = False,
|
1018
1016
|
title: Optional[str] = None,
|
1019
|
-
conversation_id: Optional[
|
1017
|
+
conversation_id: Optional[str] = None,
|
1020
1018
|
city: Optional[str] = None,
|
1021
1019
|
region: Optional[str] = None,
|
1022
1020
|
country: Optional[str] = None,
|