Skip to content

Commit a3c4347

Browse files
committed
Add a one-click action to export all conversations. Add a self-service delete account action to the settings page
1 parent 79816d2 commit a3c4347

File tree

7 files changed

+500
-201
lines changed

7 files changed

+500
-201
lines changed

src/interface/web/app/settings/page.tsx

+365-199
Large diffs are not rendered by default.

src/interface/web/package.json

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@radix-ui/react-toggle": "^1.1.0",
4444
"@radix-ui/react-tooltip": "^1.1.6",
4545
"@radix-ui/themes": "^3.1.1",
46+
"@types/file-saver": "^2.0.7",
4647
"autoprefixer": "^10.4.19",
4748
"class-variance-authority": "^0.7.1",
4849
"clsx": "^2.1.1",
@@ -53,9 +54,11 @@
5354
"embla-carousel-react": "^8.5.1",
5455
"eslint": "^8",
5556
"eslint-config-next": "14.2.3",
57+
"file-saver": "^2.0.5",
5658
"framer-motion": "^12.0.6",
5759
"input-otp": "^1.2.4",
5860
"intl-tel-input": "^23.8.1",
61+
"jszip": "^3.10.1",
5962
"katex": "^0.16.21",
6063
"libphonenumber-js": "^1.11.4",
6164
"lucide-react": "^0.468.0",

src/interface/web/yarn.lock

+85-2
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,11 @@
11851185
dependencies:
11861186
dompurify "*"
11871187

1188+
"@types/file-saver@^2.0.7":
1189+
version "2.0.7"
1190+
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.7.tgz#8dbb2f24bdc7486c54aa854eb414940bbd056f7d"
1191+
integrity sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==
1192+
11881193
"@types/geojson@*":
11891194
version "7946.0.16"
11901195
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
@@ -1770,6 +1775,11 @@ confbox@^0.2.1:
17701775
resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.1.tgz#ae39f2c99699afa451d00206479f15f9a1208a8b"
17711776
integrity sha512-hkT3yDPFbs95mNCy1+7qNKC6Pro+/ibzYxtM2iqEigpf0sVw+bg4Zh9/snjsBcf990vfIsg5+1U7VyiyBb3etg==
17721777

1778+
core-util-is@~1.0.0:
1779+
version "1.0.3"
1780+
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
1781+
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
1782+
17731783
cose-base@^1.0.0:
17741784
version "1.0.3"
17751785
resolved "https://registry.yarnpkg.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
@@ -2739,6 +2749,11 @@ file-entry-cache@^6.0.1:
27392749
dependencies:
27402750
flat-cache "^3.0.4"
27412751

2752+
file-saver@^2.0.5:
2753+
version "2.0.5"
2754+
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
2755+
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
2756+
27422757
fill-range@^7.1.1:
27432758
version "7.1.1"
27442759
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
@@ -3065,6 +3080,11 @@ ignore@^5.2.0:
30653080
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
30663081
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
30673082

3083+
immediate@~3.0.5:
3084+
version "3.0.6"
3085+
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
3086+
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
3087+
30683088
import-fresh@^3.2.1:
30693089
version "3.3.1"
30703090
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
@@ -3086,7 +3106,7 @@ inflight@^1.0.4:
30863106
once "^1.3.0"
30873107
wrappy "1"
30883108

3089-
inherits@2:
3109+
inherits@2, inherits@~2.0.3:
30903110
version "2.0.4"
30913111
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
30923112
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -3343,6 +3363,11 @@ isarray@^2.0.5:
33433363
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
33443364
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
33453365

3366+
isarray@~1.0.0:
3367+
version "1.0.0"
3368+
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
3369+
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
3370+
33463371
isexe@^2.0.0:
33473372
version "2.0.0"
33483373
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -3427,6 +3452,16 @@ json5@^1.0.2:
34273452
object.assign "^4.1.4"
34283453
object.values "^1.1.6"
34293454

3455+
jszip@^3.10.1:
3456+
version "3.10.1"
3457+
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
3458+
integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
3459+
dependencies:
3460+
lie "~3.3.0"
3461+
pako "~1.0.2"
3462+
readable-stream "~2.3.6"
3463+
setimmediate "^1.0.5"
3464+
34303465
katex@^0.16.21, katex@^0.16.9:
34313466
version "0.16.21"
34323467
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.21.tgz#8f63c659e931b210139691f2cc7bb35166b792a3"
@@ -3497,6 +3532,13 @@ libphonenumber-js@^1.11.4:
34973532
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.5.tgz#8e6043a67112d4beedb8627b359a613f04d88fba"
34983533
integrity sha512-DOjiaVjjSmap12ztyb4QgoFmUe/GbgnEXHu+R7iowk0lzDIjScvPAm8cK9RYTEobbRb0OPlwlZUGTTJPJg13Kw==
34993534

3535+
lie@~3.3.0:
3536+
version "3.3.0"
3537+
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
3538+
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
3539+
dependencies:
3540+
immediate "~3.0.5"
3541+
35003542
lilconfig@^3.0.0, lilconfig@^3.1.3:
35013543
version "3.1.3"
35023544
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4"
@@ -3960,6 +4002,11 @@ package-manager-detector@^0.2.8:
39604002
dependencies:
39614003
quansync "^0.2.7"
39624004

4005+
pako@~1.0.2:
4006+
version "1.0.11"
4007+
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
4008+
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
4009+
39634010
parent-module@^1.0.0:
39644011
version "1.0.1"
39654012
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
@@ -4160,6 +4207,11 @@ [email protected]:
41604207
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
41614208
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
41624209

4210+
process-nextick-args@~2.0.0:
4211+
version "2.0.1"
4212+
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
4213+
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
4214+
41634215
prop-types@^15.8.1:
41644216
version "15.8.1"
41654217
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
@@ -4309,6 +4361,19 @@ read-cache@^1.0.0:
43094361
dependencies:
43104362
pify "^2.3.0"
43114363

4364+
readable-stream@~2.3.6:
4365+
version "2.3.8"
4366+
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
4367+
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
4368+
dependencies:
4369+
core-util-is "~1.0.0"
4370+
inherits "~2.0.3"
4371+
isarray "~1.0.0"
4372+
process-nextick-args "~2.0.0"
4373+
safe-buffer "~5.1.1"
4374+
string_decoder "~1.1.1"
4375+
util-deprecate "~1.0.1"
4376+
43124377
readdirp@~3.6.0:
43134378
version "3.6.0"
43144379
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -4433,6 +4498,11 @@ safe-array-concat@^1.1.3:
44334498
has-symbols "^1.1.0"
44344499
isarray "^2.0.5"
44354500

4501+
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
4502+
version "5.1.2"
4503+
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
4504+
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
4505+
44364506
safe-push-apply@^1.0.0:
44374507
version "1.0.0"
44384508
resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5"
@@ -4503,6 +4573,11 @@ set-proto@^1.0.0:
45034573
es-errors "^1.3.0"
45044574
es-object-atoms "^1.0.0"
45054575

4576+
setimmediate@^1.0.5:
4577+
version "1.0.5"
4578+
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
4579+
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
4580+
45064581
shadcn-ui@^0.9.0:
45074582
version "0.9.5"
45084583
resolved "https://registry.yarnpkg.com/shadcn-ui/-/shadcn-ui-0.9.5.tgz#b7f35b78f2c7fe0b71651fe542ed62f01795b4a6"
@@ -4616,6 +4691,7 @@ string-argv@^0.3.2:
46164691
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
46174692

46184693
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
4694+
name string-width-cjs
46194695
version "4.2.3"
46204696
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
46214697
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -4710,6 +4786,13 @@ string.prototype.trimstart@^1.0.8:
47104786
define-properties "^1.2.1"
47114787
es-object-atoms "^1.0.0"
47124788

4789+
string_decoder@~1.1.1:
4790+
version "1.1.1"
4791+
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
4792+
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
4793+
dependencies:
4794+
safe-buffer "~5.1.0"
4795+
47134796
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
47144797
version "6.0.1"
47154798
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
@@ -5043,7 +5126,7 @@ use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0:
50435126
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
50445127
integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==
50455128

5046-
util-deprecate@^1.0.2:
5129+
util-deprecate@^1.0.2, util-deprecate@~1.0.1:
50475130
version "1.0.2"
50485131
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
50495132
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

src/khoj/database/adapters/__init__.py

+22
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,28 @@ def get_conversation_by_user(
939939

940940
return conversation
941941

942+
@staticmethod
943+
@require_valid_user
944+
def get_all_conversations_for_export(user: KhojUser, page: Optional[int] = 0):
945+
all_conversations = Conversation.objects.filter(user=user).prefetch_related("agent")[page : page + 10]
946+
histories = []
947+
for conversation in all_conversations:
948+
history = {
949+
"title": conversation.title,
950+
"agent": conversation.agent.name if conversation.agent else "Khoj",
951+
"created_at": datetime.strftime(conversation.created_at, "%Y-%m-%d %H:%M:%S"),
952+
"updated_at": datetime.strftime(conversation.updated_at, "%Y-%m-%d %H:%M:%S"),
953+
"conversation_log": conversation.conversation_log,
954+
"file_filters": conversation.file_filters,
955+
}
956+
histories.append(history)
957+
return histories
958+
959+
@staticmethod
960+
@require_valid_user
961+
def get_num_conversations(user: KhojUser):
962+
return Conversation.objects.filter(user=user).count()
963+
942964
@staticmethod
943965
@require_valid_user
944966
def get_conversation_sessions(user: KhojUser, client_application: ClientApplication = None):

src/khoj/processor/conversation/prompts.py

+1
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@
13611361
- **/default**: Chat using your knowledge base and Khoj's general knowledge for context.
13621362
- **/online**: Chat using the internet as a source of information.
13631363
- **/image**: Generate an image based on your message.
1364+
- **/research**: Go deeper in a topic for more accurate, in-depth responses.
13641365
- **/help**: Show this help message.
13651366
13661367
You are using the **{model}** model on the **{device}**.

src/khoj/routers/api.py

+8
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@
6868
)
6969

7070

71+
@api.delete("/self")
72+
@requires(["authenticated"])
73+
def delete_self(request: Request):
74+
user = request.user.object
75+
user.delete()
76+
return {"status": "ok"}
77+
78+
7179
@api.get("/search", response_model=List[SearchResponse])
7280
@requires(["authenticated"])
7381
async def search(

src/khoj/routers/api_chat.py

+16
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,22 @@
9494
api_chat = APIRouter()
9595

9696

97+
@api_chat.get("/stats", response_class=Response)
98+
@requires(["authenticated"])
99+
def chat_stats(request: Request, common: CommonQueryParams) -> Response:
100+
num_conversations = ConversationAdapters.get_num_conversations(request.user.object)
101+
return Response(
102+
content=json.dumps({"num_conversations": num_conversations}), media_type="application/json", status_code=200
103+
)
104+
105+
106+
@api_chat.get("/export", response_class=Response)
107+
@requires(["authenticated"])
108+
def export_conversation(request: Request, common: CommonQueryParams, page: Optional[int] = 1) -> Response:
109+
all_conversations = ConversationAdapters.get_all_conversations_for_export(request.user.object, page=page)
110+
return Response(content=json.dumps(all_conversations), media_type="application/json", status_code=200)
111+
112+
97113
@api_chat.get("/conversation/file-filters/{conversation_id}", response_class=Response)
98114
@requires(["authenticated"])
99115
def get_file_filter(request: Request, conversation_id: str) -> Response:

0 commit comments

Comments
 (0)