From 6e957584acd7d1df87b73a70fefe94fc6ab217ec Mon Sep 17 00:00:00 2001 From: Debanjum Singh Solanky Date: Tue, 7 Nov 2023 02:08:06 -0800 Subject: [PATCH] Create config page on web app to manage computer files indexed by Khoj Remove the table of all files indexed by Khoj. This seems overkill and doesn't match the UI semantics of the other data sources like Github, Notion. Create instead a data source card for computer files with the same update, disable semantics of the Github and Notion data source cards Users can disable each data source from its card on the main config page. They can see/delete individual files indexed from the computer data source once they click into the computer files data source card on the config page --- .../interface/web/assets/icons/computer.png | Bin 0 -> 10517 bytes src/khoj/interface/web/config.html | 114 ++++++------------ .../web/content_source_computer_input.html | 107 ++++++++++++++++ src/khoj/routers/api.py | 25 +--- src/khoj/routers/web_client.py | 35 +++--- 5 files changed, 165 insertions(+), 116 deletions(-) create mode 100644 src/khoj/interface/web/assets/icons/computer.png create mode 100644 src/khoj/interface/web/content_source_computer_input.html diff --git a/src/khoj/interface/web/assets/icons/computer.png b/src/khoj/interface/web/assets/icons/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..12473485486c7dbf1b90dbc3d19ae9cc0e340171 GIT binary patch literal 10517 zcmV+wDeBgVP)005u}1^@s6i_d2*001c5NklH{_ z$7W{cJhM|n$ax4`m3AL?4H)V@2J(S>7E_E zed^KPpMe5#;K-2^V*o})q$WvwAOMi~k3vaq09Z!^a_I1Jc!5J{6z0JfDcoy+HJfsteJp*V!t7brH@rexXI0=!J}F#ylOK@J}|(K1NR=6U{L zS(bPI^2<*nzx?tuwl=p=Y6%ZXO*sBq({Ydb@ifx8oUg{bNJy4pW_lWfgM(02RDoy{ zWGC_q${uC_bWAk(V^)@_SQ(3CBIxTMz|hd}?$2hHWe=r*e*4YAFFyYiMbT$Pd+4$X&dp(V zb{3^7z;;O#zu}8{B-j==az{v2SxZDJN#qe958qhY8_JI@%Mz1Q)4L=*Eh5+DH{EpW z13&)w!$qHb@)1BH%d$UZS18@gV&>l#P60+xOknlp0QSndDXtD#phru(EpAHw6GPB(stf*hGAN zsnN{+$vtF^JT>ar3P#6?NbAo`tX>^_-E0b?_Q(N3X|v9&zX}4LtK?06-Gc3AdG;~ zcS5>&S~Tl+abD8@{EpE-T&=Gxjb;BfHDZdY-$D2{L2!4l`(m}H-5_WuwWlayacxyAV+Y8Rj%(Q#) zy!|IUs$sR5kYMF)Sb@aOz5qPm{5{l`5(*;!oxSrw?j+aEctg@=Y3+JB9KhimFCD@0 z6njv|%Fa25ot*Gl{1?>otFIcSIq$F9*|mB~qizNe?1o5WqO%SNCrgnOrNW7-m9^ZV zrg;Wg`>2|vUs`V~*p;ps1rOU&)SPl(+|~FnK=YNQW*|wa?y=p;H;furxW5CTz!)VN z4#o7%=QrEc}S9D;vfFu?{2>Q^1ru; zix&F*I7UB6#6w;2;RhcMVZ>auRu##tkDX7I#1?0;IXmlVFyM3`$##gUOKl&2^zm>? zcyv{esFS4_%SbZ&=y2R;XXP7E^hvB)H*(7`({4MaG2-( zl6rmYM5O}7Z_PfUP*x|NZ5EZE<xo>^zJCCmD=;?|@ zLDWud+XD^W(^V_{t5>haDmct@*v4u{Nz*UZrBgYC#C5`(J47U5&n+sNuyo4JrQjYI z$uU)pvGqjNxjEoysi&QoZsh}t0-XVw!p{Jy0&v${r~cSZ3cqmHpg3$*yTS{xX$337 z<7uk}=Yl4#&i328@oK(d_7Zo!fAsj#a8|A8Iqhu|j5~O(!pkie0ki-g@67o!Ro~^+ z(gl(`VwTt*c#e^>>T3)ByTAL}n=fw-<9$rX2v%aEU}hc){}t7$lHJg+&6S zIP7Ch39z7MCjMw_d(PZG{p8bI6XbWHh%N?_rYq_uQ|TVN_jEnD?GO9#SOo_mN^+iS zS?HI9vYe}85g+8d+jLCCrBLG`N>nP?l!Cczx>qzU{SVjuuY%8?Ki?iddVFiD`c>Lg zmsh-`MMEZBwDILMVXsBr2|j;ye@Ij2klHYfhTZDq=b2&d^NPicE`8m4TaG=e>N5;{ z`RGJswosI88a?U_J@ zfKhw`zD3O-(f`P(8P&aQhbscLhF)ZBImWf_=_~qZkTwU-q0c}&~YW7PfHIl6{fI3?hdo>K|a?uobve% zK|uot<||<9&nzw)J&tjVhTHT8XS0+_R9a}Z_{(`=w^jW1=LeME25Osa394#!%?YZZL-<)f3z z!v7$3@PSgllIl2|LynC8gyc~22-X%gSDRZwD)ZdQG9o$Q zHv~q(g4d!A+3UC%SOr(;vUTXh6IYSLXV*{gOO2jwt% zskxuj`3l%$;`cLH{>B(R2lHGyfwp?L+(s&FaMEIsCjJR9;`ZsF{Qd0tU3e3>HpkZphq{dWckX&LKWiYFk zk)4fIl@W2^Q;4*?h`KMODygoz$zYBPKrBmX zA=IGc%T-CXRWExzFwc#+k?ZS|t5&9`$CJUe043Y9N!;(AO-nYPdIYW6u4;VmE`)d< z-}+H*Qr1$Mn?~8V&r)|OxA9NoXOvaFjOa6~U6NJ5tE`4uJm3DW>N3$b_wiMlbOF?< z{Pa_O0D4FI?wMdU-~vG5QFAC1st$d1YExyxef=&$&!wYMk;GtCS0gy3~zYD{QQf*{;R*d`H%nn5A%irB}beb zQX}Tc%=ia6b7g1U`h|%(4rdN>QxC`Sn2$4S$2#n`MKEnbBdO1_-mZE+9(ZmeUl&S` z)BA$Zb2y~-8B$NH`S*YD!$(&|G-u)gh4x+T`H9d*w&yd9vw*L>ezl#q0bSjh=Wag# ze7C)&RP`|J6v3{zQ&p{nTnds!kjSdIrw_qdcC@Ddro^yJ{qMvQhWuB0J!IOiv~Fge zGedz=>kEiP?IH>LDX0`c4%rqasKX8U2w$xPEo&rBYGZ(jna*K7H;aoN@rd123C(-Uc)^1 z?AcRySe<9IS{FLVyxD?OW%WMoeI4_m0(?rrXas3F_m>{hTUkZ2mxpJ;nK8n7nYXU; zHE@nL0+G8Im7}nNv%yqmK@C~XmBOPix9usQ;nkfWV!=MSg{5-|%$XEVe3=}Fp$byr z82-M>@+z^eE_)%WdR(vHyuMupZ{tQj`D8iYpHj24LJs*tvX?24LejE-w;M*tcLyRi`RXNWoj4>U+n-*m59OMt!EK&148AG8xo=Icn<#Wt<97 zUt=d7K@ZIOboQ*K={Q(cL6sddx5Sv$(-Uz73#vt;Et;TNZD_gUlEgo%Kg#55UEz~N z0nQlPv2$;&f+zFb>C`1jbC-E5@06rHF{2TPV<|3pFHcL+o2P+oa64|ZB;YI*2s^t7 zS$8^+yxuynNIrY7F&wzUI4{%t3MUwBUmoUqKshe`T%B?0W-FGm!pGAfz@g9`(Q`<7 z=7c~XQzC49LQ&4bIROF=%yTJ=id!&NpI^!Qeq>V9dyQisJvS87Binw`E1KlJmoFwA z%7rbscgY9w$@u*w0X}%ce4S&6Ke^QjUJifF?6RPCyU03WPTllTpUR!+s6tC$0e3HE zsTD-kq~NHQY&`M3532%9O1Svt4xGo)qcU6!o_ZZH_{Uyib;-<|Z^3R>**fhJ4j-0Q z!KY770*M8ncfsr~=@Q}P)y~PG^`iC_G9^#XRvkiHWRy687pEFVM>r$n_$2S1v*M8S zz->nx${}#)1{#epZUUBwf`iZ@&cYbD0SsqijnXG84W{!>8}!hrJ$~|db0>ua)9>l5a*AV=23jHL8VTQd*$jMYvkT(Z^WF)8U*SzF)6izaF^-_qY+J*iD`1*!|vB4_kbM zpQ6tA%<>Sga`J$)D>ExiZ?`Gdz}=fB+SW$F#=K=Wm*B!+hqX23F3W`hoJtEJMWZBd zDF-l}?;#T82RR1`@WThdixRBOxIwXLgvze)~3 zo*5x`6V?G0T$D8|8#PN#ifQliHn^FD?80VeW!VkEawO_bwj~~f&HfJC#tX0X=PIf) zA14B^jwWC2w;&j{9Zm{EF+EOR;zz~`1DkQ*(%<`lLdG0EaX@%yT}55g3Vl zJ^OxN=0u9czwT2i+A0Ffvd@j*(YzetzR7`t;+VJFyt;E3u7WA7XmZ5#Zc{!mVm5KF zURN8jg*=3`!8P!#xu>{@Qq`EQiFh%FcnBmTkll!a{(R5?Xyx2sKw5A{AgU7dgmzwV z5n6Kzlb)UALMXnS_$8kaf3Luj!7^oZdDZlA1a}rArtF49n_RRV2S zpSA*Gr-5|%R!X}VrnrlSc-|WY^ur}{QW&e?un{YBruUohbE~dgIq%nI$=@#%+rM^c z@*j@<8mr*2jF6Bz^}L(GB`v$@PwIe2jvw-X+W!T(7MY08_&mu3y9jE0%@a!XymtiO zRdmn(VTThcAY7A6D2(C2&;~ql2;faefk1z4!NZ%)1dbD)$jC}Iu5XaS|Bk3fHM;`6 zB<>ok&T^^N=B-_B+?$Z&3levs4+4l3GXe>`Zp1Z7`=)E-|ux zwgPI+xG~G>v|Kj78uRc1ZtIuM2y1CxXP6ISh)Xg3E8uVjA+9=|_WAxuK_EW-PBv#c zXgH)=O8+QS2}Ik)eqv-g3lo*JGGx1H*)G9{6{lJ45adnmkHSCCmf$0+A0$5tO%_EP z;A~}@zpz+hm?xwQZOlzcPm;Zlv5GP?JzoBu zpFwyU@~DqdjJ)%AuE@qCc_v;1-yF#D$k$PZ4=uomnm~s6OW>}z*%V|`j;oN*5~c)W zOzOy6AN(I)STfSsl(EA+Hxe4%TwiZ;qRK}1SI^OfqdFf#9e#N{XXOcsUaiyffV|=& zwhKdio@0(>>wJwmVcV#JMrkA`YSY;^(DPCfzN;L3*j9a7pbugmwE1D>dnG> zEo4XN`~bx!65;nepaXw*=TbA(dHJHi5@&RF<@oo{@2xw;_H$puHzH2vxe`c>0M|WU zCLNu~nz&(8!^ow}*prdhBAJ5&td5>IC$^~P^jvwxPv*W%hpS;Mfs4O>s@VWGj28(H z_o{IVR>zSgl9#7>&=^kOy&5l?eJDQ~o*|xLj-U-*N3wgy;V$) zghl~p?`YHc^Fhs4u^6Z!`LnfCH^#&u zE_QdMW$nGp+UlU(F`3#@Wnk2So$v$p-G-~yicqzs?pc z?VXt=A35Kvfhu1;dBg5oR~P%i|6)(^j!4aOus9SyrK>+2)`hC$b~0Ry>{>$d`FOeE z$y)m!j&D16zB}Ig1<)gB-{1xJ(+vC=ScX%k>UUk^5r!>966jtp}^ZZ91b{FO3#RJA^EsG6w~2(^Z9%n?u<!idMgF4?#=4VyIXGp^^HR5VB1w2^&2g(%j5c_twWONDkSeE=*7eCJOZ=q4q)&X@3EE ztb#)t9?^4Lk*0x162^&`!Am8Tln0aO^X;SHN0eoV!F?W

1k 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

  • aXw=eA6-b=){p6n7dkwC+xwEV?BzlJ(cF5Y= z;X-)OyCJw1^penX108^iYX_Rp;vuG|fwW9SPxnYSqV^h6fsPXoHwct~QHc8)`ZMUq zpg+voAVlx7%PuOz(gm~w9SH)!15AsZAFPx5loRwPm;j6i;>=bIwOSVXYt;lPq>z6A XUvqKATJtxd00000NkvXXu0mjfHN?UZ literal 0 HcmV?d00001 diff --git a/src/khoj/interface/web/config.html b/src/khoj/interface/web/config.html index 4e77a8ef..1410f20b 100644 --- a/src/khoj/interface/web/config.html +++ b/src/khoj/interface/web/config.html @@ -3,9 +3,40 @@
    -

    Plugins

    +

    Content

    +
    + Computer +

    + Files + {% if current_model_state.computer == True %} + Configured + {% endif %} +

    +
    +
    +

    Manage files from your computer

    +
    + + {% if current_model_state.computer %} +
    + +
    + {% endif %} +
    + -
    -

    Manage Data

    -
    -
    - -
    -
    -
    -
    -
    @@ -363,70 +384,5 @@ } }) } - - // Get all currently indexed files - function getAllFilenames() { - fetch('/api/config/data/all') - .then(response => response.json()) - .then(data => { - var indexedFiles = document.getElementsByClassName("indexed-files")[0]; - indexedFiles.innerHTML = ""; - - if (data.length == 0) { - document.getElementById("delete-all-files").style.display = "none"; - indexedFiles.innerHTML = ""; - } else { - document.getElementById("delete-all-files").style.display = "block"; - } - - for (var filename of data) { - let fileElement = document.createElement("div"); - fileElement.classList.add("file-element"); - - let fileNameElement = document.createElement("div"); - fileNameElement.classList.add("content-name"); - fileNameElement.innerHTML = filename; - fileElement.appendChild(fileNameElement); - - let buttonContainer = document.createElement("div"); - buttonContainer.classList.add("remove-button-container"); - let removeFileButton = document.createElement("button"); - removeFileButton.classList.add("remove-file-button"); - removeFileButton.innerHTML = "🗑️"; - removeFileButton.addEventListener("click", ((filename) => { - return () => { - removeFile(filename); - }; - })(filename)); - buttonContainer.appendChild(removeFileButton); - fileElement.appendChild(buttonContainer); - indexedFiles.appendChild(fileElement); - } - }) - .catch((error) => { - console.error('Error:', error); - }); - } - - // Get all currently indexed files on page load - getAllFilenames(); - - let deleteAllFilesButton = document.getElementById("delete-all-files"); - deleteAllFilesButton.addEventListener("click", function(event) { - event.preventDefault(); - fetch('/api/config/data/all', { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - } - }) - .then(response => response.json()) - .then(data => { - if (data.status == "ok") { - getAllFilenames(); - } - }) - }); - {% endblock %} diff --git a/src/khoj/interface/web/content_source_computer_input.html b/src/khoj/interface/web/content_source_computer_input.html new file mode 100644 index 00000000..01992d5e --- /dev/null +++ b/src/khoj/interface/web/content_source_computer_input.html @@ -0,0 +1,107 @@ +{% extends "base_config.html" %} +{% block content %} +
    +
    +

    + files + Files +
    +

    Manage files from your computer

    +

    Download the Khoj Desktop app to sync files from your computer

    +
    +

    +
    +
    + +
    +
    +
    +
    +
    +
    + + +{% endblock %} diff --git a/src/khoj/routers/api.py b/src/khoj/routers/api.py index c2002048..fabfebe1 100644 --- a/src/khoj/routers/api.py +++ b/src/khoj/routers/api.py @@ -270,10 +270,11 @@ async def remove_file_data( return {"status": "ok"} -@api.get("/config/data/all", response_model=List[str]) +@api.get("/config/data/{content_source}", response_model=List[str]) @requires(["authenticated"]) async def get_all_filenames( request: Request, + content_source: str, client: Optional[str] = None, ): user = request.user.object @@ -285,27 +286,7 @@ async def get_all_filenames( client=client, ) - return await sync_to_async(list)(EntryAdapters.aget_all_filenames(user)) - - -@api.delete("/config/data/all", status_code=200) -@requires(["authenticated"]) -async def remove_all_config_data( - request: Request, - client: Optional[str] = None, -): - user = request.user.object - - update_telemetry_state( - request=request, - telemetry_type="api", - api="delete_all_config", - client=client, - ) - - await EntryAdapters.adelete_all_entries(user) - - return {"status": "ok"} + return await sync_to_async(list)(EntryAdapters.aget_all_filenames_by_source(user, content_source)) @api.post("/config/data/conversation/model", status_code=200) diff --git a/src/khoj/routers/web_client.py b/src/khoj/routers/web_client.py index 8016cfce..3e568bc7 100644 --- a/src/khoj/routers/web_client.py +++ b/src/khoj/routers/web_client.py @@ -110,25 +110,14 @@ def login_page(request: Request): def config_page(request: Request): user = request.user.object user_picture = request.session.get("user", {}).get("picture") - enabled_content = set(EntryAdapters.get_unique_file_types(user).all()) + enabled_content_source = set(EntryAdapters.get_unique_file_source(user).all()) successfully_configured = { - "pdf": ("pdf" in enabled_content), - "markdown": ("markdown" in enabled_content), - "org": ("org" in enabled_content), - "image": False, - "github": ("github" in enabled_content), - "notion": ("notion" in enabled_content), - "plaintext": ("plaintext" in enabled_content), + "computer": ("computer" in enabled_content_source), + "github": ("github" in enabled_content_source), + "notion": ("notion" in enabled_content_source), } - if state.content_index: - successfully_configured.update( - { - "image": state.content_index.image is not None, - } - ) - conversation_options = ConversationAdapters.get_conversation_processor_options().all() all_conversation_options = list() for conversation_option in conversation_options: @@ -209,3 +198,19 @@ def notion_config_page(request: Request): "user_photo": user_picture, }, ) + + +@web_client.get("/config/content-source/computer", response_class=HTMLResponse) +@requires(["authenticated"], redirect="login_page") +def computer_config_page(request: Request): + user = request.user.object + user_picture = request.session.get("user", {}).get("picture") + + return templates.TemplateResponse( + "content_source_computer_input.html", + context={ + "request": request, + "username": user.username, + "user_photo": user_picture, + }, + )