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.
Files changed (71) hide show
  1. khoj/database/adapters/__init__.py +8 -8
  2. khoj/database/migrations/0063_conversation_temp_id.py +36 -0
  3. khoj/database/migrations/0064_remove_conversation_temp_id_alter_conversation_id.py +86 -0
  4. khoj/database/models/__init__.py +1 -0
  5. khoj/interface/compiled/404/index.html +1 -1
  6. khoj/interface/compiled/_next/static/chunks/1603-d643510c2c0b8871.js +1 -0
  7. khoj/interface/compiled/_next/static/chunks/{1906-1747a36c336df02c.js → 4051-3dc2df557ccb5213.js} +2 -2
  8. khoj/interface/compiled/_next/static/chunks/8423-62ac6c832be2461b.js +1 -0
  9. khoj/interface/compiled/_next/static/chunks/9178-421e47df97ff0213.js +1 -0
  10. khoj/interface/compiled/_next/static/chunks/app/agents/page-f8ad4d2944dbcf91.js +1 -0
  11. khoj/interface/compiled/_next/static/chunks/app/automations/{page-3f4b6ff0261e19b7.js → page-cc875a656df43713.js} +1 -1
  12. khoj/interface/compiled/_next/static/chunks/app/chat/page-3e75b0e0aa3aaaf5.js +1 -0
  13. khoj/interface/compiled/_next/static/chunks/app/factchecker/{page-828cf3c5b8e3af79.js → page-bb320ff7d4dee716.js} +1 -1
  14. khoj/interface/compiled/_next/static/chunks/app/{page-2f423a763e2ee0c7.js → page-60193524cf570002.js} +1 -1
  15. khoj/interface/compiled/_next/static/chunks/app/search/page-4bceb5b0df9cfd66.js +1 -0
  16. khoj/interface/compiled/_next/static/chunks/app/settings/page-532ed8b778a0b40d.js +1 -0
  17. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-b5e63aabfd573dba.js +1 -0
  18. khoj/interface/compiled/_next/static/chunks/{webpack-61a553b6ff44f97c.js → webpack-de28762883e5816d.js} +1 -1
  19. khoj/interface/compiled/_next/static/css/1105696872e3f20c.css +25 -0
  20. khoj/interface/compiled/_next/static/css/37a313cb39403a84.css +1 -0
  21. khoj/interface/compiled/_next/static/css/6bde1f2045622ef7.css +1 -0
  22. khoj/interface/compiled/_next/static/css/{a3530ec58b0b660f.css → ab57702ed2b98214.css} +1 -1
  23. khoj/interface/compiled/_next/static/css/{92c48eece0b102b9.css → e41ec62af8ee4e38.css} +1 -1
  24. khoj/interface/compiled/agents/index.html +1 -1
  25. khoj/interface/compiled/agents/index.txt +2 -2
  26. khoj/interface/compiled/automations/index.html +1 -1
  27. khoj/interface/compiled/automations/index.txt +2 -2
  28. khoj/interface/compiled/chat/index.html +1 -1
  29. khoj/interface/compiled/chat/index.txt +2 -2
  30. khoj/interface/compiled/factchecker/index.html +1 -1
  31. khoj/interface/compiled/factchecker/index.txt +2 -2
  32. khoj/interface/compiled/index.html +1 -1
  33. khoj/interface/compiled/index.txt +2 -2
  34. khoj/interface/compiled/search/index.html +1 -1
  35. khoj/interface/compiled/search/index.txt +2 -2
  36. khoj/interface/compiled/settings/index.html +1 -1
  37. khoj/interface/compiled/settings/index.txt +2 -2
  38. khoj/interface/compiled/share/chat/index.html +1 -1
  39. khoj/interface/compiled/share/chat/index.txt +2 -2
  40. khoj/interface/email/magic_link.html +1 -1
  41. khoj/interface/email/task.html +31 -34
  42. khoj/interface/email/welcome.html +82 -53
  43. khoj/main.py +1 -1
  44. khoj/processor/content/images/image_to_entries.py +6 -4
  45. khoj/processor/conversation/google/utils.py +106 -7
  46. khoj/processor/conversation/utils.py +13 -8
  47. khoj/routers/api.py +1 -1
  48. khoj/routers/api_chat.py +13 -15
  49. khoj/routers/helpers.py +12 -7
  50. khoj/utils/cli.py +6 -0
  51. khoj/utils/constants.py +9 -2
  52. khoj/utils/initialization.py +158 -71
  53. {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/METADATA +2 -2
  54. {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/RECORD +60 -58
  55. khoj/interface/compiled/_next/static/chunks/1603-fb2d80ae73990df3.js +0 -1
  56. khoj/interface/compiled/_next/static/chunks/8423-14fc72aec9104ce9.js +0 -1
  57. khoj/interface/compiled/_next/static/chunks/9178-c153fc402c970365.js +0 -1
  58. khoj/interface/compiled/_next/static/chunks/app/agents/page-989a824c640bc532.js +0 -1
  59. khoj/interface/compiled/_next/static/chunks/app/chat/page-cc71b18feddf80d6.js +0 -1
  60. khoj/interface/compiled/_next/static/chunks/app/search/page-dcd385f03255ef36.js +0 -1
  61. khoj/interface/compiled/_next/static/chunks/app/settings/page-ddcd51147d18c694.js +0 -1
  62. khoj/interface/compiled/_next/static/chunks/app/share/chat/page-a84001b4724b5463.js +0 -1
  63. khoj/interface/compiled/_next/static/css/2272c73fc7a3b571.css +0 -1
  64. khoj/interface/compiled/_next/static/css/3e49e5ee49c6bda1.css +0 -25
  65. khoj/interface/compiled/_next/static/css/553f9cdcc7a2bcd6.css +0 -1
  66. /khoj/interface/compiled/_next/static/chunks/{7023-52c1be60135eb057.js → 7023-1074a582ec989284.js} +0 -0
  67. /khoj/interface/compiled/_next/static/{A47_BQNcqhjWCuNh_iSec → u496AO3jlFBPp2apVnMkP}/_buildManifest.js +0 -0
  68. /khoj/interface/compiled/_next/static/{A47_BQNcqhjWCuNh_iSec → u496AO3jlFBPp2apVnMkP}/_ssgManifest.js +0 -0
  69. {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/WHEEL +0 -0
  70. {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/entry_points.txt +0 -0
  71. {khoj-1.23.1.dev1.dist-info → khoj-1.23.4.dev1.dist-info}/licenses/LICENSE +0 -0
@@ -1,40 +1,37 @@
1
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Khoj AI - Automation</title>
5
- </head>
6
- <body>
7
- <body style="font-family: 'Verdana', sans-serif; font-weight: 400; font-style: normal; padding: 0; text-align: left; width: 600px; margin: 20px auto;">
8
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
9
- <a class="logo" href="https://khoj.dev" target="_blank" style="text-decoration: none; text-decoration: underline dotted;">
10
- <img src="https://khoj.dev/khoj-logo-sideways-500.png" alt="Khoj Logo" style="width: 100px;">
11
- </a>
12
- <div class="calls-to-action" style="margin-top: 20px;">
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
- <div style="display: grid; grid-template-columns: 1fr; grid-gap: 12px; margin-top: 20px;">
17
- <div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
18
- <a href="https://app.khoj.dev/automations" style="text-decoration: none; text-decoration: underline dotted;">
19
- <h3 style="color: #333; font-size: large; margin: 0; padding: 0; line-height: 2.0; background-color: #b8f1c7; padding: 8px; ">{{subject}}</h3>
20
- </a>
21
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">{{result}}</p>
22
- </div>
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
- <head>
4
- <title>Welcome to Khoj</title>
5
- </head>
6
- <body>
7
- <body style="font-family: 'Verdana', sans-serif; font-weight: 400; font-style: normal; padding: 0; text-align: left; width: 600px; margin: 20px auto;">
8
- <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
9
- <a class="logo" href="https://khoj.dev" target="_blank" style="text-decoration: none; text-decoration: underline dotted;">
10
- <img src="https://khoj.dev/khoj-logo-sideways-500.png" alt="Khoj Logo" style="width: 100px;">
11
- </a>
12
- <div class="calls-to-action" style="margin-top: 20px;">
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);">Merge AI with your brain</h1>
15
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">Hi {{name}}! We are psyched to be part of your journey with personal AI. To better help you, we're committed to staying transparent, accessible, and completely open-source.</p>
16
- <a class="button" href="https://app.khoj.dev" target="_blank" style="display: block; width: 200px; text-align: center; padding: 10px; margin-top: 20px; color: #333; background-color: #fee285; text-decoration: none; border-radius: 5px; font-weight: bold; transition: background-color 0.3s ease; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); padding: 4px; font-size: large; text-transform: uppercase;">Get Started</a>
17
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">You're about to get a whole lot more productive.</p>
18
-
19
- <div style="display: grid; grid-template-columns: 1fr 1fr; grid-gap: 12px; margin-top: 20px;">
20
- <div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
21
- <a href="https://docs.khoj.dev/features/online_search" style="text-decoration: none; text-decoration: underline dotted;">
22
- <h3 style="color: #333; font-size: large; margin: 0; padding: 0; line-height: 2.0; background-color: #b8f1c7; padding: 8px; ">Ditch the search bar</h3>
23
- </a>
24
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">You don't need to click around Google results and sift through information yourself, because Khoj is connected to the internet.</p>
25
- </div>
26
- <div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
27
- <a href="https://app.khoj.dev/agents" style="text-decoration: none; text-decoration: underline dotted;">
28
- <h3 style="color: #333; font-size: large; margin: 0; padding: 0; line-height: 2.0; background-color: #b8f1c7; padding: 8px;">Get a village, not just an agent</h3>
29
- </a>
30
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">Khoj can fill the need for more specialized assistance, <a href="https://blog.khoj.dev/posts/using-khoj-for-studying/">such as tutoring</a>, with its curated agents. You get a whole team, always available.</p>
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
- <div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
33
- <a href="https://docs.khoj.dev/category/clients" style="text-decoration: none; text-decoration: underline dotted;">
34
- <h3 style="color: #333; font-size: large; margin: 0; padding: 0; line-height: 2.0; background-color: #b8f1c7; padding: 8px;">Available where you are</h3>
35
- </a>
36
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">Build on top of your digital brain. Khoj stores whatever data you share with it, so you can get answers from your personal notes and documents in your native language. You can engage from your desktop, Obsidian, WhatsApp, or the web.</p>
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
- <div style="border: 1px solid black; border-radius: 8px; padding: 8px; box-shadow: 6px 6px rgba(0, 0, 0, 1.0); margin-top: 20px;">
39
- <a href="https://blog.khoj.dev/posts/how-khoj-generates-images/" style="text-decoration: none; text-decoration: underline dotted;">
40
- <h3 style="color: #333; font-size: large; margin: 0; padding: 0; line-height: 2.0; background-color: #b8f1c7; padding: 8px;">Create rich, contextual images</h3>
41
- </a>
42
- <p style="color: #333; font-size: medium; margin-top: 20px; padding: 0; line-height: 1.5;">With your shared data, Khoj can help you create astoundingly personal images depicting scenes of what's important to you.</p>
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
- </div>
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(f"Unable to process file: {image_file}. This file will not be indexed.")
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(model_name, generation_config=model_kwargs, system_instruction=system_prompt)
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
- # all messages up to the last are considered to be part of the chat history
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
- # the last message is considered to be the current prompt
41
- aggregated_response = chat_session.send_message(formatted_messages[-1]["parts"][0])
42
- return aggregated_response.text
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(model_name, generation_config=model_kwargs, system_instruction=system_prompt)
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
- g.send(chunk.text)
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: int = None,
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, 2000)
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
- if response.startswith("```json") and response.endswith("```"):
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: int,
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 = int(filter.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 = int(filter.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 = int(filter.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 = int(filter.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[int] = None,
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[int] = None,
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: int,
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[int] = None,
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[int] = None
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[int] = None,
1017
+ conversation_id: Optional[str] = None,
1020
1018
  city: Optional[str] = None,
1021
1019
  region: Optional[str] = None,
1022
1020
  country: Optional[str] = None,