From 607a22ad1221ecc51a23831d17f76f39886a5b8b Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 16 Dec 2024 14:33:00 +0800 Subject: [PATCH 001/138] fix: tool constant params change cause page crashed (#11682) --- .../nodes/tool/components/input-var-list.tsx | 16 ++++------------ .../components/workflow/nodes/tool/use-config.ts | 9 +++++++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx index 5d29b767ad..db1a32e319 100644 --- a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx +++ b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx @@ -61,20 +61,12 @@ const InputVarList: FC = ({ const newValue = produce(value, (draft: ToolVarInputs) => { const target = draft[variable] if (target) { - if (!isSupportConstantValue || varKindType === VarKindType.variable) { - if (isSupportConstantValue) - target.type = VarKindType.variable - - target.value = varValue as ValueSelector - } - else { - target.type = VarKindType.constant - target.value = varValue as string - } + target.type = varKindType + target.value = varValue } else { draft[variable] = { - type: VarKindType.variable, + type: varKindType, value: varValue, } } @@ -173,7 +165,7 @@ const InputVarList: FC = ({ value={varInput?.type === VarKindType.constant ? (varInput?.value || '') : (varInput?.value || [])} onChange={handleNotMixedTypeChange(variable)} onOpen={handleOpen(index)} - defaultVarKindType={isNumber ? VarKindType.constant : VarKindType.variable} + defaultVarKindType={varInput?.type || (isNumber ? VarKindType.constant : VarKindType.variable)} isSupportConstantValue={isSupportConstantValue} filterVar={isNumber ? filterVar : undefined} availableVars={isSelect ? availableVars : undefined} diff --git a/web/app/components/workflow/nodes/tool/use-config.ts b/web/app/components/workflow/nodes/tool/use-config.ts index df8ad47985..94046ba4fa 100644 --- a/web/app/components/workflow/nodes/tool/use-config.ts +++ b/web/app/components/workflow/nodes/tool/use-config.ts @@ -132,7 +132,7 @@ const useConfig = (id: string, payload: ToolNodeType) => { draft.tool_parameters = {} }) setInputs(inputsWithDefaultValue) - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [currTool]) // setting when call @@ -214,8 +214,13 @@ const useConfig = (id: string, payload: ToolNodeType) => { .map(k => inputs.tool_parameters[k]) const varInputs = getInputVars(hadVarParams.map((p) => { - if (p.type === VarType.variable) + if (p.type === VarType.variable) { + // handle the old wrong value not crash the page + if (!(p.value as any).join) + return `{{#${p.value}#}}` + return `{{#${(p.value as ValueSelector).join('.')}#}}` + } return p.value as string })) From 41de7e76ecafddd61028d53dd7f5ea045d7e0ae1 Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 16 Dec 2024 15:06:03 +0800 Subject: [PATCH 002/138] fix: iteration output array type causes always outputting string array (#11686) --- web/app/components/workflow/nodes/iteration/use-config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web/app/components/workflow/nodes/iteration/use-config.ts b/web/app/components/workflow/nodes/iteration/use-config.ts index 6fb8797dcd..fd69fecaf0 100644 --- a/web/app/components/workflow/nodes/iteration/use-config.ts +++ b/web/app/components/workflow/nodes/iteration/use-config.ts @@ -52,6 +52,12 @@ const useConfig = (id: string, payload: IterationNodeType) => { [VarType.number]: VarType.arrayNumber, [VarType.object]: VarType.arrayObject, [VarType.file]: VarType.arrayFile, + // list operator node can output array + [VarType.array]: VarType.array, + [VarType.arrayFile]: VarType.arrayFile, + [VarType.arrayString]: VarType.arrayString, + [VarType.arrayNumber]: VarType.arrayNumber, + [VarType.arrayObject]: VarType.arrayObject, } as Record)[outputItemType] || VarType.arrayString }) setInputs(newInputs) From 9f602f73ebdc455b7efe0991cdc5d9c4549f2e25 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 16 Dec 2024 15:39:53 +0800 Subject: [PATCH 003/138] fix: workflow continue on error edge color (#11689) --- web/app/components/workflow/hooks/use-workflow-run.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/workflow/hooks/use-workflow-run.ts b/web/app/components/workflow/hooks/use-workflow-run.ts index f6a9d24cd3..a01b2d3154 100644 --- a/web/app/components/workflow/hooks/use-workflow-run.ts +++ b/web/app/components/workflow/hooks/use-workflow-run.ts @@ -192,6 +192,7 @@ export const useWorkflowRun = () => { const newNodes = produce(nodes, (draft) => { draft.forEach((node) => { node.data._waitingRun = true + node.data._runningBranchId = undefined }) }) setNodes(newNodes) From 967eb811120417a57f95eb72e3d9ee9086a929af Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 16 Dec 2024 15:49:17 +0800 Subject: [PATCH 004/138] chore: bump version to 0.14.0 (#11679) Signed-off-by: -LAN- --- api/configs/packaging/__init__.py | 2 +- docker-legacy/docker-compose.yaml | 6 +++--- docker/docker-compose.yaml | 6 +++--- web/package.json | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/configs/packaging/__init__.py b/api/configs/packaging/__init__.py index 0c2ccd826e..51db50ec3d 100644 --- a/api/configs/packaging/__init__.py +++ b/api/configs/packaging/__init__.py @@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings): CURRENT_VERSION: str = Field( description="Dify version", - default="0.13.2", + default="0.14.0", ) COMMIT_SHA: str = Field( diff --git a/docker-legacy/docker-compose.yaml b/docker-legacy/docker-compose.yaml index 4392407a73..6c38b5c4f9 100644 --- a/docker-legacy/docker-compose.yaml +++ b/docker-legacy/docker-compose.yaml @@ -2,7 +2,7 @@ version: '3' services: # API service api: - image: langgenius/dify-api:0.13.2 + image: langgenius/dify-api:0.14.0 restart: always environment: # Startup mode, 'api' starts the API server. @@ -227,7 +227,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.13.2 + image: langgenius/dify-api:0.14.0 restart: always environment: CONSOLE_WEB_URL: '' @@ -397,7 +397,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.13.2 + image: langgenius/dify-web:0.14.0 restart: always environment: # The base URL of console application api server, refers to the Console base URL of WEB service if console domain is diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index cfc3d750c9..eece49b113 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -292,7 +292,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:0.13.2 + image: langgenius/dify-api:0.14.0 restart: always environment: # Use the shared environment variables. @@ -312,7 +312,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.13.2 + image: langgenius/dify-api:0.14.0 restart: always environment: # Use the shared environment variables. @@ -331,7 +331,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.13.2 + image: langgenius/dify-web:0.14.0 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/web/package.json b/web/package.json index 44e92806d3..c2ed7502f1 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "dify-web", - "version": "0.13.2", + "version": "0.14.0", "private": true, "engines": { "node": ">=18.17.0" From be93c19b7e99057794cf741ba42496620ebcadd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Mon, 16 Dec 2024 17:59:00 +0800 Subject: [PATCH 005/138] chore: remove duplicate folder with case sensitivity issue (#11687) --- web/public/screenshots/Light/Agent.png | Bin 36209 -> 0 bytes web/public/screenshots/Light/Agent@2x.png | Bin 103245 -> 0 bytes web/public/screenshots/Light/Agent@3x.png | Bin 209674 -> 0 bytes web/public/screenshots/Light/ChatFlow.png | Bin 28423 -> 0 bytes web/public/screenshots/Light/ChatFlow@2x.png | Bin 81229 -> 0 bytes web/public/screenshots/Light/ChatFlow@3x.png | Bin 160820 -> 0 bytes web/public/screenshots/Light/Chatbot.png | Bin 31633 -> 0 bytes web/public/screenshots/Light/Chatbot@2x.png | Bin 84515 -> 0 bytes web/public/screenshots/Light/Chatbot@3x.png | Bin 142013 -> 0 bytes web/public/screenshots/Light/Chatflow.png | Bin 28423 -> 0 bytes web/public/screenshots/Light/Chatflow@2x.png | Bin 81229 -> 0 bytes web/public/screenshots/Light/Chatflow@3x.png | Bin 160820 -> 0 bytes web/public/screenshots/Light/TextGenerator.png | Bin 26627 -> 0 bytes .../screenshots/Light/TextGenerator@2x.png | Bin 63818 -> 0 bytes .../screenshots/Light/TextGenerator@3x.png | Bin 122391 -> 0 bytes web/public/screenshots/Light/Workflow.png | Bin 22110 -> 0 bytes web/public/screenshots/Light/Workflow@2x.png | Bin 62688 -> 0 bytes web/public/screenshots/Light/Workflow@3x.png | Bin 147073 -> 0 bytes 18 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 web/public/screenshots/Light/Agent.png delete mode 100644 web/public/screenshots/Light/Agent@2x.png delete mode 100644 web/public/screenshots/Light/Agent@3x.png delete mode 100644 web/public/screenshots/Light/ChatFlow.png delete mode 100644 web/public/screenshots/Light/ChatFlow@2x.png delete mode 100644 web/public/screenshots/Light/ChatFlow@3x.png delete mode 100644 web/public/screenshots/Light/Chatbot.png delete mode 100644 web/public/screenshots/Light/Chatbot@2x.png delete mode 100644 web/public/screenshots/Light/Chatbot@3x.png delete mode 100644 web/public/screenshots/Light/Chatflow.png delete mode 100644 web/public/screenshots/Light/Chatflow@2x.png delete mode 100644 web/public/screenshots/Light/Chatflow@3x.png delete mode 100644 web/public/screenshots/Light/TextGenerator.png delete mode 100644 web/public/screenshots/Light/TextGenerator@2x.png delete mode 100644 web/public/screenshots/Light/TextGenerator@3x.png delete mode 100644 web/public/screenshots/Light/Workflow.png delete mode 100644 web/public/screenshots/Light/Workflow@2x.png delete mode 100644 web/public/screenshots/Light/Workflow@3x.png diff --git a/web/public/screenshots/Light/Agent.png b/web/public/screenshots/Light/Agent.png deleted file mode 100644 index fe596a555ff81f7bc454b193246cc8d2dd944f77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36209 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 z%0AZ>5)-;GBX7&9e!>M%%}(CPhaS{Be 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 Rj1m9<002ovPDHLkV1gru%TWLT diff --git a/web/public/screenshots/Light/Agent@3x.png b/web/public/screenshots/Light/Agent@3x.png deleted file mode 100644 index 0d05644eab427d950bf35b5b287c5c698595e33d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209674 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 N6}(4ph*ybtv7o@- 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

Eue zlgPGG(iMg5RrfD6*VWfQN>i^=aG}u&`e@MZ!{~}k`r%#*{TqzkHE~zbxd1-UgZ;6! zs$`LMXG6ija&>)FiXD~#04kRI{`{;uNLEgkl1mY?>=KlDRAONdn;w-x=278*=$p&R z)XcmJ)s8o_BLn{ovaclAE5aG{O>A`8KujRYr~;1v;bk}n6>ym6R48fvP}nViwNP1p z3WT^I-R)e@Yc(Meolf8SGnAv=Aw(#7c7~kJ>eoeHC|OL24gNkNeTWxzwL_`aLrC2H z*w{LX>mvp5#oE*2|eCf#9e+Gf5pJ9fpQLj{ciw!*E4w zcN_EEuz4~0lslO(rK*XXK;)$Qddfwc%b(yG6_-UXJSRCzm_Jvi*<^jnjvul zKb+5sWL}@oqGp|^s362Job7zh&HvEiVH@F_-R;w2IHDl>CSmWV9XzgUt@f23Wo(IH zfV4NWBF_2)EO=j376cm;7Z6=`?w$GG+WCZH%MuIN9{XOV!aG7Qi&a{DW9ldHh4;P0 zr9P284(ICA#Pf%`vsJhsbGzd@%t+tFpphr5ahm@*&qtB8{&p+ z%q}q|?cGvMVUfm(t$loSS?e>&%8)ip3G)v%AIW}2=Wp?q@!|7X4xB$tnwC)8(h?Wq z_fsc)CQ<5f@Y6zK{Y2+`Q_)W|+;3#4Sy^ll7v4}OgKc@)1AEg0_{FlArjDwS<#yRa zZ?C#QGA+CKVWVHGWZj`R15h5FLRhg00RERjCn}X-1A^~-+Ubc?d*ct2;FcVh&wtUU zT z&IdVe=m^gs01&C@2LYm8Dya~cHHPSY>M_!L0>;?s4q}LRbSU%1n3z@^SH5gO=^&%Z zM0bY=Fg-d0k73C(J$aLNNnY=b&FPa(w%oAJ(9?Fw;>pq8z>5qB08VDruqJp zg{qs5se%ogl)U|p`@i;DKSJP_k?H_WHCsE-D}f;uSYS`w&OI3jCeDVBz&zJ<1W74% ze1O1@3lp_GgQk)Rhw5vAY&gQtItKKxoI_^azV)i%KgFMTWh5g}bDuqRKX{3L;rT6L z)3PaJy-|$7S(;2Sk9%3G*_Sap@HuiXkj97#ep+&lm&RPU-c>!~fpP-D2wjZ%hT^a% z=u+!QW`=kKt%8Q}dCcLgV9FO1kDs4<7opiqa*JaVmGl!Sw~27APVd}&(eeBsnnE4~ zd$!Gp2muV)JO(`q6J3^fAt#9ZQec!hp7UKOiox>09V0MQ3-nR3Q0Ny*al5st-WCp@J7mX#V|B%wIM zCF^_Y4Nrr$aOHJOBW?tbm+Mah|(?|g#lFAXqfukj=ZECE~XAxSQ?}~#^Nvm#DNvl zMma7Ys^jIDKE%l7e9`A5k36t?#kDtxXWs-iQ`&5;+!EOU`8lFYOdZKPj-xQNX%*5s3WYt@EAvn>q*PDj{oo`Y|g0 z+^4_1@)_BEg6Fs_M!B=*k8J$npF#(X5b^4`Qx}af2g&BNwn#jlxU0p2C4S2W9G`s~ z-+%fQuq`z$BqtO_v%-qwgkmn7^kbrPO~BQ}5;a@YNUOGMNv0VZXd6f_s7rd8|IvRy zIl)&A^m{-RQpsq*GPP_~5G=zwM!K zUT4jgfABDan{k)|%wo`qfnX& zsk$hH%U8ABu`e?Z33Gw7-NtfHW{GPmn-=baea;u zHUBb@1yr zM0G-4R+P7>3*Qi!M24tzasacbKTfbXKF+(YsK%ELnI*m$_YzJjNJ3*HS)V|&DsmV6 zs-I#;ruL?PO;>H-(nkJeOy-jTtbXLCD=%dchfKT(&LH>THaarc?`7OkfiOd*Vc-GA z;rbI10A0!={Hfb5cdB5J$PYhr(j68zbwSjF%D!~ruL3NS0!qN&(H{Ta-F|w3D~_m4V2FM6Yc=eviRY@~Oh# z9B*7~sE;^w(~ZheZ=B!f(jtt1`xcwLpJ`HCI)0gxPILo%4iQtfPuq0$_ia({bkdf( zb;@GwII8e?70jv5^)N^ED1RdF)u6LI7-l8xbe!N7Y)MQG1{L;~wV_ zb4L$b0)iR-9emmt<2IUQ?At-8bQ8EP&6Rs}<(%V!1$~^ayVDFbE*|f+a_~MmlyIN3 zGss3<#wjR~)e;rcC3JA+o@>PYnd19WkDNS76_vqX_W^^A2{t?Sj#gT(T%gLsF&|%F zTh^C&WvV}g-ZMI${-0$kgbP+E;RG(a_XlFGsga`9Vu_bHn0U@|t#b5Ov!+^08Kg#Y z=OFasOtE9W1I+j9Exp(-$L>T=Kl9E?xsDe`>VIzN4*Qg6(D)MPWDA>3A0MpO3i#gH5F71Bq)^b38!K(n~OuP2mECg!uuh`Q2zt}3RDG}8(Qp~lLH+F zFBYn2$Kuk-P|UIA`ae^(uBHAez|<;Gim* z|D+11c#vWixrG00sW75avwV@!R@#yE-wIRiO>nCcUU5Oxo`isNh@T{|@?(H64==mD z`dMqu)^muIhH3PsFJHY(z;35#yC@K9W+sn%>0jpW1crsIjSq%pGR|>J>^*}R{ATto zpNxG$!8N0x9NCA9oNlUJGkdHv-z`X+eWaq76hFVh>~A6db9i~g{2^V*o6Erb@wgLu zwu;r|8wi4Nt?mdPS;6`uZkl5r&lcI?-j(O?$`kxR)JD=cqFOYnI2+G~hXGLJIzrwR zi6iHnzFPp7jD7y`5h+!s_91?Wl0HJkv~Vh*q+5B_-xHd4CCZ+1)*TH8V;x2T$f2_*=cRy`5Bt?CHjE8uRqonQ$qvev=4&UR+9fU;M2rQxh(doJg8} zGW=`%8TvOCQc=tmFQj!Y((INU)@p#1k@qXCdWoe^?oLW{@6#_@3?K}k z05veDW7a4c!p+7^WY!J&w>QUh>-FkzuBgMf5An!0$xm+1qUX-iZe|liNSmBR?poi` z=n83Tafm$RL*9Eqc5I;US#N^+Q*2E1YsqAjNt#ksm83P}lZEX9%)FO@+Am}0mNcWm z?gRQA)MA9;{_lF#ABnms+GtxjAm%fhROa^sBFKmp8#WU93QPqjXjk!aVCRyUFui<= z$!{@C#zH0=U9?EnRcw0EtUhj|w8^yhhfA1$>$B>n!snCpOl>qhGp#574VD=R2fyxr z986Cf*jOrSwpRYVKJsHXkkSy*Skq`=xRk*k-gaV&C`06*L+G`T}k^w6cxpy1zSUi{E0)2U$j0$Vp!*4 zM2H(2qwI`U8N0}5h#OBNS6H&BpH@T+msJmy-nZSbh}g#2W`(?>w%O)$MqtOycbN(m zQzP!U?RVB<2XE|1dw1TrN~9;9w5)-lg}` z`3K#`OC!2jf^Q`=>7?gyg9l^F-FAS4#H%pu*fIiyt&ONrkpN;^h+cE2@fk9`w`AG1Z+)R* zEPKnOmNKT!mmDsiAqVD zlx!%Yq@?X>ICRt#&A8EkaHdE5I@pq4^7ko*L zOWVp_&N~&1>}4punFq4vz&^(pn}1L2CO#!9&CqP-KqCPFj7D!9$w|BjM)M$M{UEs@ zBP4(4b=Z+67Q>`I>-a+kJ?<8n@{_zmN%IbVgT#qW0r+sLpkio3!W$TFXrfPV=6 zx$Huz@I*P8hib=Gj^*c39u7)C#iYxLUcsK=$H^c=6mHh0qH zce5q%ZDm3Qn67$TWl`nWJ`{G+gI> z>rLX#Jq7?(BXaDm{HHuTlyb`4z!mn~8NT_T9>tPujoLuqA=M$;VHN@IP_s$1Mfr=E z$Lcb6uU|(&6-^6TS;*Ek6fe(XVc3&28eJO@dD65-UA>}qr6lNye81vRc|MX{@QpLD z;1L8^5NX}nlFS1CaR~ zkJxQ^P(Kky*xGWP^1ujsg%B1nzqy9bl5V)J-Y_tsa5h`dK&BT5d;Qnz-Z+-O`N~1} z7q4_aVYu6Sf5;pvwyqFVjC40;2)ln-z=hTo+cKuRk^%-O=5zE|Xn7 zs(YR!>lyNb>ZUhQ2FRT*jp@FSdLC*8{z6NG6~tBKa7a zsqeySW<0-Qx*DB7hxfdG7glPxYYdWgnL639Zv`HF8;f|nWfE4cB47Z0Pz1eY^WKYJJWI{=uw^gjNV)=QVa zU0LL=2x|zBt+L#nGH---;E2=T5s`C+E@$HX*ZI$BPup5s^`#H-u|3|F#mVQ{J>*m{dl3D=pJr;vY{~h07H4v2ph3NpBqODJ z_<63*xlCvU7y!lM?ZRBt`petnvGR`JWvIF$Q^xaytDeT^9=40JjrN%R5}gYAb93x< zUCedl!z8&CU6yq?N=RHReOuEo@tX9)eJmY$S#OJ<2(Q_+2fJ`E%b0+$Q| zYv_OkR3l9Q=oW!YgSpA4(=6E1`Ma!b#M8S!Z%HioR7CZ8v0e0^l#biM3v-N9&eH3`MJ9~h=O)2fA zzjpL9cB1o!_~5{ezkzWzNFGC6Km}pP|A*htmI}WYHBZIAwJ_|Nd_TngnY;vI4pC>E z7jR&H>i_G{S$5v>ox1jayO~LT2RbW({)y%p9=?NdTn2kej7J9bQb;~&dw-Ef0Jy*a zKe=ETBaE)d4+A4kb1!GRSEsA$7c@SP@T)-F`G_CrK*ayF*4_4+ojU5U?jLH`)GvhQ z4vy+{a90k#ev#iq$lu0d%N!Qx&=}#2k2qQNTF1`RqMhZ$(Zc;^R|!lbZ)f>2ej>}- zx~-!BL{163`}$1IA~3~%>)8AW#A1V;#mU1w?G&X%*S~C^pVDpjM1@@Jtcj1|w97YN zU4O(=xpwG9VZRQ>_%A(qR5U*byutr-b?_e~DZ0EAri+RmV9I7~FjID#vYx&M5+E-n znz#RJ+N9%(+pj04txEs&9Dfs-PmjZ1tkxRrQeum%5BCVZ@^ z?^Q26Hk*p8(K#&Aubnk4f>vQc}ouqr@hkd~FY*DoP z?*a=cf8M2t3HXj>*43* zbZzBfK^$KfeSGgE(_Tc^^cr*K)#XICmjcu+P`PLnD^b|04fIrsC;cNUmDW%x$ZMZn&L+Gy246@`(BDYBU4u?q82y^`y9-O6$Z4avJk9#DY_M- zP;c7wkt*zBX!9AV>BT9G-Kx@#T)>5M`Up{nXv5LVHJ*-*@3EwpmLjUzTCPyLbLAo&AC z7~4@3EpZ`=giC$pph@9pBMvMhtzLx-1xK=!e$71D#3w5S$7hL?9O*~FYx`nz$F7kmu z()+W772T)lmp%2O^+iiH{afXk=dT=`izr{RSZ|oY$s$X>M!bOLUC44RxuFZhU0|3dMiQE=dL?~qGErx&j)KI1) z`lI;2d_(fp1$;~ETXKfV^71l^j+S=g>1ui>X&Ls|bH%Vmk{AL2Zdua<)g4uP;7|S$ z#vS%i+|8Fr{PpUrU7IV&0&>&#@fyN-SvI*Wfel?Cf@IN}C?>H%ILb#t0eLThQ| za&t|(qb&8Yw08{{7b;M|#4>tsavu|BiVB^@c9pM)f>y-tV9hVnzOWwILN@ zcQ3(EWytMMzVOD3H#Rw2DxDA)pTb~QEWnge`5a4DD7p~ftA2plySjWNda3wS&5^5dfB!V}t1Pcg+2CV!>7Z9nUl4uDkDwWtNCSm{B`kYw zX=-QKhT1%%WYFz7Ejae(A_S-=x~1WB(!QhbEM7hIpo~li*7J=(qwN}KLz4$B6`JvNU4Z7a zBI(|8h2o{9xO)pF!Ci`5 zf>YcnS{#bCr9g0sy9Oyv(4cRg=l6X7dhgkD?m2sR=kCnx?!CJ^GoP~&gv2^!;?X)) zYqX17QJuK-RO`!*##2r2=6bfbB|L3&AFg)hOupkZ1~JZ?uq7T{TjrosFhBhe!B72kE~)RYu-}-&=@bz$t5wp8Xx~N4OHKU>iBwX!zS!_{$N! zeRiX*ID{H%%&(}iwZ$SElkBSc>=qVSQf*_+L?$>4az2RWPx^?3kRXE6_grH@AvQW( zJwZj<&?E0lYSbmE7e`#WW6KWq2+7mmYf7$^lfLk1d}`hY-~gKgB&Xht7) zVb6SYINSD2S|WEJ=GPo*B2a*i$_&dce@!Dl{*NWOk(AncqcPq#R|R!nr)D19WJT$4 zJ&CN8Pg;ktwyGbFHcjm8>vbJ?rqPyFWOprI8_btc9v6E(eK4cWiDL9j-EP3Zyt_W= z9*h`X_Zq-Ly8YUM*GA-)tEfY*Bu|ox;$O_(Jo4shp-czZu?zr?RygC2BG^~_Bf8&+ zX>X|Pz+GM_>6nW`OuV^LB*FBa(-qvNCAabL_c= z5Bt}~a6j~>yoj&PK`2TnCquvQmpdNH1Hdk?`64p)jnflwq#QpkG-l9s;H*gj%RVG8 zagZU7qqtohB+h-?LFj)VjtA%b^*bF4$h)f-VMqvQg;_{|Q*#UzKw!j|XxG5#_Tgj1 zBi0#1yXL!QjyCTt!@X&$g{t)_b^-aXpWZ0+cYqPFc%x)c=+IrQOWM@rfrjMM`@!Mq zkh{zQg>76*tAK>8Aj!FppANy-ttflR__Rem+!cY)15(_D}kfj;Z1Oj z-2ETWKq7V0*r;z$^My0%Mu0LCfirDK)u4#duS8p229js?^!5fZ-9!35~TJCmNB*DyS5lshqm5>gUD34>BQCE}KUw)*8a zm_${LaDSr`MBQp1^PHT=`H>j+k0ujv{j7bFOPI%7tPl{e853AB!$gu}`bd{{t!!irWbJbap*E^GYR^++Q z@oYIXX|icDvl+6L-Ml|oy4BmCnI^Wg=CY;lm;4LsJggT3o9Vu4&Dl!hDpl$o_ne{&?+IsdIZ{tKT8HOmI~ek7G6mpDPVr( zZEe2$F`HWS)5txut_7m_iFz1bzi$q5B&GzsQQvo??VE+u$BzP`(N@rLY^6pB%6Q-Y!h}F?HTD3x&S6-+I+VOr z%%dD2aFqOb;yj+_#mOoqJ$h`Bockna= zn9SNFT~m2u%nkYI{DLdxmplqEJA&x?lKHsWT1}wq$?u9-)Ir*5MX9{$Kusqah+GBY z!UYX{+p3~D)o%D+StzVR4i=HfaNi!n8C50qs5(!>|7Bmivqjpx}|z=oQE#7D{G`XU{OxG@+$8KUfO1_`@nl1CB_SS#3%v(-id9LeZi zmQ9rXRemJ?_GwSwU%JRicSMdpK0^PrxK)gxSYG95F?QViSjFPi+~JTp)c2gn5)!!< z^qvU59f=}nX;o0A9`+OGGE|gT5z*%Xyb+1mqJ#?p{fV%=JB#Ry_`WuE$BYWOv4*X0 z4Z7r&to)!wecde2eCli?oY+%G*Wk}KF0xrz-R zLMR?+Yk%Ls&r3>=N#N&W5)Iq(E5MRT`;BfM^!AvdW?o3Dw(TOmCvF~)1va#JO=VlGXQ3n)1?)ZGzf9}BRR$7>Qx8m zsVs*c#{q?TeXvyX7jA0;h(yon5ajy)4B>jxdm`&hx`n9*ZzPJ;zd3b`lcD=g%Gy z%N#VGnE^mY^O(*ATXJS_Pv70D;$1Tc6);?#MUm*k9vk$j?81BRR7kqU+ODpDgdkT4 z2@r->9m<<5{!&yU(iNm$lnX2)`KrS5Y<|V{YAd1?BlbE0&!h<=j~Q|7oz-Q(#TJ4 z*-S}ZKUqg&hh~N}XtN!{_aCG*qN&O0l9pcibV`6Q!HIY%4FSvs82iIHALU&u7BWcw z5EJp32wXz|K7dcD91sYEG2!Fp9?V|4 zKlYJ%gW1X)vPdkSqbLcXE&l={%4=(I0|pIE*U{a(b^c+X(?3U^c4DDCiZceMunOX2 z(n+H;aVPyXGaJ9La2UNvlGqZeROUqr&xLRLJsS z3=n)N2UyYDKEpy-*a`?vrAsu-wK06KTxI+sH+gl>0rv&P@aU(><+Bmz&K6<&PflC4 z-#x#tk+o`61@SxVBkA5G%85!MeV|9NjcnVB&mNm_)7?eC%y5j{+4lKV1+&@R|J$<6 zmS(b%`erKP83__rI6EBI!|DDNmA;;vIeeupISo5+xN<_T8v#$dbGa~)Kh( zYSFPvqx?vMrCbMKiYqVSZy{m|P$4BnxBHU;UI1_a{dB@;BDj=5ti+StCfg~1L z3OQjOz^Wb2d?>wvL_g5j`9ordF@S`>b=U}}+Z{f$25H4bi40A5hhnvMjon;-II`8= zXMAIRbY~6OOhE_;m06*NwrPKOp&c4GmzejO6Oy3r`Va`vEeeZli7@x&F@td@5 z`8qbOQKcVoWE4!nt{2Tr3^s9sCXQt5+9t!eg4f2jp1d$+={F&qc|C$wEm~I z_8B^eIKpVaCiLf{f#s_PH$>&RQV7q%z8W-XMRU>M;+G2xL+eiH8hoxq;nbTFU(pU4 z%oUtv8RkU!WP}?02<}G?zZ)-iT=RJZR!S3``(e)d$)CL}D2r|aKs?%&Gs@CbH1b<6 zNa67}BB<5pfRb@_3nmbqq22s=ZMfg9h&aId?>^ls_>Rt6;q4e*X?ofkE2f)}gNmc$ z@z<#><;}b=L^_fA=jQF6PpBkuQ1&$i0z_4pf^b;WF8qwsw^Wap#!$a--@e!WU7*`C z*6r0_0W@kCvCpU9#w2*Yi`1VTDukR&c!s-Z(w3WDZ}04S=0}ss&?X4+;*wy`nY~Vu zERlfw{_VJ7PtW0|pG#GuxF;Tn?}Z+X6VAM=ej8+$KVT7yO_h!JmA4|}Kfht1RG}{# zBc4A7PanuIJcD;tB+D2??Gr-ZbeWo^`zRBA;C&2LZ2Pvp8JAu47aTO}05Y|ODiU0S z?M>`&li4ENm_z1et}216v|m7StY?X8Vb9}Roq!M5JoghilzOG}i_Buk2975l{> zWhFgvVS^d$SwE{_6!5=(!hQNfV+A!(BdVj&dV2>a+By{Pi0Fdi99c&~~H!SIz{Q37^j6~oEJ!Y97e{fnc6F079#`0&iZ7#stq{(U`#DrLORo-AWZ7|!|PxE)+Tyn zs-*$&UTdC$$vb9F>YuPuR+;$a>m2k3w@+ZkKHN3Z0|7uS8@E^8f7Q@NCETPhTK|ojAO-X^UQ`##nFHq-W@bLK(pruVgf<}wRhme62 zxUd+YA(g5&;ZO|uqT#Cc7k>kaEXuTP zgEw@DdnFg0(Dp~BS{S2v@Hq7%>OK`&0n2Bm*^UclQcl^-A;sCUPr!gooq*Hns`aXlpzvv0#C0)uL=@e*Ds=Cs`?LTS;#;>o zky1<&Wh4(%y=c&hIU~aM034S6Tbwf4(*p~PFvbMTsLB0Y!Y)>uoS@2=s=+|} za)S@FYz$iFHWet_bbva!d0L7|q0G^sK~0l2%`}K_hPQMRY1ie~ciP){^2Zx}hGJxu zr5C&zW#6vUlC>@DzP#m6n;|#atiwe9*d}PpG84sH`SXjB9MML%_5@jfv)X*dtn2#W z4I`%y=CMjLV8DSLKPSc?x1YHI_5LkZ_J$ArT{0s9E$76x^!3zFhp9sY zQ9AzF7zNEM@nbABsO~<>gRasVU%g?4unn$oO{{|G3BM$%WLU90zaG=dOCAQHMo;oV zUIl!+%Y0?zLt?M&U&bD?st&?{Zb0+y-=9Um;dV34Ms^|mC zuaadXOA%D?2diJw$UmcL^wN@&i_TMom-@6!r}T(Lp{FxXp@28`lP%Lv39q zpyk_9_JJz|AzG(=WA2$KkqK7qQ8_typnx>&-3u1!wWrqE!jj;R^p(v6jT5gprj|OqxUTs)szr^r+CoiowuUy} zWOjR^j1>1tT?a+3#&o-XnJEi8Na0up0`q5dh!JsDuglM!tI{*ua#x-fO}m_ZC5g8UMacpQcPedkUomEbZ;A`<-e`1w z8+u8bG`%5d!RWY=cfo{W(<0oB7AL1`%k0@|oYpAbczB(w+0X=WsR}F+IbD%G(`Um5 zHt>sNon|2JdNR9m{2X==h^=LDj6TZndnu{P;H$OV=7?beS&NKwmnY$Y^<59$gambd zQ9WV(bU~V$jKmNi&%X1Al+qOo4fJwy4`5ah5SC|%nUGLZS^Mtk(?h0&+_?y7%~ICHL>)O8{6m*U3PnOfg7eJ5)OUwF7tYrasqu&4c0 za|D-cgC`29XhmUMCFrlx@iw3+(t%bYNHSE`P9o}lNp-xI2m zUk}Jva5g7Y)bT$dj5VIt_FbiQ7H^fE=hj=y;dhc4_AW_xf}HCScsRw+R|{R%Fayp3XMmu)}yhP+6Sxl0a$@GVJczHA|C8&0gBKG0+x z5ra>}9W8g=A3+1V@Tcd9O`RU-+msz{hA95RH{F^ol3dk+zxUlFV*>54>q?}=O+K`K z?udS>nG*eacEm-ez4(<4MEbzvy!}c%G^_n8?0sR1Gi*@AaKdB2M@N>2LDlFG_S()u zb}mMBRuDD}efG4+Fy9IOSS!^65)jX@J4$p-tf~CKCz!^3#}viGUa4zDQ9<`$!=;v(NX?Ku(0=M{u&9uJO2`Bp9_p zVKqZPsA)2Lqfwg;yKgI$=l!vDGtcmWQeHp5WC*muaA2U$lzpYP9M>im9-VjkdzT1lQ3P5 z_3i0B_PRp0SS54mQ>)m?F4sw8<4H*4N$~5P^+H@5Qb>AUYKEOsy*>Z^lZQo`U-$hS@Kd z*nbUGEwtazu56|l`^P19ebrjw^q`dGGRDYAGp`f*DE#qZU=`LS)Ws^Re~Mb4NmJiy zM>VG7(+aF`Fd*%8`dBtQJzs9-xj*v$)Zc8Q(Qq{MO1Gx~#Q%JbP9R_V=cH-TRZ%V* z*7W@9`VmVv3xoE+5`ETJ2YH$puC2{cGju!^kx}cBlqpBb9NUU!93XYs1d+!~>=4N6u%q z+}7DWMtb2=U?=LXR~gM!{nmzp8AOE9?vTainat8Daop5^h&-)noDNjA4 zR9Iu<{}mHQ)1`bCymnV$Oaksm*}q`AZfPL*|9&E^b`U_8#+IgO9H{n@*P>+ce!@P` zd+Y-LRVU@(!XHC;3_h)AXC1v~wIUpQqePd@t^M9{xLRDk!2i7e!dL9IN%iTw+>+3J z+xl5Oxo4j2gBc5S2IC&7&emm4CSMy9KNW9&{8Z=dccXTzC2&wc#y=nErRF~E>?7p+ z_6J+w7~leDK(AD>JHohq-VYruf^6s#P>uMb%erpzf$}*Y2S{+pTyrVc=l& zz%D&+quX+LOJa|cw0?QPl)PjIpFP+3%WfWcoCib92ca<7a)O_n6gLKEo+t9_Wg>3R zYFo40eb<*Fp2ac40=2toPR}`JX#l8uH#xuKs3+dMEZac9+KAkQF4X@>Ca!R!Vm8mP zz4aKxVx(#w8Sed&^+Hsp?k9Ox?TC!{mbEf8$7Gw0O$dW)Z^nCOXO}~@78{HEz1=L_ zEsqcFGSqE}%Qp{N9+`h;utFT-_a7A*4|<6gKrca|CEpj#=wGU6#$;f(EkO0_qp9&* zRBD0RL9q>@9u)}f@ayV?i}C|r+wNxRSY1z>kC&NN@)k}6WDTkI{?ykm1G`7PE=802 zi@0lCm=dm!`?#H#P64e4uoYf!^I74klv+=0wIQfWr07wA?aq_vo(s|W4O{t!S$;r- zi|VlcdJ&|fX&L9UuDEGbHTF&|Sr{6S&JWu|w$RHYU1;A(wheFl;%MrAcVXXyM#*K6 zO*Z4NU55s31c-@7+UbkK`I?%pZVCmJ7eej{>hl`1Fmd^p-aPG(888XE zO3nq$M1JyF91^gaHQb}dWups;j*Uh-!OR6;unGTIe1YJAv_iCkTmuxRSY!3t$6F|K*f}>=;5xW~+1k$uNYl0V{iDXSmAoJ=Ca<%d z&ArmmT&VylnkT8l{C>G28R}bk#t#`bf*FCU0>0Z#8=_CJ|7b8^-HcKB|9yCHGzk|s z$J=Km<^%kMtj>NHuj>_k@!EhsH}wLAI=C4%lU?UJRHc7c^Ls$xS*gqCEgDoS1BrlC z?91Nt>?rXxEP7clIW!D2b$ow?m3Yf%#PA?Cbu*0KDt0adPPvNcW?|JQhTJ@3S;r`fmtcK@?jso3FrZ^aHNS@(ct z@g(`^L<=#sm8uvSTR}Zn(#DWDL4DjRjjH!u=SJxCV0rE8GL0D_!^p{GXYG&5b+0h} zGs?nB&8)TLh;YY>`cqLh;_WOxK_Lgt&*llU_tbui0V>>td~NUu8jWiD^~Zr*UYiq< zQvjKEx%($|eF=DZ+z-#Ft$uf3`!f)>@+C7Sr4*2T+Dp=g+~F0)*-Lo$+h)sB-@(7W zv}{$aX`_GB>8-W6`YQ{Qg8{s^9oTNQuK^~W&HuKq>%68ycC3aWk9<1VOnqT34DlK9 zD?8749i0CBZvOp4W9_|@)txW2dr)0J?eQKROyHhw0&im4?1NuyKbx!tS~>f0%%uT$ z^K1iWqalM*2okRW5{xZy6u@-CEPaOzzI^tn%YyP5%SgSZdd=I;JTE7y18|&+v;Rt8r0@h`eA!j4TauSj__tQ+)v98Tf|2i zpbK_gPocf7v#v%D-Hh%DfH<$E+EK~lM)$@_O4fGq!Ra3T-dRpdonB-gMcUMJ$QJlq z`trr7Nnr;+qA&&A+WbQNyaP>5kP&Nd;(IxLw^&*0D8-M1(?iG;rd*5@vdEvvpyv2o z;-t8N9d2}iu%6xbbE4-pG1r0SgA(tWH15W}26%BYO?Xa*ZEYTbLwB-~L0bcoSt(~* zS#o&GLd|d*Zh~g$)T;$os$$mh4>k5Hq^6^s#FI4v=M`pm6}zKjs z+$5;>-R$r+tySBk9ifyht)Wfk>}SeIBX`x^aE)NipbOAVhr06ExPZmwsFd+g@4t|z zfJu1t&{9AXtoh0;;fk}&7d&;O8==x5);I#p>&~d|E&H~f(jAA_hBzlvXg^foxOD=1t7J0CLbF?tkNf<|NJ-~1mYI*Fk&H~@nIR#@7`xt*(yft^ z`;VAO9^YPl-Zb09Bf+80mG4g4_S+d&RaF%4 z?(93RA6zCM0t@d0@$Y?~hWwT-7g^Uh*kY7e2U9; z->~#=Z>YIHKL;U%|8v41&yy3ywF(EX#siQity^8a zlz%#`9~IDYp-(R8!XV*z@kz)ee#ELJ1+fUrlLk$_e z2>!L%W16e#FvPdbE$ol(E$x)e7}skzG9D^gevvb7efbz$2f*IlV zIGeFVID?NePQ6Sv8%{+;2jhqmekuFvFx+r{u2NlS{xg_kPL7|iK$h}TvyiVW<5u3{ zELfKziw^$g|I~a`^g!rqm%JeUMDeHpW!ITU$4*c>S@47`P>YW>ij_N#>xWyCgg*WK z5ng3N3!7PeVwrqom+2$c8=-gwg&m-X5O%t*r4og>No?7M>^1ow>c^rBeG#O`;!ZvM zi1Y{tFCZzo>vmHh5T&*L5E`%}gzUPEbpoDzd8Pyg*U3XB?5(#;RO3^A5`4lCGaU;FJaqxXw2j#lj{md8I07&N=LLKQk)d7H`Jw<%D_a}sA- z%#kzsd_HirBgm#=E$j@?rX?dd>=(XuRhp~AS^En`jgtIV6N2g*fVlkN&Z{0WNN3Am z3DJ&kZgTC%;I)a|Jv|PyH-Xj0$cwcnTBRs_DgOoMh9^#w;kIKFHpgz z9}abd>Z>!Fo(WN!T2qQ{gpm#j$KB2ejSQImTVos9XFb{(X*T33&Ub0bu>e&JzsNBJ z7L+~5R~xA%D4g|SdhS})ie}^=`XJ;3m-l3$ffkg{C_R!JDwQu^GDkF zEm)gO|1D?K^N$whNTA?uQ_7}SGN+hWfzT3M#N(~%Oc?4e0*K_R&w2iJnsOYFCdKJ#yi zeVoVuEq&y>yFTBW=~9r@Fl9{1d~el6Bt0`2R2FI?Q$>mZ-NyJEs3U NR8`hgs#Y)$`G4@2P*eZ_ diff --git a/web/public/screenshots/Light/ChatFlow.png b/web/public/screenshots/Light/ChatFlow.png deleted file mode 100644 index 1753de776350e897373585d4a4f3fc918f5dd1ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28423 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

9*oQ`Q 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

qjip)w^ViD}Ps@(7r@fMNzS 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 z�(azvMeq!MCk2b}{`A*=vu_(#M9cgwO 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%0D1S3m3PRd_cLQ^%I!W(c9OW&E{R72i>@ABi3 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%0DFLQPVGEf9x7cl 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ÐXkBaNi;n!eqdZy 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 tMl$8YGy~#{0|pwA$}h6+pT7?$ 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 OU7D(TDz!>Bk^ckbMx1Q` diff --git a/web/public/screenshots/Light/Chatflow.png b/web/public/screenshots/Light/Chatflow.png deleted file mode 100644 index 1753de776350e897373585d4a4f3fc918f5dd1ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28423 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

9*oQ`Q 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

qjip)w^ViD}Ps@(7r@fMNzS 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 z�(azvMeq!MCk2b}{`A*=vu_(#M9cgwO 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%0D1S3m3PRd_cLQ^%I!W(c9OW&E{R72i>@ABi3 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%0DFLQPVGEf9x7cl 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
TCfM64Mc1;9-*cHh>Y9*6^?ZvK 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

k4ou-n{@k!T&W%X)CegEky2jcy}NT}%Rk8X`d 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
du5x;<2M(f%l)`|?Qr-#aP22x0M z$PD#$;q{`l6hHvUlm6u1padKkD6wwBVDLYZ>o_&kR3WJga`J) zSd@D%?iZA^YJX9Jijn+fccow4K6o=IMNQ(Vv{zXcQz9sn6hA|}^9|Q3QHOWG);G%> zeQ5bcr_h{AD6_8A1OPXQ@hCY=Sgmgpzk<=Ee-FOsAwBsaMAf18$aq5tTj}I!hY0L~ zxS-DUcMh0z3rjBK{cyflxMRprZS4JGl)Kx+*nT=mz=?1KckXymml*EZOS(6pz)I(3 zVP^awkD(9r$MKh(^e6LnA|Kl2!ITrhl$YAXB{K+f z^OV3q(xXv)?ByCPBmb{>$s#wGh5)aLcQ_?r;N7M0$t6i#1L-0D5J@?a_dYQGiU`fj z$gC|eI`)w~smgdk4+*)*7WgHj4YCQ+_ef>SUbJ6x+?~9?R4g;+4L%Luw>%qYX3=cs zU)`Wdn4q3}(hd;IGBQ#Q%#9ne8zjw9n^5~mc^c&G&);70eTTRo2wU=mAq2Ki=7?bn zA;ywGKkc`t#MeAwfr@rdbc-k3(>trkE4}xuKR|Z?H0#`Oy{>)sZvmmJ#2K`Z6ls!5 z46E*J)lW*6E3pc%Mt9ywWX#Wwj$G9>U>3MJHD-Je;Jx#}${8fYSE*Ng&J&D&`|Idd^}oMcp4j%=mo+rChxm) z3yn*ZCLBoF@q~QhUipRGO)mhKmI{M;{eVpiBB(IjXOn~z+%H=ZYy)KjU)IRqB!UIg zT^sL^ywCiVPQYUFM-558(#o#960%E(T!u=^%Jy=ecdFGA5)0Hz>RIfgb$ZXxEsvlo z#ZC!1e42THgcTsHSnUlg?#5vMi}d>020~`zu7zRaNVWqZ%HwvB8ANe}cM~N4G`m*u zCh6g}bO&Aiy1d|9rF&MUO@#2X$P;s)fu@XlC zzQ+Q8fm))cW%{_l>gfbSwb1)2mk+x;eiIWujsgbjDMHPNEK&^%*0#`AB^5{4^<0Dv z-r&iRe8z(*K%~34d|?ni1&PI@V^9Ybm6sc=3#dTD|Ho6FKz4xSsEKp+|2Vn|ho-(Z zEGnR+K{p~I4br1UoPv~eOLq-Mj1ECW>2!pWV4l-%fMbPkY~!RYw*`~HD@&b{Y7 z?|tiep3I}+Gba^;-mHT9?gdA>JEE#>kFq?3Npg86�eFQ0o4AUwEAC8T3LpkX~dl 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 From 7fde6385560f824bc96effde2eb99301e14a01dc Mon Sep 17 00:00:00 2001 From: longfengpili <33371749+longfengpili@users.noreply.github.com> Date: Mon, 16 Dec 2024 18:43:59 +0800 Subject: [PATCH 006/138] fix: fix proxy for docker (#11681) --- docker/docker-compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index eece49b113..2860460d97 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -288,6 +288,8 @@ x-shared-env: &shared-api-worker-env OCEANBASE_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false} RETRIEVAL_TOP_N: ${RETRIEVAL_TOP_N:-0} + HTTP_PROXY: ${HTTP_PROXY:-} + HTTPS_PROXY: ${HTTPS_PROXY:-} services: # API service From fc8fdbacb40b5f56d585beec94efefce24386361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B9=E7=A8=8B?= Date: Mon, 16 Dec 2024 18:45:26 +0800 Subject: [PATCH 007/138] feat: add gitee ai vl models (#11697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 方程 --- .../gitee_ai/llm/InternVL2-8B.yaml | 93 +++++++++++++++++++ .../gitee_ai/llm/InternVL2.5-26B.yaml | 93 +++++++++++++++++++ .../gitee_ai/llm/_position.yaml | 2 + .../model_providers/gitee_ai/llm/llm.py | 20 ++-- 4 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2-8B.yaml create mode 100644 api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2.5-26B.yaml diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2-8B.yaml b/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2-8B.yaml new file mode 100644 index 0000000000..d288c3dd39 --- /dev/null +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2-8B.yaml @@ -0,0 +1,93 @@ +model: InternVL2-8B +label: + en_US: InternVL2-8B +model_type: llm +features: + - vision + - agent-thought +model_properties: + mode: chat + context_size: 32000 +parameter_rules: + - name: max_tokens + use_template: max_tokens + label: + en_US: "Max Tokens" + zh_Hans: "最大Token数" + type: int + default: 512 + min: 1 + required: true + help: + en_US: "The maximum number of tokens that can be generated by the model varies depending on the model." + zh_Hans: "模型可生成的最大 token 个数,不同模型上限不同。" + + - name: temperature + use_template: temperature + label: + en_US: "Temperature" + zh_Hans: "采样温度" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The randomness of the sampling temperature control output. The temperature value is within the range of [0.0, 1.0]. The higher the value, the more random and creative the output; the lower the value, the more stable it is. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样温度控制输出的随机性。温度值在 [0.0, 1.0] 范围内,值越高,输出越随机和创造性;值越低,输出越稳定。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_p + use_template: top_p + label: + en_US: "Top P" + zh_Hans: "Top P" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The value range of the sampling method is [0.0, 1.0]. The top_p value determines that the model selects tokens from the top p% of candidate words with the highest probability; when top_p is 0, this parameter is invalid. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样方法的取值范围为 [0.0,1.0]。top_p 值确定模型从概率最高的前p%的候选词中选取 tokens;当 top_p 为 0 时,此参数无效。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_k + use_template: top_k + label: + en_US: "Top K" + zh_Hans: "Top K" + type: int + default: 50 + min: 0 + max: 100 + required: true + help: + en_US: "The value range is [0,100], which limits the model to only select from the top k words with the highest probability when choosing the next word at each step. The larger the value, the more diverse text generation will be." + zh_Hans: "取值范围为 [0,100],限制模型在每一步选择下一个词时,只从概率最高的前 k 个词中选取。数值越大,文本生成越多样。" + + - name: frequency_penalty + use_template: frequency_penalty + label: + en_US: "Frequency Penalty" + zh_Hans: "频率惩罚" + type: float + default: 0 + min: -1.0 + max: 1.0 + precision: 1 + required: false + help: + en_US: "Used to adjust the frequency of repeated content in automatically generated text. Positive numbers reduce repetition, while negative numbers increase repetition. After setting this parameter, if a word has already appeared in the text, the model will decrease the probability of choosing that word for subsequent generation." + zh_Hans: "用于调整自动生成文本中重复内容的频率。正数减少重复,负数增加重复。设置此参数后,如果一个词在文本中已经出现过,模型在后续生成中选择该词的概率会降低。" + + - name: user + use_template: text + label: + en_US: "User" + zh_Hans: "用户" + type: string + required: false + help: + en_US: "Used to track and differentiate conversation requests from different users." + zh_Hans: "用于追踪和区分不同用户的对话请求。" diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2.5-26B.yaml b/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2.5-26B.yaml new file mode 100644 index 0000000000..b2dee88c02 --- /dev/null +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/InternVL2.5-26B.yaml @@ -0,0 +1,93 @@ +model: InternVL2.5-26B +label: + en_US: InternVL2.5-26B +model_type: llm +features: + - vision + - agent-thought +model_properties: + mode: chat + context_size: 32000 +parameter_rules: + - name: max_tokens + use_template: max_tokens + label: + en_US: "Max Tokens" + zh_Hans: "最大Token数" + type: int + default: 512 + min: 1 + required: true + help: + en_US: "The maximum number of tokens that can be generated by the model varies depending on the model." + zh_Hans: "模型可生成的最大 token 个数,不同模型上限不同。" + + - name: temperature + use_template: temperature + label: + en_US: "Temperature" + zh_Hans: "采样温度" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The randomness of the sampling temperature control output. The temperature value is within the range of [0.0, 1.0]. The higher the value, the more random and creative the output; the lower the value, the more stable it is. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样温度控制输出的随机性。温度值在 [0.0, 1.0] 范围内,值越高,输出越随机和创造性;值越低,输出越稳定。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_p + use_template: top_p + label: + en_US: "Top P" + zh_Hans: "Top P" + type: float + default: 0.7 + min: 0.0 + max: 1.0 + precision: 1 + required: true + help: + en_US: "The value range of the sampling method is [0.0, 1.0]. The top_p value determines that the model selects tokens from the top p% of candidate words with the highest probability; when top_p is 0, this parameter is invalid. It is recommended to adjust either top_p or temperature parameters according to your needs to avoid adjusting both at the same time." + zh_Hans: "采样方法的取值范围为 [0.0,1.0]。top_p 值确定模型从概率最高的前p%的候选词中选取 tokens;当 top_p 为 0 时,此参数无效。建议根据需求调整 top_p 或 temperature 参数,避免同时调整两者。" + + - name: top_k + use_template: top_k + label: + en_US: "Top K" + zh_Hans: "Top K" + type: int + default: 50 + min: 0 + max: 100 + required: true + help: + en_US: "The value range is [0,100], which limits the model to only select from the top k words with the highest probability when choosing the next word at each step. The larger the value, the more diverse text generation will be." + zh_Hans: "取值范围为 [0,100],限制模型在每一步选择下一个词时,只从概率最高的前 k 个词中选取。数值越大,文本生成越多样。" + + - name: frequency_penalty + use_template: frequency_penalty + label: + en_US: "Frequency Penalty" + zh_Hans: "频率惩罚" + type: float + default: 0 + min: -1.0 + max: 1.0 + precision: 1 + required: false + help: + en_US: "Used to adjust the frequency of repeated content in automatically generated text. Positive numbers reduce repetition, while negative numbers increase repetition. After setting this parameter, if a word has already appeared in the text, the model will decrease the probability of choosing that word for subsequent generation." + zh_Hans: "用于调整自动生成文本中重复内容的频率。正数减少重复,负数增加重复。设置此参数后,如果一个词在文本中已经出现过,模型在后续生成中选择该词的概率会降低。" + + - name: user + use_template: text + label: + en_US: "User" + zh_Hans: "用户" + type: string + required: false + help: + en_US: "Used to track and differentiate conversation requests from different users." + zh_Hans: "用于追踪和区分不同用户的对话请求。" diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml b/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml index 13c31ad02b..c942cda3b2 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/_position.yaml @@ -6,3 +6,5 @@ - deepseek-coder-33B-instruct-chat - deepseek-coder-33B-instruct-completions - codegeex4-all-9b +- InternVL2.5-26B +- InternVL2-8B diff --git a/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py b/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py index 0c253a4a0a..68aaad2e3f 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py +++ b/api/core/model_runtime/model_providers/gitee_ai/llm/llm.py @@ -29,18 +29,26 @@ class GiteeAILargeLanguageModel(OAIAPICompatLargeLanguageModel): user: Optional[str] = None, ) -> Union[LLMResult, Generator]: self._add_custom_parameters(credentials, model, model_parameters) - return super()._invoke(model, credentials, prompt_messages, model_parameters, tools, stop, stream, user) + return super()._invoke( + GiteeAILargeLanguageModel.MODEL_TO_IDENTITY.get(model, model), + credentials, + prompt_messages, + model_parameters, + tools, + stop, + stream, + user, + ) def validate_credentials(self, model: str, credentials: dict) -> None: - self._add_custom_parameters(credentials, None) - super().validate_credentials(model, credentials) + self._add_custom_parameters(credentials, model, None) + super().validate_credentials(GiteeAILargeLanguageModel.MODEL_TO_IDENTITY.get(model, model), credentials) - def _add_custom_parameters(self, credentials: dict, model: Optional[str]) -> None: + def _add_custom_parameters(self, credentials: dict, model: Optional[str], model_parameters: dict) -> None: if model is None: model = "Qwen2-72B-Instruct" - model_identity = GiteeAILargeLanguageModel.MODEL_TO_IDENTITY.get(model, model) - credentials["endpoint_url"] = f"https://ai.gitee.com/api/serverless/{model_identity}/" + credentials["endpoint_url"] = "https://ai.gitee.com/v1" if model.endswith("completions"): credentials["mode"] = LLMMode.COMPLETION.value else: From e20161b3def6b6611b317af87230ddd9691e52bc Mon Sep 17 00:00:00 2001 From: Kazuhisa Wada <153587838+kazuhisa-wada@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:05:27 +0900 Subject: [PATCH 008/138] make login lockout duration configurable (#11699) --- api/.env.example | 2 ++ api/configs/feature/__init__.py | 5 +++++ api/services/account_service.py | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/api/.env.example b/api/.env.example index 74f83aa06c..9602c6492d 100644 --- a/api/.env.example +++ b/api/.env.example @@ -435,3 +435,5 @@ CREATE_TIDB_SERVICE_JOB_ENABLED=false # Maximum number of submitted thread count in a ThreadPool for parallel node execution MAX_SUBMIT_COUNT=100 +# Lockout duration in seconds +LOGIN_LOCKOUT_DURATION=86400 \ No newline at end of file diff --git a/api/configs/feature/__init__.py b/api/configs/feature/__init__.py index e79401bdfd..dfcfa635d4 100644 --- a/api/configs/feature/__init__.py +++ b/api/configs/feature/__init__.py @@ -485,6 +485,11 @@ class AuthConfig(BaseSettings): default=60, ) + LOGIN_LOCKOUT_DURATION: PositiveInt = Field( + description="Time (in seconds) a user must wait before retrying login after exceeding the rate limit.", + default=86400, + ) + class ModerationConfig(BaseSettings): """ diff --git a/api/services/account_service.py b/api/services/account_service.py index f0c6ac7ebd..22b54a3ab8 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -420,7 +420,7 @@ class AccountService: if count is None: count = 0 count = int(count) + 1 - redis_client.setex(key, 60 * 60 * 24, count) + redis_client.setex(key, dify_config.LOGIN_LOCKOUT_DURATION, count) @staticmethod def is_login_error_rate_limit(email: str) -> bool: From 7f095bdc42bc9482304c4e4f937d9b72b3aa9759 Mon Sep 17 00:00:00 2001 From: crazywoola <100913391+crazywoola@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:15:23 +0800 Subject: [PATCH 009/138] fix: image icon can not display (#11701) --- web/app/components/base/app-icon/index.tsx | 4 ++-- web/i18n/en-US/app.ts | 2 +- web/i18n/tr-TR/app.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/app-icon/index.tsx b/web/app/components/base/app-icon/index.tsx index c195b7253d..1938c42d3e 100644 --- a/web/app/components/base/app-icon/index.tsx +++ b/web/app/components/base/app-icon/index.tsx @@ -3,7 +3,6 @@ import type { FC } from 'react' import { init } from 'emoji-mart' import data from '@emoji-mart/data' -import Image from 'next/image' import { cva } from 'class-variance-authority' import type { AppIconType } from '@/types/app' import classNames from '@/utils/classnames' @@ -62,7 +61,8 @@ const AppIcon: FC = ({ onClick={onClick} > {isValidImageIcon - ? app icon + // eslint-disable-next-line @next/next/no-img-element + ? app icon : (innerIcon || ((icon && icon !== '') ? : )) } diff --git a/web/i18n/en-US/app.ts b/web/i18n/en-US/app.ts index 546bb08873..2d3ad99cae 100644 --- a/web/i18n/en-US/app.ts +++ b/web/i18n/en-US/app.ts @@ -125,7 +125,7 @@ const translation = { switchStart: 'Start switch', openInExplore: 'Open in Explore', typeSelector: { - all: 'ALL Types', + all: 'All Types ', chatbot: 'Chatbot', agent: 'Agent', workflow: 'Workflow', diff --git a/web/i18n/tr-TR/app.ts b/web/i18n/tr-TR/app.ts index 1681fc9169..29c2aeaf45 100644 --- a/web/i18n/tr-TR/app.ts +++ b/web/i18n/tr-TR/app.ts @@ -112,7 +112,7 @@ const translation = { removeOriginal: 'Orijinal uygulamayı sil', switchStart: 'Geçişi Başlat', typeSelector: { - all: 'ALL Types', + all: 'All Types', chatbot: 'Chatbot', agent: 'Agent', workflow: 'Workflow', From efa8eb379ffe4c5f6eff3a0c47b8b9ea28ff7f4a Mon Sep 17 00:00:00 2001 From: yihong Date: Tue, 17 Dec 2024 00:42:01 +0800 Subject: [PATCH 010/138] fix: memory leak by pypdfium2 close(maybe) #11510 (#11700) Signed-off-by: yihong0618 --- api/poetry.lock | 73 ++++++++++++++++++++++++++++++++++++---------- api/pyproject.toml | 2 +- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/api/poetry.lock b/api/poetry.lock index 2cdd07202c..35fda9b36f 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiofiles" @@ -955,6 +955,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -967,8 +971,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -979,8 +989,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -990,6 +1016,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -1001,6 +1031,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -1013,6 +1047,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -1025,6 +1063,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -7482,23 +7524,24 @@ image = ["Pillow (>=8.0.0)"] [[package]] name = "pypdfium2" -version = "4.17.0" +version = "4.30.0" description = "Python bindings to PDFium" optional = false python-versions = ">=3.6" files = [ - {file = "pypdfium2-4.17.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:e9ed42d5a5065ae41ae3ead3cd642e1f21b6039e69ccc204e260e218e91cd7e1"}, - {file = "pypdfium2-4.17.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0a3b5a8eca53a1e68434969821b70bd2bc9ac2b70e58daf516c6ff0b6b5779e7"}, - {file = "pypdfium2-4.17.0-py3-none-manylinux_2_17_aarch64.whl", hash = "sha256:854e04b51205466ec415b86588fe5dc593e9ca3e8e15b5aa05978c5352bd57d2"}, - {file = "pypdfium2-4.17.0-py3-none-manylinux_2_17_armv7l.whl", hash = "sha256:9ff8707b28568e9585bdf9a96b7a8a9f91c0b5ad05af119b49381dad89983364"}, - {file = "pypdfium2-4.17.0-py3-none-manylinux_2_17_i686.whl", hash = "sha256:09ecbef6212993db0b5460cfd46d6b157a921ff45c97b0764e6fe8ea2e8cdebf"}, - {file = "pypdfium2-4.17.0-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:f680e469b79c71c3fb086d7ced8361fbd66f4cd7b0ad08ff888289fe6743ab32"}, - {file = "pypdfium2-4.17.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ba7a7da48fbf0f1aaa903dac7d0e62186d6e8ae9a78b7b7b836d3f1b3d1be5d"}, - {file = "pypdfium2-4.17.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:451752170caf59d4b4572b527c2858dfff96eb1da35f2822c66cdce006dd4eae"}, - {file = "pypdfium2-4.17.0-py3-none-win32.whl", hash = "sha256:4930cfa793298214fa644c6986f6466e21f98eba3f338b4577614ebd8aa34af5"}, - {file = "pypdfium2-4.17.0-py3-none-win_amd64.whl", hash = "sha256:99de7f336e967dea4d324484f581fff55db1eb3c8e90baa845567dd9a3cc84f3"}, - {file = "pypdfium2-4.17.0-py3-none-win_arm64.whl", hash = "sha256:9381677b489c13d64ea4f8cbf6ebfc858216b052883e01e40fa993c2818a078e"}, - {file = "pypdfium2-4.17.0.tar.gz", hash = "sha256:2a2b3273c4614ee2004df60ace5f387645f843418ae29f379408ee11560241c0"}, + {file = "pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab"}, + {file = "pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de"}, + {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854"}, + {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2"}, + {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad"}, + {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f"}, + {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163"}, + {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e"}, + {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be"}, + {file = "pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e"}, + {file = "pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c"}, + {file = "pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29"}, + {file = "pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16"}, ] [[package]] @@ -11052,4 +11095,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.13" -content-hash = "1aa6a44bc9270d50c9c0ea09f55a304b5148bf4dbbbb068ff1b1ea8da6fa60cc" +content-hash = "14476bf95504a4df4b8d5a5c6608c6aa3dae7499d27d1e41ef39d761cc7c693d" diff --git a/api/pyproject.toml b/api/pyproject.toml index a20c129e9c..da9eabecf5 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -66,7 +66,7 @@ pydantic = "~2.9.2" pydantic-settings = "~2.6.0" pydantic_extra_types = "~2.9.0" pyjwt = "~2.8.0" -pypdfium2 = "~4.17.0" +pypdfium2 = "~4.30.0" python = ">=3.11,<3.13" python-docx = "~1.1.0" python-dotenv = "1.0.0" From 56cfdce4539c885c5c6ec33c2d23c42cf8c10844 Mon Sep 17 00:00:00 2001 From: yihong Date: Tue, 17 Dec 2024 09:01:23 +0800 Subject: [PATCH 011/138] chore: update docker env close #11703 (#11706) Signed-off-by: yihong0618 --- docker/.env.example | 3 +++ docker/docker-compose.yaml | 1 + 2 files changed, 4 insertions(+) diff --git a/docker/.env.example b/docker/.env.example index db85e5d511..5ffab952c7 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -944,3 +944,6 @@ CSP_WHITELIST= # Enable or disable create tidb service job CREATE_TIDB_SERVICE_JOB_ENABLED=false + +# Maximum number of submitted thread count in a ThreadPool for parallel node execution +MAX_SUBMIT_COUNT=100 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 2860460d97..7e5f7ef11f 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -287,6 +287,7 @@ x-shared-env: &shared-api-worker-env OCEANBASE_CLUSTER_NAME: ${OCEANBASE_CLUSTER_NAME:-difyai} OCEANBASE_MEMORY_LIMIT: ${OCEANBASE_MEMORY_LIMIT:-6G} CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false} + MAX_SUBMIT_COUNT: ${MAX_SUBMIT_COUNT:-100} RETRIEVAL_TOP_N: ${RETRIEVAL_TOP_N:-0} HTTP_PROXY: ${HTTP_PROXY:-} HTTPS_PROXY: ${HTTPS_PROXY:-} From 74fdc16bd18e76ceea8bd613de5f8ad5ac039776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Tue, 17 Dec 2024 12:05:13 +0800 Subject: [PATCH 012/138] feat: enhance gemini models (#11497) --- api/core/file/file_manager.py | 16 +-- .../entities/message_entities.py | 3 +- .../model_providers/anthropic/llm/llm.py | 12 +- .../google/llm/gemini-1.5-flash-001.yaml | 2 + .../google/llm/gemini-1.5-flash-002.yaml | 2 + .../llm/gemini-1.5-flash-8b-exp-0827.yaml | 2 + .../llm/gemini-1.5-flash-8b-exp-0924.yaml | 2 + .../google/llm/gemini-1.5-flash-exp-0827.yaml | 2 + .../google/llm/gemini-1.5-flash-latest.yaml | 2 + .../google/llm/gemini-1.5-flash.yaml | 2 + .../google/llm/gemini-1.5-pro-001.yaml | 2 + .../google/llm/gemini-1.5-pro-002.yaml | 2 + .../google/llm/gemini-1.5-pro-exp-0801.yaml | 2 + .../google/llm/gemini-1.5-pro-exp-0827.yaml | 2 + .../google/llm/gemini-1.5-pro-latest.yaml | 2 + .../google/llm/gemini-1.5-pro.yaml | 2 + .../google/llm/gemini-exp-1114.yaml | 2 + .../google/llm/gemini-exp-1121.yaml | 3 + .../model_providers/google/llm/llm.py | 119 +++++++++--------- .../model_providers/openai/llm/llm.py | 4 +- .../model_runtime/__mock/google.py | 58 ++++----- .../model_runtime/google/test_llm.py | 6 +- .../core/workflow/nodes/llm/test_node.py | 2 + 23 files changed, 138 insertions(+), 113 deletions(-) diff --git a/api/core/file/file_manager.py b/api/core/file/file_manager.py index 3b83683755..9df605b493 100644 --- a/api/core/file/file_manager.py +++ b/api/core/file/file_manager.py @@ -50,12 +50,12 @@ def to_prompt_message_content( else: data = _to_base64_data_string(f) - return ImagePromptMessageContent(data=data, detail=image_detail_config) + return ImagePromptMessageContent(data=data, detail=image_detail_config, format=f.extension.lstrip(".")) case FileType.AUDIO: - encoded_string = _get_encoded_string(f) + data = _to_base64_data_string(f) if f.extension is None: raise ValueError("Missing file extension") - return AudioPromptMessageContent(data=encoded_string, format=f.extension.lstrip(".")) + return AudioPromptMessageContent(data=data, format=f.extension.lstrip(".")) case FileType.VIDEO: if dify_config.MULTIMODAL_SEND_VIDEO_FORMAT == "url": data = _to_url(f) @@ -65,14 +65,8 @@ def to_prompt_message_content( raise ValueError("Missing file extension") return VideoPromptMessageContent(data=data, format=f.extension.lstrip(".")) case FileType.DOCUMENT: - data = _get_encoded_string(f) - if f.mime_type is None: - raise ValueError("Missing file mime_type") - return DocumentPromptMessageContent( - encode_format="base64", - mime_type=f.mime_type, - data=data, - ) + data = _to_base64_data_string(f) + return DocumentPromptMessageContent(encode_format="base64", data=data, format=f.extension.lstrip(".")) case _: raise ValueError(f"file type {f.type} is not supported") diff --git a/api/core/model_runtime/entities/message_entities.py b/api/core/model_runtime/entities/message_entities.py index f2870209bb..26af522ea6 100644 --- a/api/core/model_runtime/entities/message_entities.py +++ b/api/core/model_runtime/entities/message_entities.py @@ -101,13 +101,14 @@ class ImagePromptMessageContent(PromptMessageContent): type: PromptMessageContentType = PromptMessageContentType.IMAGE detail: DETAIL = DETAIL.LOW + format: str = Field("jpg", description="Image format") class DocumentPromptMessageContent(PromptMessageContent): type: PromptMessageContentType = PromptMessageContentType.DOCUMENT encode_format: Literal["base64"] - mime_type: str data: str + format: str = Field(..., description="Document format") class PromptMessage(ABC, BaseModel): diff --git a/api/core/model_runtime/model_providers/anthropic/llm/llm.py b/api/core/model_runtime/model_providers/anthropic/llm/llm.py index 3faf5abbe8..edf56591f0 100644 --- a/api/core/model_runtime/model_providers/anthropic/llm/llm.py +++ b/api/core/model_runtime/model_providers/anthropic/llm/llm.py @@ -526,17 +526,19 @@ class AnthropicLargeLanguageModel(LargeLanguageModel): } sub_messages.append(sub_message_dict) elif isinstance(message_content, DocumentPromptMessageContent): - if message_content.mime_type != "application/pdf": + data_split = message_content.data.split(";base64,") + mime_type = data_split[0].replace("data:", "") + base64_data = data_split[1] + if mime_type != "application/pdf": raise ValueError( - f"Unsupported document type {message_content.mime_type}, " - "only support application/pdf" + f"Unsupported document type {mime_type}, " "only support application/pdf" ) sub_message_dict = { "type": "document", "source": { "type": message_content.encode_format, - "media_type": message_content.mime_type, - "data": message_content.data, + "media_type": mime_type, + "data": base64_data, }, } sub_messages.append(sub_message_dict) diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-001.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-001.yaml index 43f4e4787d..86bba2154a 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-001.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-001.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-002.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-002.yaml index 7b9add6af1..9ad57a1933 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-002.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-002.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml index d6de82012e..72205f15a8 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0827.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0924.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0924.yaml index 23b8d318fc..1193e60669 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0924.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-8b-exp-0924.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml index 9762706cd7..7eba1f3d4d 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-exp-0827.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml index b9739d068e..b8c5024158 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash-latest.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash.yaml index d8ab4efc91..ea0c42dda8 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-flash.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 1048576 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-001.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-001.yaml index 05184823e4..16df30857c 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-001.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-001.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-002.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-002.yaml index 548fe6ddb2..717d9481b9 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-002.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-002.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml index defab26acf..bf9704f0d5 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0801.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml index 9cbc889f17..714ff35f34 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-exp-0827.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml index e5aefcdb99..bbca2ba385 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro-latest.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro.yaml index 00bd3e8d99..ae127fb4e2 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-1.5-pro.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 2097152 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-exp-1114.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-exp-1114.yaml index 0515e706c2..bd49b47693 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-exp-1114.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-exp-1114.yaml @@ -8,6 +8,8 @@ features: - tool-call - stream-tool-call - document + - video + - audio model_properties: mode: chat context_size: 32767 diff --git a/api/core/model_runtime/model_providers/google/llm/gemini-exp-1121.yaml b/api/core/model_runtime/model_providers/google/llm/gemini-exp-1121.yaml index 9ca4f6e675..8e3f218df4 100644 --- a/api/core/model_runtime/model_providers/google/llm/gemini-exp-1121.yaml +++ b/api/core/model_runtime/model_providers/google/llm/gemini-exp-1121.yaml @@ -7,6 +7,9 @@ features: - vision - tool-call - stream-tool-call + - document + - video + - audio model_properties: mode: chat context_size: 32767 diff --git a/api/core/model_runtime/model_providers/google/llm/llm.py b/api/core/model_runtime/model_providers/google/llm/llm.py index c19e860d2e..9a1b13f96f 100644 --- a/api/core/model_runtime/model_providers/google/llm/llm.py +++ b/api/core/model_runtime/model_providers/google/llm/llm.py @@ -1,29 +1,30 @@ import base64 -import io import json +import os +import tempfile +import time from collections.abc import Generator -from typing import Optional, Union, cast +from typing import Optional, Union import google.ai.generativelanguage as glm import google.generativeai as genai import requests from google.api_core import exceptions -from google.generativeai.client import _ClientManager -from google.generativeai.types import ContentType, GenerateContentResponse +from google.generativeai.types import ContentType, File, GenerateContentResponse from google.generativeai.types.content_types import to_part -from PIL import Image from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, - DocumentPromptMessageContent, ImagePromptMessageContent, PromptMessage, + PromptMessageContent, PromptMessageContentType, PromptMessageTool, SystemPromptMessage, ToolPromptMessage, UserPromptMessage, + VideoPromptMessageContent, ) from core.model_runtime.errors.invoke import ( InvokeAuthorizationError, @@ -35,21 +36,7 @@ from core.model_runtime.errors.invoke import ( ) from core.model_runtime.errors.validate import CredentialsValidateFailedError from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel - -GOOGLE_AVAILABLE_MIMETYPE = [ - "application/pdf", - "application/x-javascript", - "text/javascript", - "application/x-python", - "text/x-python", - "text/plain", - "text/html", - "text/css", - "text/md", - "text/csv", - "text/xml", - "text/rtf", -] +from extensions.ext_redis import redis_client class GoogleLargeLanguageModel(LargeLanguageModel): @@ -201,29 +188,17 @@ class GoogleLargeLanguageModel(LargeLanguageModel): if stop: config_kwargs["stop_sequences"] = stop + genai.configure(api_key=credentials["google_api_key"]) google_model = genai.GenerativeModel(model_name=model) history = [] - # hack for gemini-pro-vision, which currently does not support multi-turn chat - if model == "gemini-pro-vision": - last_msg = prompt_messages[-1] - content = self._format_message_to_glm_content(last_msg) - history.append(content) - else: - for msg in prompt_messages: # makes message roles strictly alternating - content = self._format_message_to_glm_content(msg) - if history and history[-1]["role"] == content["role"]: - history[-1]["parts"].extend(content["parts"]) - else: - history.append(content) - - # Create a new ClientManager with tenant's API key - new_client_manager = _ClientManager() - new_client_manager.configure(api_key=credentials["google_api_key"]) - new_custom_client = new_client_manager.make_client("generative") - - google_model._client = new_custom_client + for msg in prompt_messages: # makes message roles strictly alternating + content = self._format_message_to_glm_content(msg) + if history and history[-1]["role"] == content["role"]: + history[-1]["parts"].extend(content["parts"]) + else: + history.append(content) response = google_model.generate_content( contents=history, @@ -346,7 +321,7 @@ class GoogleLargeLanguageModel(LargeLanguageModel): content = message.content if isinstance(content, list): - content = "".join(c.data for c in content if c.type != PromptMessageContentType.IMAGE) + content = "".join(c.data for c in content if c.type == PromptMessageContentType.TEXT) if isinstance(message, UserPromptMessage): message_text = f"{human_prompt} {content}" @@ -359,6 +334,44 @@ class GoogleLargeLanguageModel(LargeLanguageModel): return message_text + def _upload_file_content_to_google(self, message_content: PromptMessageContent) -> File: + key = f"{message_content.type.value}:{hash(message_content.data)}" + if redis_client.exists(key): + try: + return genai.get_file(redis_client.get(key).decode()) + except: + pass + with tempfile.NamedTemporaryFile(delete=False) as temp_file: + if message_content.data.startswith("data:"): + metadata, base64_data = message_content.data.split(",", 1) + file_content = base64.b64decode(base64_data) + mime_type = metadata.split(";", 1)[0].split(":")[1] + temp_file.write(file_content) + else: + # only ImagePromptMessageContent and VideoPromptMessageContent has url + try: + response = requests.get(message_content.data) + response.raise_for_status() + if message_content.type is ImagePromptMessageContent: + prefix = "image/" + elif message_content.type is VideoPromptMessageContent: + prefix = "video/" + mime_type = prefix + message_content.format + temp_file.write(response.content) + except Exception as ex: + raise ValueError(f"Failed to fetch data from url {message_content.data}, {ex}") + temp_file.flush() + try: + file = genai.upload_file(path=temp_file.name, mime_type=mime_type) + while file.state.name == "PROCESSING": + time.sleep(5) + file = genai.get_file(file.name) + # google will delete your upload files in 2 days. + redis_client.setex(key, 47 * 60 * 60, file.name) + return file + finally: + os.unlink(temp_file.name) + def _format_message_to_glm_content(self, message: PromptMessage) -> ContentType: """ Format a single message into glm.Content for Google API @@ -374,28 +387,8 @@ class GoogleLargeLanguageModel(LargeLanguageModel): for c in message.content: if c.type == PromptMessageContentType.TEXT: glm_content["parts"].append(to_part(c.data)) - elif c.type == PromptMessageContentType.IMAGE: - message_content = cast(ImagePromptMessageContent, c) - if message_content.data.startswith("data:"): - metadata, base64_data = c.data.split(",", 1) - mime_type = metadata.split(";", 1)[0].split(":")[1] - else: - # fetch image data from url - try: - image_content = requests.get(message_content.data).content - with Image.open(io.BytesIO(image_content)) as img: - mime_type = f"image/{img.format.lower()}" - base64_data = base64.b64encode(image_content).decode("utf-8") - except Exception as ex: - raise ValueError(f"Failed to fetch image data from url {message_content.data}, {ex}") - blob = {"inline_data": {"mime_type": mime_type, "data": base64_data}} - glm_content["parts"].append(blob) - elif c.type == PromptMessageContentType.DOCUMENT: - message_content = cast(DocumentPromptMessageContent, c) - if message_content.mime_type not in GOOGLE_AVAILABLE_MIMETYPE: - raise ValueError(f"Unsupported mime type {message_content.mime_type}") - blob = {"inline_data": {"mime_type": message_content.mime_type, "data": message_content.data}} - glm_content["parts"].append(blob) + else: + glm_content["parts"].append(self._upload_file_content_to_google(c)) return glm_content elif isinstance(message, AssistantPromptMessage): diff --git a/api/core/model_runtime/model_providers/openai/llm/llm.py b/api/core/model_runtime/model_providers/openai/llm/llm.py index 07cb1e2d10..b73ce8752f 100644 --- a/api/core/model_runtime/model_providers/openai/llm/llm.py +++ b/api/core/model_runtime/model_providers/openai/llm/llm.py @@ -920,10 +920,12 @@ class OpenAILargeLanguageModel(_CommonOpenAI, LargeLanguageModel): } sub_messages.append(sub_message_dict) elif isinstance(message_content, AudioPromptMessageContent): + data_split = message_content.data.split(";base64,") + base64_data = data_split[1] sub_message_dict = { "type": "input_audio", "input_audio": { - "data": message_content.data, + "data": base64_data, "format": message_content.format, }, } diff --git a/api/tests/integration_tests/model_runtime/__mock/google.py b/api/tests/integration_tests/model_runtime/__mock/google.py index 402bd9c2c2..5ea86baa83 100644 --- a/api/tests/integration_tests/model_runtime/__mock/google.py +++ b/api/tests/integration_tests/model_runtime/__mock/google.py @@ -1,4 +1,5 @@ from collections.abc import Generator +from unittest.mock import MagicMock import google.generativeai.types.generation_types as generation_config_types import pytest @@ -6,11 +7,10 @@ from _pytest.monkeypatch import MonkeyPatch from google.ai import generativelanguage as glm from google.ai.generativelanguage_v1beta.types import content as gag_content from google.generativeai import GenerativeModel -from google.generativeai.client import _ClientManager, configure from google.generativeai.types import GenerateContentResponse, content_types, safety_types from google.generativeai.types.generation_types import BaseGenerateContentResponse -current_api_key = "" +from extensions import ext_redis class MockGoogleResponseClass: @@ -57,11 +57,6 @@ class MockGoogleClass: stream: bool = False, **kwargs, ) -> GenerateContentResponse: - global current_api_key - - if len(current_api_key) < 16: - raise Exception("Invalid API key") - if stream: return MockGoogleClass.generate_content_stream() @@ -75,33 +70,29 @@ class MockGoogleClass: def generative_response_candidates(self) -> list[MockGoogleResponseCandidateClass]: return [MockGoogleResponseCandidateClass()] - def make_client(self: _ClientManager, name: str): - global current_api_key - if name.endswith("_async"): - name = name.split("_")[0] - cls = getattr(glm, name.title() + "ServiceAsyncClient") - else: - cls = getattr(glm, name.title() + "ServiceClient") +def mock_configure(api_key: str): + if len(api_key) < 16: + raise Exception("Invalid API key") - # Attempt to configure using defaults. - if not self.client_config: - configure() - client_options = self.client_config.get("client_options", None) - if client_options: - current_api_key = client_options.api_key +class MockFileState: + def __init__(self): + self.name = "FINISHED" - def nop(self, *args, **kwargs): - pass - original_init = cls.__init__ - cls.__init__ = nop - client: glm.GenerativeServiceClient = cls(**self.client_config) - cls.__init__ = original_init +class MockGoogleFile: + def __init__(self, name: str = "mock_file_name"): + self.name = name + self.state = MockFileState() - if not self.default_metadata: - return client + +def mock_get_file(name: str) -> MockGoogleFile: + return MockGoogleFile(name) + + +def mock_upload_file(path: str, mime_type: str) -> MockGoogleFile: + return MockGoogleFile() @pytest.fixture @@ -109,8 +100,17 @@ def setup_google_mock(request, monkeypatch: MonkeyPatch): monkeypatch.setattr(BaseGenerateContentResponse, "text", MockGoogleClass.generative_response_text) monkeypatch.setattr(BaseGenerateContentResponse, "candidates", MockGoogleClass.generative_response_candidates) monkeypatch.setattr(GenerativeModel, "generate_content", MockGoogleClass.generate_content) - monkeypatch.setattr(_ClientManager, "make_client", MockGoogleClass.make_client) + monkeypatch.setattr("google.generativeai.configure", mock_configure) + monkeypatch.setattr("google.generativeai.get_file", mock_get_file) + monkeypatch.setattr("google.generativeai.upload_file", mock_upload_file) yield monkeypatch.undo() + + +@pytest.fixture +def setup_mock_redis() -> None: + ext_redis.redis_client.get = MagicMock(return_value=None) + ext_redis.redis_client.setex = MagicMock(return_value=None) + ext_redis.redis_client.exists = MagicMock(return_value=True) diff --git a/api/tests/integration_tests/model_runtime/google/test_llm.py b/api/tests/integration_tests/model_runtime/google/test_llm.py index 2877fa1507..777bbfdcb6 100644 --- a/api/tests/integration_tests/model_runtime/google/test_llm.py +++ b/api/tests/integration_tests/model_runtime/google/test_llm.py @@ -13,7 +13,7 @@ from core.model_runtime.entities.message_entities import ( ) from core.model_runtime.errors.validate import CredentialsValidateFailedError from core.model_runtime.model_providers.google.llm.llm import GoogleLargeLanguageModel -from tests.integration_tests.model_runtime.__mock.google import setup_google_mock +from tests.integration_tests.model_runtime.__mock.google import setup_google_mock, setup_mock_redis @pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True) @@ -95,7 +95,7 @@ def test_invoke_stream_model(setup_google_mock): @pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True) -def test_invoke_chat_model_with_vision(setup_google_mock): +def test_invoke_chat_model_with_vision(setup_google_mock, setup_mock_redis): model = GoogleLargeLanguageModel() result = model.invoke( @@ -124,7 +124,7 @@ def test_invoke_chat_model_with_vision(setup_google_mock): @pytest.mark.parametrize("setup_google_mock", [["none"]], indirect=True) -def test_invoke_chat_model_with_vision_multi_pics(setup_google_mock): +def test_invoke_chat_model_with_vision_multi_pics(setup_google_mock, setup_mock_redis): model = GoogleLargeLanguageModel() result = model.invoke( diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py index 9a24d35a1f..024f9129c8 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py @@ -326,6 +326,7 @@ def test_fetch_prompt_messages__basic(faker, llm_node, model_config): tenant_id="test", type=FileType.IMAGE, filename="test1.jpg", + extension=".jpg", transfer_method=FileTransferMethod.REMOTE_URL, remote_url=fake_remote_url, ) @@ -395,6 +396,7 @@ def test_fetch_prompt_messages__basic(faker, llm_node, model_config): tenant_id="test", type=FileType.IMAGE, filename="test1.jpg", + extension=".jpg", transfer_method=FileTransferMethod.REMOTE_URL, remote_url=fake_remote_url, ) From 92a840f1b2c869f3310e0b5f5291b3600c1b43e3 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Tue, 17 Dec 2024 12:11:50 +0800 Subject: [PATCH 013/138] feat(tool_node): Suppress exceptions thrown by the Tool (#11724) Signed-off-by: -LAN- --- api/core/app/task_pipeline/workflow_cycle_manage.py | 6 +++--- api/core/workflow/nodes/tool/tool_node.py | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index d78f124e3a..8b588b48eb 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -271,9 +271,9 @@ class WorkflowCycleManage: db.session.close() - with Session(db.engine, expire_on_commit=False) as session: - session.add(workflow_run) - session.refresh(workflow_run) + # with Session(db.engine, expire_on_commit=False) as session: + # session.add(workflow_run) + # session.refresh(workflow_run) if trace_manager: trace_manager.add_trace_task( diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index 9b901c026e..3b56f94876 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -92,6 +92,16 @@ class ToolNode(BaseNode[ToolNodeData]): error=f"Failed to invoke tool: {str(e)}", error_type=type(e).__name__, ) + except Exception as e: + return NodeRunResult( + status=WorkflowNodeExecutionStatus.FAILED, + inputs=parameters_for_log, + metadata={ + NodeRunMetadataKey.TOOL_INFO: tool_info, + }, + error=f"Failed to invoke tool: {str(e)}", + error_type="UnknownError", + ) # convert tool messages plain_text, files, json = self._convert_tool_messages(messages) From a399502ecdb9bcc4eb16007e27bec7cbe7aa0856 Mon Sep 17 00:00:00 2001 From: NFish Date: Tue, 17 Dec 2024 12:20:49 +0800 Subject: [PATCH 014/138] Dark Mode: Workflow darkmode style (#11695) --- .../components/tools/add-tool-modal/empty.tsx | 4 +- .../workflow/block-selector/all-tools.tsx | 16 ++--- .../workflow/block-selector/blocks.tsx | 14 ++--- .../workflow/block-selector/index-bar.tsx | 4 +- .../workflow/block-selector/index.tsx | 11 ++-- .../workflow/block-selector/tabs.tsx | 8 +-- .../workflow/block-selector/tools.tsx | 12 ++-- .../workflow/header/editing-title.tsx | 2 +- web/app/components/workflow/header/index.tsx | 20 +++--- .../components/workflow/header/undo-redo.tsx | 29 ++++----- .../workflow/header/view-workflow-history.tsx | 62 +++++++++---------- web/app/components/workflow/index.tsx | 9 +-- .../workflow/operator/add-block.tsx | 6 +- .../components/workflow/operator/control.tsx | 23 +++---- .../components/workflow/operator/index.tsx | 4 +- .../workflow/operator/tip-popup.tsx | 8 +-- .../workflow/operator/zoom-in-out.tsx | 62 ++++++++++--------- .../components/workflow/panel-contextmenu.tsx | 21 ++++--- .../components/workflow/shortcuts-name.tsx | 4 +- web/app/components/workflow/style.css | 4 +- web/app/components/workflow/utils.ts | 1 + web/app/layout.tsx | 2 +- web/app/styles/globals.css | 8 +++ web/tailwind.config.js | 1 + web/themes/manual-dark.css | 1 + web/themes/manual-light.css | 1 + 26 files changed, 179 insertions(+), 158 deletions(-) diff --git a/web/app/components/tools/add-tool-modal/empty.tsx b/web/app/components/tools/add-tool-modal/empty.tsx index 78c2deeed4..051ae446d4 100644 --- a/web/app/components/tools/add-tool-modal/empty.tsx +++ b/web/app/components/tools/add-tool-modal/empty.tsx @@ -6,8 +6,8 @@ const Empty = () => { return (

-
{t('tools.addToolModal.emptyTitle')}
-
{t('tools.addToolModal.emptyTip')}
+
{t('tools.addToolModal.emptyTitle')}
+
{t('tools.addToolModal.emptyTip')}
) } diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index dc15313216..aaa3811251 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -43,23 +43,23 @@ const AllTools = ({ return mergedTools.filter((toolWithProvider) => { return isMatchingKeywords(toolWithProvider.name, searchText) - || toolWithProvider.tools.some((tool) => { - return Object.values(tool.label).some((label) => { - return isMatchingKeywords(label, searchText) + || toolWithProvider.tools.some((tool) => { + return Object.values(tool.label).some((label) => { + return isMatchingKeywords(label, searchText) + }) }) - }) }) }, [activeTab, buildInTools, customTools, workflowTools, searchText]) return (
-
+
{ tabs.map(tab => (
setActiveTab(tab.key)} diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx index a1bada1a8e..eaaa473f3d 100644 --- a/web/app/components/workflow/block-selector/blocks.tsx +++ b/web/app/components/workflow/block-selector/blocks.tsx @@ -58,7 +58,7 @@ const Blocks = ({ > { classification !== '-' && !!list.length && ( -
+
{t(`workflow.tabs.${classification}`)}
) @@ -68,7 +68,7 @@ const Blocks = ({ -
{block.title}
-
{nodesExtraData[block.type].about}
+
{block.title}
+
{nodesExtraData[block.type].about}
)} >
onSelect(block.type)} > -
{block.title}
+
{block.title}
)) @@ -103,7 +103,7 @@ const Blocks = ({
{ isEmpty && ( -
{t('workflow.tabs.noResult')}
+
{t('workflow.tabs.noResult')}
) } { diff --git a/web/app/components/workflow/block-selector/index-bar.tsx b/web/app/components/workflow/block-selector/index-bar.tsx index 6eab51246d..2a4cbad432 100644 --- a/web/app/components/workflow/block-selector/index-bar.tsx +++ b/web/app/components/workflow/block-selector/index-bar.tsx @@ -47,9 +47,9 @@ const IndexBar: FC = ({ letters, itemRefs }) => { element.scrollIntoView({ behavior: 'smooth' }) } return ( -
+
{letters.map(letter => ( -
handleIndexClick(letter)}> +
handleIndexClick(letter)}> {letter}
))} diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index 6f05ba16fb..dc93c275f2 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -25,6 +25,7 @@ import Input from '@/app/components/base/input' import { Plus02, } from '@/app/components/base/icons/src/vender/line/general' +import classNames from '@/utils/classnames' type NodeSelectorProps = { open?: boolean @@ -114,19 +115,21 @@ const NodeSelector: FC = ({
- +
) } -
-
e.stopPropagation()}> +
+
e.stopPropagation()}> = ({
e.stopPropagation()}> { !noBlocks && ( -
+
{ tabs.map(tab => (
onActiveTabChange(tab.key)} > diff --git a/web/app/components/workflow/block-selector/tools.tsx b/web/app/components/workflow/block-selector/tools.tsx index a2ae845997..394966fb4f 100644 --- a/web/app/components/workflow/block-selector/tools.tsx +++ b/web/app/components/workflow/block-selector/tools.tsx @@ -45,7 +45,7 @@ const Blocks = ({ -
{tool.label[language]}
-
{tool.description[language]}
+
{tool.label[language]}
+
{tool.description[language]}
)} >
onSelect(BlockEnum.Tool, { provider_id: toolWithProvider.id, provider_type: toolWithProvider.type, @@ -75,7 +75,7 @@ const Blocks = ({ type={BlockEnum.Tool} toolIcon={toolWithProvider.icon} /> -
{tool.label[language]}
+
{tool.label[language]}
)) @@ -100,7 +100,7 @@ const Blocks = ({
{ !tools.length && !showWorkflowEmpty && ( -
{t('workflow.tabs.noResult')}
+
{t('workflow.tabs.noResult')}
) } {!tools.length && showWorkflowEmpty && ( diff --git a/web/app/components/workflow/header/editing-title.tsx b/web/app/components/workflow/header/editing-title.tsx index 44a85631dc..9148420cbe 100644 --- a/web/app/components/workflow/header/editing-title.tsx +++ b/web/app/components/workflow/header/editing-title.tsx @@ -13,7 +13,7 @@ const EditingTitle = () => { const isSyncingWorkflowDraft = useStore(s => s.isSyncingWorkflowDraft) return ( -
+
{ !!draftUpdatedAt && ( <> diff --git a/web/app/components/workflow/header/index.tsx b/web/app/components/workflow/header/index.tsx index 010d9ca1cd..6e46990df8 100644 --- a/web/app/components/workflow/header/index.tsx +++ b/web/app/components/workflow/header/index.tsx @@ -27,6 +27,7 @@ import { } from '../hooks' import AppPublisher from '../../app/app-publisher' import { ToastContext } from '../../base/toast' +import Divider from '../../base/divider' import RunAndHistory from './run-and-history' import EditingTitle from './editing-title' import RunningTitle from './running-title' @@ -144,15 +145,12 @@ const Header: FC = () => { return (
{ appSidebarExpand === 'collapse' && ( -
{appDetail?.name}
+
{appDetail?.name}
) } { @@ -171,7 +169,7 @@ const Header: FC = () => { {/* */} {isChatMode && } -
+ -
+
- )} - -
- {isRunning && ( - - )} - {isFinished && ( - <> - {result} - - )} -
+ {isRunning && ( + + )} + {isFinished && ( + <> + {result} + + )} +
+ ) + }
) diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx index f11f8bd5fb..89412cabb3 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx @@ -14,7 +14,6 @@ import type { CommonNodeType, Node, } from '@/app/components/workflow/types' -import Split from '@/app/components/workflow/nodes/_base/components/split' import Tooltip from '@/app/components/base/tooltip' type ErrorHandleProps = Pick @@ -45,7 +44,6 @@ const ErrorHandle = ({ return ( <> -
{ + const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate() + + const handleRetryConfigChange = useCallback((value?: WorkflowRetryConfig) => { + handleNodeDataUpdateWithSyncDraft({ + id, + data: { + retry_config: value, + }, + }) + }, [id, handleNodeDataUpdateWithSyncDraft]) + + return { + handleRetryConfigChange, + } +} + +export const useRetryDetailShowInSingleRun = () => { + const [retryDetails, setRetryDetails] = useState() + + const handleRetryDetailsChange = useCallback((details: NodeTracing[] | undefined) => { + setRetryDetails(details) + }, []) + + return { + retryDetails, + handleRetryDetailsChange, + } +} diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx new file mode 100644 index 0000000000..f5d2f08ac8 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx @@ -0,0 +1,88 @@ +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { + RiAlertFill, + RiCheckboxCircleFill, + RiLoader2Line, +} from '@remixicon/react' +import type { Node } from '@/app/components/workflow/types' +import { NodeRunningStatus } from '@/app/components/workflow/types' +import cn from '@/utils/classnames' + +type RetryOnNodeProps = Pick +const RetryOnNode = ({ + data, +}: RetryOnNodeProps) => { + const { t } = useTranslation() + const { retry_config } = data + const showSelectedBorder = data.selected || data._isBundled || data._isEntering + const { + isRunning, + isSuccessful, + isException, + isFailed, + } = useMemo(() => { + return { + isRunning: data._runningStatus === NodeRunningStatus.Running && !showSelectedBorder, + isSuccessful: data._runningStatus === NodeRunningStatus.Succeeded && !showSelectedBorder, + isFailed: data._runningStatus === NodeRunningStatus.Failed && !showSelectedBorder, + isException: data._runningStatus === NodeRunningStatus.Exception && !showSelectedBorder, + } + }, [data._runningStatus, showSelectedBorder]) + const showDefault = !isRunning && !isSuccessful && !isException && !isFailed + + if (!retry_config) + return null + + return ( +
+
+
+ { + showDefault && ( + t('workflow.nodes.common.retry.retryTimes', { times: retry_config.max_retries }) + ) + } + { + isRunning && ( + <> + + {t('workflow.nodes.common.retry.retrying')} + + ) + } + { + isSuccessful && ( + <> + + {t('workflow.nodes.common.retry.retrySuccessful')} + + ) + } + { + (isFailed || isException) && ( + <> + + {t('workflow.nodes.common.retry.retryFailed')} + + ) + } +
+ { + !showDefault && ( +
+ {data._retryIndex}/{data.retry_config?.max_retries} +
+ ) + } +
+
+ ) +} + +export default RetryOnNode diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx new file mode 100644 index 0000000000..dc877a632c --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx @@ -0,0 +1,117 @@ +import { useTranslation } from 'react-i18next' +import { useRetryConfig } from './hooks' +import s from './style.module.css' +import Switch from '@/app/components/base/switch' +import Slider from '@/app/components/base/slider' +import Input from '@/app/components/base/input' +import type { + Node, +} from '@/app/components/workflow/types' +import Split from '@/app/components/workflow/nodes/_base/components/split' + +type RetryOnPanelProps = Pick +const RetryOnPanel = ({ + id, + data, +}: RetryOnPanelProps) => { + const { t } = useTranslation() + const { handleRetryConfigChange } = useRetryConfig(id) + const { retry_config } = data + + const handleRetryEnabledChange = (value: boolean) => { + handleRetryConfigChange({ + retry_enabled: value, + max_retries: retry_config?.max_retries || 3, + retry_interval: retry_config?.retry_interval || 1000, + }) + } + + const handleMaxRetriesChange = (value: number) => { + if (value > 10) + value = 10 + else if (value < 1) + value = 1 + handleRetryConfigChange({ + retry_enabled: true, + max_retries: value, + retry_interval: retry_config?.retry_interval || 1000, + }) + } + + const handleRetryIntervalChange = (value: number) => { + if (value > 5000) + value = 5000 + else if (value < 100) + value = 100 + handleRetryConfigChange({ + retry_enabled: true, + max_retries: retry_config?.max_retries || 3, + retry_interval: value, + }) + } + + return ( + <> +
+
+
+
{t('workflow.nodes.common.retry.retryOnFailure')}
+
+ handleRetryEnabledChange(v)} + /> +
+ { + retry_config?.retry_enabled && ( +
+
+
{t('workflow.nodes.common.retry.maxRetries')}
+ + handleMaxRetriesChange(e.target.value as any)} + min={1} + max={10} + unit={t('workflow.nodes.common.retry.times') || ''} + className={s.input} + /> +
+
+
{t('workflow.nodes.common.retry.retryInterval')}
+ + handleRetryIntervalChange(e.target.value as any)} + min={100} + max={5000} + unit={t('workflow.nodes.common.retry.ms') || ''} + className={s.input} + /> +
+
+ ) + } +
+ + + ) +} + +export default RetryOnPanel diff --git a/web/app/components/workflow/nodes/_base/components/retry/style.module.css b/web/app/components/workflow/nodes/_base/components/retry/style.module.css new file mode 100644 index 0000000000..2ce8717af8 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/retry/style.module.css @@ -0,0 +1,5 @@ +.input::-webkit-inner-spin-button, +.input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} \ No newline at end of file diff --git a/web/app/components/workflow/nodes/_base/components/retry/types.ts b/web/app/components/workflow/nodes/_base/components/retry/types.ts new file mode 100644 index 0000000000..bb5f593fd5 --- /dev/null +++ b/web/app/components/workflow/nodes/_base/components/retry/types.ts @@ -0,0 +1,5 @@ +export type WorkflowRetryConfig = { + max_retries: number + retry_interval: number + retry_enabled: boolean +} diff --git a/web/app/components/workflow/nodes/_base/components/retry/utils.ts b/web/app/components/workflow/nodes/_base/components/retry/utils.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx index f2da2da35a..4807fa3b2b 100644 --- a/web/app/components/workflow/nodes/_base/node.tsx +++ b/web/app/components/workflow/nodes/_base/node.tsx @@ -25,7 +25,10 @@ import { useNodesReadOnly, useToolIcon, } from '../../hooks' -import { hasErrorHandleNode } from '../../utils' +import { + hasErrorHandleNode, + hasRetryNode, +} from '../../utils' import { useNodeIterationInteractions } from '../iteration/use-interactions' import type { IterationNodeType } from '../iteration/types' import { @@ -35,6 +38,7 @@ import { import NodeResizer from './components/node-resizer' import NodeControl from './components/node-control' import ErrorHandleOnNode from './components/error-handle/error-handle-on-node' +import RetryOnNode from './components/retry/retry-on-node' import AddVariablePopupWithPosition from './components/add-variable-popup-with-position' import cn from '@/utils/classnames' import BlockIcon from '@/app/components/workflow/block-icon' @@ -237,6 +241,14 @@ const BaseNode: FC = ({
) } + { + hasRetryNode(data.type) && ( + + ) + } { hasErrorHandleNode(data.type) && ( = ({
{cloneElement(children, { id, data })}
+ + { + hasRetryNode(data.type) && ( + + ) + } { hasErrorHandleNode(data.type) && ( = { defaultValue: { @@ -24,6 +27,11 @@ const nodeDefault: NodeDefault = { max_read_timeout: 0, max_write_timeout: 0, }, + retry_config: { + retry_enabled: true, + max_retries: 3, + retry_interval: 100, + }, }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode diff --git a/web/app/components/workflow/nodes/http/panel.tsx b/web/app/components/workflow/nodes/http/panel.tsx index 5c613aa0f3..91b3a6140d 100644 --- a/web/app/components/workflow/nodes/http/panel.tsx +++ b/web/app/components/workflow/nodes/http/panel.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import { memo } from 'react' import { useTranslation } from 'react-i18next' import useConfig from './use-config' import ApiInput from './components/api-input' @@ -18,6 +18,7 @@ import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files' import type { NodePanelProps } from '@/app/components/workflow/types' import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form' import ResultPanel from '@/app/components/workflow/run/result-panel' +import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks' const i18nPrefix = 'workflow.nodes.http' @@ -60,6 +61,10 @@ const Panel: FC> = ({ hideCurlPanel, handleCurlImport, } = useConfig(id, data) + const { + retryDetails, + handleRetryDetailsChange, + } = useRetryDetailShowInSingleRun() // To prevent prompt editor in body not update data. if (!isDataReady) return null @@ -181,6 +186,7 @@ const Panel: FC> = ({ {isShowSingleRun && ( > = ({ runningStatus={runningStatus} onRun={handleRun} onStop={handleStop} - result={} + retryDetails={retryDetails} + onRetryDetailBack={handleRetryDetailsChange} + result={} /> )} {(isShowCurlPanel && !readOnly) && ( @@ -207,4 +215,4 @@ const Panel: FC> = ({ ) } -export default React.memo(Panel) +export default memo(Panel) diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 21ef6395b1..60f68d93e2 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -19,6 +19,7 @@ import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/c import ResultPanel from '@/app/components/workflow/run/result-panel' import Tooltip from '@/app/components/base/tooltip' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks' const i18nPrefix = 'workflow.nodes.llm' @@ -69,6 +70,10 @@ const Panel: FC> = ({ runResult, filterJinjia2InputVar, } = useConfig(id, data) + const { + retryDetails, + handleRetryDetailsChange, + } = useRetryDetailShowInSingleRun() const model = inputs.model @@ -282,12 +287,15 @@ const Panel: FC> = ({ {isShowSingleRun && ( } + retryDetails={retryDetails} + onRetryDetailBack={handleRetryDetailsChange} + result={} /> )}
diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 49e645faa4..d0d4c3a839 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -14,6 +14,8 @@ import Loading from '@/app/components/base/loading' import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import ResultPanel from '@/app/components/workflow/run/result-panel' +import { useRetryDetailShowInSingleRun } from '@/app/components/workflow/nodes/_base/components/retry/hooks' +import { useToolIcon } from '@/app/components/workflow/hooks' const i18nPrefix = 'workflow.nodes.tool' @@ -48,6 +50,11 @@ const Panel: FC> = ({ handleStop, runResult, } = useConfig(id, data) + const toolIcon = useToolIcon(data) + const { + retryDetails, + handleRetryDetailsChange, + } = useRetryDetailShowInSingleRun() if (isLoading) { return
@@ -143,12 +150,16 @@ const Panel: FC> = ({ {isShowSingleRun && ( } + retryDetails={retryDetails} + onRetryDetailBack={handleRetryDetailsChange} + result={} /> )}
diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index 5d932a1ba2..ebd5e7a99d 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -27,6 +27,7 @@ import { getProcessedFilesFromResponse, } from '@/app/components/base/file-uploader/utils' import type { FileEntity } from '@/app/components/base/file-uploader/types' +import type { NodeTracing } from '@/types/workflow' type GetAbortController = (abortController: AbortController) => void type SendCallback = { @@ -381,6 +382,28 @@ export const useChat = ( } })) }, + onNodeRetry: ({ data }) => { + if (data.iteration_id) + return + + const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => { + if (!item.execution_metadata?.parallel_id) + return item.node_id === data.node_id + return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id) + }) + if (responseItem.workflowProcess!.tracing[currentIndex].retryDetail) + responseItem.workflowProcess!.tracing[currentIndex].retryDetail?.push(data as NodeTracing) + else + responseItem.workflowProcess!.tracing[currentIndex].retryDetail = [data as NodeTracing] + + handleUpdateChatList(produce(chatListRef.current, (draft) => { + const currentIndex = draft.findIndex(item => item.id === responseItem.id) + draft[currentIndex] = { + ...draft[currentIndex], + ...responseItem, + } + })) + }, onNodeFinished: ({ data }) => { if (data.iteration_id) return @@ -394,6 +417,9 @@ export const useChat = ( ...(responseItem.workflowProcess!.tracing[currentIndex]?.extras ? { extras: responseItem.workflowProcess!.tracing[currentIndex].extras } : {}), + ...(responseItem.workflowProcess!.tracing[currentIndex]?.retryDetail + ? { retryDetail: responseItem.workflowProcess!.tracing[currentIndex].retryDetail } + : {}), ...data, } as any handleUpdateChatList(produce(chatListRef.current, (draft) => { diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 2139ebd338..210a95f1f8 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -25,6 +25,7 @@ import { import { SimpleBtn } from '../../app/text-generate/item' import Toast from '../../base/toast' import IterationResultPanel from '../run/iteration-result-panel' +import RetryResultPanel from '../run/retry-result-panel' import InputsPanel from './inputs-panel' import cn from '@/utils/classnames' import Loading from '@/app/components/base/loading' @@ -53,11 +54,16 @@ const WorkflowPreview = () => { }, [workflowRunningData]) const [iterationRunResult, setIterationRunResult] = useState([]) + const [retryRunResult, setRetryRunResult] = useState([]) const [iterDurationMap, setIterDurationMap] = useState({}) const [isShowIterationDetail, { setTrue: doShowIterationDetail, setFalse: doHideIterationDetail, }] = useBoolean(false) + const [isShowRetryDetail, { + setTrue: doShowRetryDetail, + setFalse: doHideRetryDetail, + }] = useBoolean(false) const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterationDurationMap: IterationDurationMap) => { setIterDurationMap(iterationDurationMap) @@ -65,6 +71,11 @@ const WorkflowPreview = () => { doShowIterationDetail() }, [doShowIterationDetail]) + const handleRetryDetail = useCallback((detail: NodeTracing[]) => { + setRetryRunResult(detail) + doShowRetryDetail() + }, [doShowRetryDetail]) + if (isShowIterationDetail) { return (
{
)} - {currentTab === 'TRACING' && ( + {currentTab === 'TRACING' && !isShowRetryDetail && ( )} {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( @@ -213,7 +225,14 @@ const WorkflowPreview = () => {
)} - + { + currentTab === 'TRACING' && isShowRetryDetail && ( + + ) + }
)} diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 2bf705f4ce..520c59bf4c 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -9,6 +9,7 @@ import OutputPanel from './output-panel' import ResultPanel from './result-panel' import TracingPanel from './tracing-panel' import IterationResultPanel from './iteration-result-panel' +import RetryResultPanel from './retry-result-panel' import cn from '@/utils/classnames' import { ToastContext } from '@/app/components/base/toast' import Loading from '@/app/components/base/loading' @@ -107,6 +108,18 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe const processNonIterationNode = (item: NodeTracing) => { const { execution_metadata } = item if (!execution_metadata?.iteration_id) { + if (item.status === 'retry') { + const retryNode = result.find(node => node.node_id === item.node_id) + + if (retryNode) { + if (retryNode?.retryDetail) + retryNode.retryDetail.push(item) + else + retryNode.retryDetail = [item] + } + + return + } result.push(item) return } @@ -181,10 +194,15 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe const [iterationRunResult, setIterationRunResult] = useState([]) const [iterDurationMap, setIterDurationMap] = useState({}) + const [retryRunResult, setRetryRunResult] = useState([]) const [isShowIterationDetail, { setTrue: doShowIterationDetail, setFalse: doHideIterationDetail, }] = useBoolean(false) + const [isShowRetryDetail, { + setTrue: doShowRetryDetail, + setFalse: doHideRetryDetail, + }] = useBoolean(false) const handleShowIterationDetail = useCallback((detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => { setIterationRunResult(detail) @@ -192,6 +210,11 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe setIterDurationMap(iterDurationMap) }, [doShowIterationDetail, setIterationRunResult, setIterDurationMap]) + const handleShowRetryDetail = useCallback((detail: NodeTracing[]) => { + setRetryRunResult(detail) + doShowRetryDetail() + }, [doShowRetryDetail, setRetryRunResult]) + if (isShowIterationDetail) { return (
@@ -261,13 +284,22 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe exceptionCounts={runDetail.exceptions_count} /> )} - {!loading && currentTab === 'TRACING' && ( + {!loading && currentTab === 'TRACING' && !isShowRetryDetail && ( )} + { + !loading && currentTab === 'TRACING' && isShowRetryDetail && ( + + ) + }
) diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index d1a02ecfe0..bb07bd1e8c 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -8,6 +8,7 @@ import { RiCheckboxCircleFill, RiErrorWarningLine, RiLoader2Line, + RiRestartFill, } from '@remixicon/react' import BlockIcon from '../block-icon' import { BlockEnum } from '../types' @@ -20,6 +21,7 @@ import Button from '@/app/components/base/button' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' +import { hasRetryNode } from '@/app/components/workflow/utils' type Props = { className?: string @@ -28,8 +30,10 @@ type Props = { hideInfo?: boolean hideProcessDetail?: boolean onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void + onShowRetryDetail?: (detail: NodeTracing[]) => void notShowIterationNav?: boolean justShowIterationNavArrow?: boolean + justShowRetryNavArrow?: boolean } const NodePanel: FC = ({ @@ -39,6 +43,7 @@ const NodePanel: FC = ({ hideInfo = false, hideProcessDetail, onShowIterationDetail, + onShowRetryDetail, notShowIterationNav, justShowIterationNavArrow, }) => { @@ -88,11 +93,17 @@ const NodePanel: FC = ({ }, [nodeInfo.expand, setCollapseState]) const isIterationNode = nodeInfo.node_type === BlockEnum.Iteration + const isRetryNode = hasRetryNode(nodeInfo.node_type) && nodeInfo.retryDetail const handleOnShowIterationDetail = (e: React.MouseEvent) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() onShowIterationDetail?.(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {}) } + const handleOnShowRetryDetail = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + onShowRetryDetail?.(nodeInfo.retryDetail || []) + } return (
@@ -169,6 +180,19 @@ const NodePanel: FC = ({
)} + {isRetryNode && ( + + )}
{(nodeInfo.status === 'stopped') && ( diff --git a/web/app/components/workflow/run/result-panel.tsx b/web/app/components/workflow/run/result-panel.tsx index a688693e4f..bbe740ad48 100644 --- a/web/app/components/workflow/run/result-panel.tsx +++ b/web/app/components/workflow/run/result-panel.tsx @@ -1,11 +1,17 @@ 'use client' import type { FC } from 'react' import { useTranslation } from 'react-i18next' +import { + RiArrowRightSLine, + RiRestartFill, +} from '@remixicon/react' import StatusPanel from './status' import MetaData from './meta' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' +import type { NodeTracing } from '@/types/workflow' +import Button from '@/app/components/base/button' type ResultPanelProps = { inputs?: string @@ -22,6 +28,8 @@ type ResultPanelProps = { showSteps?: boolean exceptionCounts?: number execution_metadata?: any + retry_events?: NodeTracing[] + onShowRetryDetail?: (retries: NodeTracing[]) => void } const ResultPanel: FC = ({ @@ -38,8 +46,11 @@ const ResultPanel: FC = ({ showSteps, exceptionCounts, execution_metadata, + retry_events, + onShowRetryDetail, }) => { const { t } = useTranslation() + return (
@@ -51,6 +62,23 @@ const ResultPanel: FC = ({ exceptionCounts={exceptionCounts} />
+ { + retry_events?.length && onShowRetryDetail && ( +
+ +
+ ) + }
void +} + +const RetryResultPanel: FC = ({ + list, + onBack, +}) => { + const { t } = useTranslation() + + return ( +
+
{ + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + onBack() + }} + > + + {t('workflow.singleRun.back')} +
+ ({ + ...item, + title: `${t('workflow.nodes.common.retry.retry')} ${index + 1}`, + }))} + className='bg-background-section-burn' + /> +
+ ) +} +export default memo(RetryResultPanel) diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index 57b3a5cf5f..ad78971895 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -21,6 +21,7 @@ import type { IterationDurationMap, NodeTracing } from '@/types/workflow' type TracingPanelProps = { list: NodeTracing[] onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void + onShowRetryDetail?: (detail: NodeTracing[]) => void className?: string hideNodeInfo?: boolean hideNodeProcessDetail?: boolean @@ -160,6 +161,7 @@ function buildLogTree(nodes: NodeTracing[], t: (key: string) => string): Tracing const TracingPanel: FC = ({ list, onShowIterationDetail, + onShowRetryDetail, className, hideNodeInfo = false, hideNodeProcessDetail = false, @@ -251,7 +253,9 @@ const TracingPanel: FC = ({ diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index c40ea0de55..6d0fabd90e 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -13,6 +13,7 @@ import type { DefaultValueForm, ErrorHandleTypeEnum, } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/components/retry/types' export enum BlockEnum { Start = 'start', @@ -68,6 +69,7 @@ export type CommonNodeType = { _iterationIndex?: number _inParallelHovering?: boolean _waitingRun?: boolean + _retryIndex?: number isInIteration?: boolean iteration_id?: string selected?: boolean @@ -77,6 +79,7 @@ export type CommonNodeType = { width?: number height?: number error_strategy?: ErrorHandleTypeEnum + retry_config?: WorkflowRetryConfig default_value?: DefaultValueForm[] } & T & Partial> @@ -293,6 +296,7 @@ export enum NodeRunningStatus { Succeeded = 'succeeded', Failed = 'failed', Exception = 'exception', + Retry = 'retry', } export type OnNodeAdd = ( diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts index abe129e6d6..4c61267e4c 100644 --- a/web/app/components/workflow/utils.ts +++ b/web/app/components/workflow/utils.ts @@ -26,6 +26,8 @@ import { } from './types' import { CUSTOM_NODE, + DEFAULT_RETRY_INTERVAL, + DEFAULT_RETRY_MAX, ITERATION_CHILDREN_Z_INDEX, ITERATION_NODE_Z_INDEX, NODE_WIDTH_X_OFFSET, @@ -279,6 +281,14 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => { iterationNodeData.error_handle_mode = iterationNodeData.error_handle_mode || ErrorHandleMode.Terminated } + if (node.data.type === BlockEnum.HttpRequest && !node.data.retry_config) { + node.data.retry_config = { + retry_enabled: true, + max_retries: DEFAULT_RETRY_MAX, + retry_interval: DEFAULT_RETRY_INTERVAL, + } + } + return node }) } @@ -797,3 +807,7 @@ export const isExceptionVariable = (variable: string, nodeType?: BlockEnum) => { return false } + +export const hasRetryNode = (nodeType?: BlockEnum) => { + return nodeType === BlockEnum.LLM || nodeType === BlockEnum.Tool || nodeType === BlockEnum.HttpRequest || nodeType === BlockEnum.Code +} diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index e2a2fdb59d..fab25fa509 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -329,6 +329,20 @@ const translation = { tip: 'There are {{num}} nodes in the process running abnormally, please go to tracing to check the logs.', }, }, + retry: { + retry: 'Retry', + retryOnFailure: 'retry on failure', + maxRetries: 'max retries', + retryInterval: 'retry interval', + retryTimes: 'Retry {{times}} times on failure', + retrying: 'Retrying...', + retrySuccessful: 'Retry successful', + retryFailed: 'Retry failed', + retryFailedTimes: '{{times}} retries failed', + times: 'times', + ms: 'ms', + retries: '{{num}} Retries', + }, }, start: { required: 'required', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 19cda33057..dfad9208e7 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -329,6 +329,20 @@ const translation = { tip: '流程中有 {{num}} 个节点运行异常,请前往追踪查看日志。', }, }, + retry: { + retry: '重试', + retryOnFailure: '失败时重试', + maxRetries: '最大重试次数', + retryInterval: '重试间隔', + retryTimes: '失败时重试 {{times}} 次', + retrying: '重试中...', + retrySuccessful: '重试成功', + retryFailed: '重试失败', + retryFailedTimes: '{{times}} 次重试失败', + times: '次', + ms: '毫秒', + retries: '{{num}} 重试次数', + }, }, start: { required: '必填', diff --git a/web/service/base.ts b/web/service/base.ts index 03421d92a4..22b1a43ad1 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -62,6 +62,7 @@ export type IOnNodeStarted = (nodeStarted: NodeStartedResponse) => void export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void export type IOnIterationStarted = (workflowStarted: IterationStartedResponse) => void export type IOnIterationNext = (workflowStarted: IterationNextResponse) => void +export type IOnNodeRetry = (nodeFinished: NodeFinishedResponse) => void export type IOnIterationFinished = (workflowFinished: IterationFinishedResponse) => void export type IOnParallelBranchStarted = (parallelBranchStarted: ParallelBranchStartedResponse) => void export type IOnParallelBranchFinished = (parallelBranchFinished: ParallelBranchFinishedResponse) => void @@ -92,6 +93,7 @@ export type IOtherOptions = { onIterationStart?: IOnIterationStarted onIterationNext?: IOnIterationNext onIterationFinish?: IOnIterationFinished + onNodeRetry?: IOnNodeRetry onParallelBranchStarted?: IOnParallelBranchStarted onParallelBranchFinished?: IOnParallelBranchFinished onTextChunk?: IOnTextChunk @@ -165,6 +167,7 @@ const handleStream = ( onIterationStart?: IOnIterationStarted, onIterationNext?: IOnIterationNext, onIterationFinish?: IOnIterationFinished, + onNodeRetry?: IOnNodeRetry, onParallelBranchStarted?: IOnParallelBranchStarted, onParallelBranchFinished?: IOnParallelBranchFinished, onTextChunk?: IOnTextChunk, @@ -256,6 +259,9 @@ const handleStream = ( else if (bufferObj.event === 'iteration_completed') { onIterationFinish?.(bufferObj as IterationFinishedResponse) } + else if (bufferObj.event === 'node_retry') { + onNodeRetry?.(bufferObj as NodeFinishedResponse) + } else if (bufferObj.event === 'parallel_branch_started') { onParallelBranchStarted?.(bufferObj as ParallelBranchStartedResponse) } @@ -462,6 +468,7 @@ export const ssePost = ( onIterationStart, onIterationNext, onIterationFinish, + onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, @@ -533,7 +540,7 @@ export const ssePost = ( return } onData?.(str, isFirstMessage, moreInfo) - }, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace) + }, onCompleted, onThought, onMessageEnd, onMessageReplace, onFile, onWorkflowStarted, onWorkflowFinished, onNodeStarted, onNodeFinished, onIterationStart, onIterationNext, onIterationFinish, onNodeRetry, onParallelBranchStarted, onParallelBranchFinished, onTextChunk, onTTSChunk, onTTSEnd, onTextReplace) }).catch((e) => { if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().errorMessage.includes('TypeError: Cannot assign to read only property')) Toast.notify({ type: 'error', message: e }) diff --git a/web/types/workflow.ts b/web/types/workflow.ts index 38f0bb5a40..cd6e9cfa5f 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -52,10 +52,12 @@ export type NodeTracing = { extras?: any expand?: boolean // for UI details?: NodeTracing[][] // iteration detail + retryDetail?: NodeTracing[] // retry detail parallel_id?: string parallel_start_node_id?: string parent_parallel_id?: string parent_parallel_start_node_id?: string + retry_index?: number } export type FetchWorkflowDraftResponse = { @@ -178,6 +180,7 @@ export type NodeFinishedResponse = { } created_at: number files?: FileResponse[] + retry_index?: number } } From 4211b9abbd4ba8cd0aa559280219cfbdfca7f990 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:12:01 +0800 Subject: [PATCH 066/138] chore: translate i18n files (#11892) Co-authored-by: zxhlyh <16177003+zxhlyh@users.noreply.github.com> --- web/i18n/de-DE/workflow.ts | 14 ++++++++++++++ web/i18n/es-ES/workflow.ts | 14 ++++++++++++++ web/i18n/fa-IR/workflow.ts | 14 ++++++++++++++ web/i18n/fr-FR/workflow.ts | 14 ++++++++++++++ web/i18n/hi-IN/workflow.ts | 14 ++++++++++++++ web/i18n/it-IT/workflow.ts | 14 ++++++++++++++ web/i18n/ja-JP/workflow.ts | 14 ++++++++++++++ web/i18n/ko-KR/workflow.ts | 14 ++++++++++++++ web/i18n/pl-PL/workflow.ts | 14 ++++++++++++++ web/i18n/pt-BR/workflow.ts | 14 ++++++++++++++ web/i18n/ro-RO/workflow.ts | 14 ++++++++++++++ web/i18n/ru-RU/workflow.ts | 14 ++++++++++++++ web/i18n/sl-SI/workflow.ts | 14 ++++++++++++++ web/i18n/th-TH/workflow.ts | 14 ++++++++++++++ web/i18n/tr-TR/workflow.ts | 14 ++++++++++++++ web/i18n/uk-UA/workflow.ts | 14 ++++++++++++++ web/i18n/vi-VN/workflow.ts | 14 ++++++++++++++ web/i18n/zh-Hant/workflow.ts | 14 ++++++++++++++ 18 files changed, 252 insertions(+) diff --git a/web/i18n/de-DE/workflow.ts b/web/i18n/de-DE/workflow.ts index 8888e23739..38686f8c1d 100644 --- a/web/i18n/de-DE/workflow.ts +++ b/web/i18n/de-DE/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Fehlerbehandlung', tip: 'Ausnahmebehandlungsstrategie, die ausgelöst wird, wenn ein Knoten auf eine Ausnahme stößt.', }, + retry: { + retry: 'Wiederholen', + retryOnFailure: 'Wiederholen bei Fehler', + maxRetries: 'Max. Wiederholungen', + retryInterval: 'Wiederholungsintervall', + retryTimes: 'Wiederholen Sie {{times}} mal bei einem Fehler', + retrying: 'Wiederholung...', + retrySuccessful: 'Wiederholen erfolgreich', + retryFailed: 'Wiederholung fehlgeschlagen', + retryFailedTimes: '{{times}} fehlgeschlagene Wiederholungen', + times: 'mal', + ms: 'Frau', + retries: '{{num}} Wiederholungen', + }, }, start: { required: 'erforderlich', diff --git a/web/i18n/es-ES/workflow.ts b/web/i18n/es-ES/workflow.ts index c49c611da8..d112ad97b6 100644 --- a/web/i18n/es-ES/workflow.ts +++ b/web/i18n/es-ES/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Manejo de errores', tip: 'Estrategia de control de excepciones, que se desencadena cuando un nodo encuentra una excepción.', }, + retry: { + retryOnFailure: 'Volver a intentarlo en caso de error', + maxRetries: 'Número máximo de reintentos', + retryInterval: 'Intervalo de reintento', + retryTimes: 'Reintentar {{times}} veces en caso de error', + retrying: 'Reintentando...', + retrySuccessful: 'Volver a intentarlo correctamente', + retryFailed: 'Error en el reintento', + retryFailedTimes: '{{veces}} reintentos fallidos', + times: 'veces', + ms: 'Sra.', + retries: '{{num}} Reintentos', + retry: 'Reintentar', + }, }, start: { required: 'requerido', diff --git a/web/i18n/fa-IR/workflow.ts b/web/i18n/fa-IR/workflow.ts index c29f911556..37cba2f16b 100644 --- a/web/i18n/fa-IR/workflow.ts +++ b/web/i18n/fa-IR/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'مدیریت خطا', tip: 'استراتژی مدیریت استثنا، زمانی که یک گره با یک استثنا مواجه می شود، فعال می شود.', }, + retry: { + times: 'بار', + retryInterval: 'فاصله تلاش مجدد', + retryOnFailure: 'در مورد شکست دوباره امتحان کنید', + ms: 'خانم', + retry: 'دوباره', + retries: '{{عدد}} تلاش های مجدد', + maxRetries: 'حداکثر تلاش مجدد', + retrying: 'تلاش مجدد...', + retryFailed: 'تلاش مجدد ناموفق بود', + retryTimes: '{{times}} بار در صورت شکست دوباره امتحان کنید', + retrySuccessful: 'امتحان مجدد با موفقیت انجام دهید', + retryFailedTimes: '{{بار}} تلاش های مجدد ناموفق بود', + }, }, start: { required: 'الزامی', diff --git a/web/i18n/fr-FR/workflow.ts b/web/i18n/fr-FR/workflow.ts index a2b2406113..e7d2802cb4 100644 --- a/web/i18n/fr-FR/workflow.ts +++ b/web/i18n/fr-FR/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Gestion des erreurs', tip: 'Stratégie de gestion des exceptions, déclenchée lorsqu’un nœud rencontre une exception.', }, + retry: { + retry: 'Réessayer', + retryOnFailure: 'Réessai en cas d’échec', + maxRetries: 'Nombre maximal de tentatives', + retryInterval: 'intervalle de nouvelle tentative', + retryTimes: 'Réessayez {{times}} fois en cas d’échec', + retrying: 'Réessayer...', + retrySuccessful: 'Réessai réussi', + retryFailed: 'Échec de la nouvelle tentative', + retryFailedTimes: '{{times}} les tentatives ont échoué', + times: 'fois', + ms: 'ms', + retries: '{{num}} Tentatives', + }, }, start: { required: 'requis', diff --git a/web/i18n/hi-IN/workflow.ts b/web/i18n/hi-IN/workflow.ts index 47589078ce..619abee128 100644 --- a/web/i18n/hi-IN/workflow.ts +++ b/web/i18n/hi-IN/workflow.ts @@ -334,6 +334,20 @@ const translation = { title: 'त्रुटि हैंडलिंग', tip: 'अपवाद हैंडलिंग रणनीति, ट्रिगर जब एक नोड एक अपवाद का सामना करता है।', }, + retry: { + times: 'गुणा', + ms: 'सुश्री', + retryInterval: 'अंतराल का पुनः प्रयास करें', + retrying: 'पुनर्प्रयास।।।', + retryFailed: 'पुनः प्रयास विफल रहा', + retryFailedTimes: '{{times}} पुनः प्रयास विफल रहे', + retryTimes: 'विफलता पर {{times}} बार पुनः प्रयास करें', + retries: '{{num}} पुनर्प्रयास', + maxRetries: 'अधिकतम पुनः प्रयास करता है', + retrySuccessful: 'पुनः प्रयास सफल', + retry: 'पुनर्प्रयास', + retryOnFailure: 'विफलता पर पुनः प्रयास करें', + }, }, start: { required: 'आवश्यक', diff --git a/web/i18n/it-IT/workflow.ts b/web/i18n/it-IT/workflow.ts index e760074e6a..f4390580d5 100644 --- a/web/i18n/it-IT/workflow.ts +++ b/web/i18n/it-IT/workflow.ts @@ -337,6 +337,20 @@ const translation = { title: 'Gestione degli errori', tip: 'Strategia di gestione delle eccezioni, attivata quando un nodo rileva un\'eccezione.', }, + retry: { + retry: 'Ripetere', + retryOnFailure: 'Riprova in caso di errore', + maxRetries: 'Numero massimo di tentativi', + retryInterval: 'Intervallo tentativi', + retryTimes: 'Riprova {{times}} volte in caso di errore', + retrying: 'Riprovare...', + retryFailedTimes: '{{times}} tentativi falliti', + times: 'tempi', + retries: '{{num}} Tentativi', + retrySuccessful: 'Riprova riuscito', + retryFailed: 'Nuovo tentativo non riuscito', + ms: 'ms', + }, }, start: { required: 'richiesto', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index 8305105c22..1aa764a19f 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'エラー処理', tip: 'ノードが例外を検出したときにトリガーされる例外処理戦略。', }, + retry: { + retry: 'リトライ', + retryOnFailure: '失敗時の再試行', + maxRetries: '最大再試行回数', + retryInterval: '再試行間隔', + retrying: '再試行。。。', + retryFailed: '再試行に失敗しました', + times: '倍', + ms: 'さん', + retryTimes: '失敗時に{{times}}回再試行', + retrySuccessful: '再試行に成功しました', + retries: '{{num}} 回の再試行', + retryFailedTimes: '{{times}}回のリトライが失敗しました', + }, }, start: { required: '必須', diff --git a/web/i18n/ko-KR/workflow.ts b/web/i18n/ko-KR/workflow.ts index cc2c1b1a28..4a4d2f9193 100644 --- a/web/i18n/ko-KR/workflow.ts +++ b/web/i18n/ko-KR/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: '오류 처리', tip: '노드에 예외가 발생할 때 트리거되는 예외 처리 전략입니다.', }, + retry: { + retry: '재시도', + retryOnFailure: '실패 시 재시도', + maxRetries: '최대 재시도 횟수', + retryInterval: '재시도 간격', + retryTimes: '실패 시 {{times}}번 재시도', + retrying: '재시도...', + retrySuccessful: '재시도 성공', + retryFailed: '재시도 실패', + retryFailedTimes: '{{times}} 재시도 실패', + times: '배', + ms: '미에스', + retries: '{{숫자}} 재시도', + }, }, start: { required: '필수', diff --git a/web/i18n/pl-PL/workflow.ts b/web/i18n/pl-PL/workflow.ts index 2db6cf2bfb..13784df603 100644 --- a/web/i18n/pl-PL/workflow.ts +++ b/web/i18n/pl-PL/workflow.ts @@ -322,6 +322,20 @@ const translation = { tip: 'Strategia obsługi wyjątków, wyzwalana, gdy węzeł napotka wyjątek.', title: 'Obsługa błędów', }, + retry: { + retry: 'Ponów próbę', + maxRetries: 'Maksymalna liczba ponownych prób', + retryInterval: 'Interwał ponawiania prób', + retryTimes: 'Ponów próbę {{times}} razy w przypadku niepowodzenia', + retrying: 'Ponawianie...', + retrySuccessful: 'Ponawianie próby powiodło się', + retryFailed: 'Ponawianie próby nie powiodło się', + times: 'razy', + retries: '{{liczba}} Ponownych prób', + retryOnFailure: 'Ponawianie próby w przypadku niepowodzenia', + retryFailedTimes: '{{times}} ponawianie prób nie powiodło się', + ms: 'Ms', + }, }, start: { required: 'wymagane', diff --git a/web/i18n/pt-BR/workflow.ts b/web/i18n/pt-BR/workflow.ts index 4d53ec07c7..b99c64cdf4 100644 --- a/web/i18n/pt-BR/workflow.ts +++ b/web/i18n/pt-BR/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Tratamento de erros', tip: 'Estratégia de tratamento de exceções, disparada quando um nó encontra uma exceção.', }, + retry: { + retry: 'Repetir', + retryOnFailure: 'Tentar novamente em caso de falha', + maxRetries: 'Máximo de tentativas', + retryInterval: 'Intervalo de repetição', + retryTimes: 'Tente novamente {{times}} vezes em caso de falha', + retrying: 'Repetindo...', + retrySuccessful: 'Repetição bem-sucedida', + retryFailed: 'Falha na nova tentativa', + retryFailedTimes: '{{times}} tentativas falharam', + times: 'vezes', + ms: 'ms', + retries: '{{num}} Tentativas', + }, }, start: { required: 'requerido', diff --git a/web/i18n/ro-RO/workflow.ts b/web/i18n/ro-RO/workflow.ts index 3dfa6d04ed..b142640c9b 100644 --- a/web/i18n/ro-RO/workflow.ts +++ b/web/i18n/ro-RO/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Gestionarea erorilor', tip: 'Strategie de gestionare a excepțiilor, declanșată atunci când un nod întâlnește o excepție.', }, + retry: { + retry: 'Reîncercare', + retryOnFailure: 'Reîncercați în caz de eșec', + maxRetries: 'numărul maxim de încercări', + retryInterval: 'Interval de reîncercare', + retrying: 'Reîncerca...', + retrySuccessful: 'Reîncercați cu succes', + retryFailed: 'Reîncercarea a eșuat', + retryFailedTimes: '{{times}} reîncercări eșuate', + times: 'Ori', + ms: 'Ms', + retries: '{{num}} Încercări', + retryTimes: 'Reîncercați {{times}} ori în caz de eșec', + }, }, start: { required: 'necesar', diff --git a/web/i18n/ru-RU/workflow.ts b/web/i18n/ru-RU/workflow.ts index 600c59f2ed..49c43b4d6d 100644 --- a/web/i18n/ru-RU/workflow.ts +++ b/web/i18n/ru-RU/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Обработка ошибок', tip: 'Стратегия обработки исключений, запускаемая при обнаружении исключения на узле.', }, + retry: { + retry: 'Снова пробовать', + retryOnFailure: 'Повторная попытка при неудаче', + maxRetries: 'максимальное количество повторных попыток', + retryInterval: 'Интервал повторных попыток', + retryTimes: 'Повторите {{раз}} раз при неудаче', + retrying: 'Повтор...', + retrySuccessful: 'Повторить попытку успешно', + retryFailed: 'Повторная попытка не удалась', + times: 'раз', + ms: 'госпожа', + retryFailedTimes: 'Повторные попытки {{times}} не увенчались успехом', + retries: '{{число}} Повторных попыток', + }, }, start: { required: 'обязательно', diff --git a/web/i18n/sl-SI/workflow.ts b/web/i18n/sl-SI/workflow.ts index 2c9dab8b55..7c40c25e92 100644 --- a/web/i18n/sl-SI/workflow.ts +++ b/web/i18n/sl-SI/workflow.ts @@ -759,6 +759,20 @@ const translation = { title: 'Ravnanje z napakami', tip: 'Strategija ravnanja z izjemami, ki se sproži, ko vozlišče naleti na izjemo.', }, + retry: { + retryOnFailure: 'Ponovni poskus ob neuspehu', + retryInterval: 'Interval ponovnega poskusa', + retrying: 'Ponovnim...', + retry: 'Ponoviti', + retryFailedTimes: '{{times}} ponovni poskusi niso uspeli', + retries: '{{num}} Poskusov', + times: 'Krat', + retryTimes: 'Ponovni poskus {{times}}-krat ob neuspehu', + retryFailed: 'Ponovni poskus ni uspel', + retrySuccessful: 'Ponovni poskus je bil uspešen', + maxRetries: 'Največ ponovnih poskusov', + ms: 'Ms', + }, }, start: { outputVars: { diff --git a/web/i18n/th-TH/workflow.ts b/web/i18n/th-TH/workflow.ts index c4305466aa..b8d2e72de0 100644 --- a/web/i18n/th-TH/workflow.ts +++ b/web/i18n/th-TH/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'การจัดการข้อผิดพลาด', tip: 'กลยุทธ์การจัดการข้อยกเว้น ทริกเกอร์เมื่อโหนดพบข้อยกเว้น', }, + retry: { + retry: 'ลอง', + retryOnFailure: 'ลองใหม่เมื่อล้มเหลว', + maxRetries: 'การลองซ้ําสูงสุด', + retryInterval: 'ช่วงเวลาลองใหม่', + retryTimes: 'ลอง {{times}} ครั้งเมื่อล้มเหลว', + retrying: 'กําลังลองซ้ํา...', + retrySuccessful: 'ลองใหม่สําเร็จ', + retryFailed: 'ลองใหม่ล้มเหลว', + retryFailedTimes: '{{times}} การลองซ้ําล้มเหลว', + times: 'ครั้ง', + retries: '{{num}} ลอง', + ms: 'นางสาว', + }, }, start: { required: 'ต้องระบุ', diff --git a/web/i18n/tr-TR/workflow.ts b/web/i18n/tr-TR/workflow.ts index 951a20e049..edec6a0b49 100644 --- a/web/i18n/tr-TR/workflow.ts +++ b/web/i18n/tr-TR/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Hata İşleme', tip: 'Bir düğüm bir özel durumla karşılaştığında tetiklenen özel durum işleme stratejisi.', }, + retry: { + retry: 'Yeni -den deneme', + retryOnFailure: 'Hata durumunda yeniden dene', + maxRetries: 'En fazla yeniden deneme', + times: 'kere', + retries: '{{sayı}} Yeni -den deneme', + retryFailed: 'Yeniden deneme başarısız oldu', + retryInterval: 'Yeniden deneme aralığı', + retryTimes: 'Hata durumunda {{times}} kez yeniden deneyin', + retryFailedTimes: '{{times}} yeniden denemeleri başarısız oldu', + retrySuccessful: 'Yeniden deneme başarılı', + retrying: 'Yeniden deneniyor...', + ms: 'Ms', + }, }, start: { required: 'gerekli', diff --git a/web/i18n/uk-UA/workflow.ts b/web/i18n/uk-UA/workflow.ts index 2c00d3bf59..29fd9d8188 100644 --- a/web/i18n/uk-UA/workflow.ts +++ b/web/i18n/uk-UA/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: 'Обробка помилок', tip: 'Стратегія обробки винятків, що спрацьовує, коли вузол стикається з винятком.', }, + retry: { + retry: 'Повторити', + retryOnFailure: 'повторити спробу в разі невдачі', + retryInterval: 'Інтервал повторних спроб', + retrying: 'Спроби...', + retryFailed: 'Повторна спроба не вдалася', + times: 'Разів', + ms: 'МС', + retries: '{{num}} Спроб', + maxRetries: 'Максимальна кількість повторних спроб', + retrySuccessful: 'Повторна спроба успішна', + retryFailedTimes: '{{times}} повторні спроби не вдалися', + retryTimes: 'Повторіть спробу {{times}} у разі невдачі', + }, }, start: { required: 'обов\'язковий', diff --git a/web/i18n/vi-VN/workflow.ts b/web/i18n/vi-VN/workflow.ts index 956fe84159..9e16cb5347 100644 --- a/web/i18n/vi-VN/workflow.ts +++ b/web/i18n/vi-VN/workflow.ts @@ -322,6 +322,20 @@ const translation = { tip: 'Chiến lược xử lý ngoại lệ, được kích hoạt khi một nút gặp phải ngoại lệ.', title: 'Xử lý lỗi', }, + retry: { + retry: 'Thử lại', + maxRetries: 'Số lần thử lại tối đa', + retryInterval: 'Khoảng thời gian thử lại', + retryTimes: 'Thử lại {{lần}} lần khi không thành công', + retrying: 'Thử lại...', + retrySuccessful: 'Thử lại thành công', + retryFailed: 'Thử lại không thành công', + retryFailedTimes: '{{lần}} lần thử lại không thành công', + retries: '{{số}} Thử lại', + retryOnFailure: 'Thử lại khi không thành công', + times: 'lần', + ms: 'Ms', + }, }, start: { required: 'bắt buộc', diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts index 4bbbf7a04f..a78c6a2f04 100644 --- a/web/i18n/zh-Hant/workflow.ts +++ b/web/i18n/zh-Hant/workflow.ts @@ -322,6 +322,20 @@ const translation = { title: '錯誤處理', tip: '異常處理策略,當節點遇到異常時觸發。', }, + retry: { + retry: '重試', + retryOnFailure: '失敗時重試', + maxRetries: '最大重試次數', + retryInterval: '重試間隔', + retryTimes: '失敗時重試 {{times}} 次', + retrying: '重試。。。', + retrySuccessful: '重試成功', + retryFailed: '重試失敗', + retryFailedTimes: '{{times}} 次重試失敗', + times: '次', + ms: '女士', + retries: '{{num}}重試', + }, }, start: { required: '必填', From ef7e47d1625c5e678b8bf5da2d253e2fedd9470f Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Fri, 20 Dec 2024 16:12:34 +0800 Subject: [PATCH 067/138] fix: rerank switch (#11897) --- .../components/workflow/nodes/knowledge-retrieval/utils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts b/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts index e9da9acccc..794fcbca4a 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts @@ -129,9 +129,6 @@ export const getMultipleRetrievalConfig = ( reranking_enable: ((allInternal && allEconomic) || allExternal) ? reranking_enable : true, } - if (!rerankModelIsValid) - result.reranking_model = undefined - const setDefaultWeights = () => { result.weights = { vector_setting: { @@ -198,7 +195,6 @@ export const getMultipleRetrievalConfig = ( setDefaultWeights() } } - if (reranking_mode === RerankingModeEnum.RerankingModel && !rerankModelIsValid && shouldSetWeightDefaultValue) { result.reranking_mode = RerankingModeEnum.WeightedScore setDefaultWeights() From ac635c70cd0a7de8cb959ed89be9cadf7420329f Mon Sep 17 00:00:00 2001 From: yihong Date: Fri, 20 Dec 2024 17:19:46 +0800 Subject: [PATCH 068/138] fix: doc can not extract tables (#11879) Signed-off-by: yihong0618 Co-authored-by: akinobu-i --- .../workflow/nodes/document_extractor/node.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/document_extractor/node.py b/api/core/workflow/nodes/document_extractor/node.py index 95d0ea3aab..6d82dbe6d7 100644 --- a/api/core/workflow/nodes/document_extractor/node.py +++ b/api/core/workflow/nodes/document_extractor/node.py @@ -1,6 +1,7 @@ import csv import io import json +import logging import os import tempfile @@ -22,6 +23,8 @@ from models.workflow import WorkflowNodeExecutionStatus from .entities import DocumentExtractorNodeData from .exc import DocumentExtractorError, FileDownloadError, TextExtractionError, UnsupportedFileTypeError +logger = logging.getLogger(__name__) + class DocumentExtractorNode(BaseNode[DocumentExtractorNodeData]): """ @@ -177,10 +180,43 @@ def _extract_text_from_pdf(file_content: bytes) -> str: def _extract_text_from_doc(file_content: bytes) -> str: + """ + Extract text from a DOC/DOCX file. + For now support only paragraph and table add more if needed + """ try: doc_file = io.BytesIO(file_content) doc = docx.Document(doc_file) - return "\n".join([paragraph.text for paragraph in doc.paragraphs]) + text = [] + # Process paragraphs + for paragraph in doc.paragraphs: + if paragraph.text.strip(): + text.append(paragraph.text) + + # Process tables + for table in doc.tables: + # Table header + try: + # table maybe cause errors so ignore it. + if len(table.rows) > 0 and table.rows[0].cells is not None: + # Check if any cell in the table has text + has_content = False + for row in table.rows: + if any(cell.text.strip() for cell in row.cells): + has_content = True + break + + if has_content: + markdown_table = "| " + " | ".join(cell.text for cell in table.rows[0].cells) + " |\n" + markdown_table += "| " + " | ".join(["---"] * len(table.rows[0].cells)) + " |\n" + for row in table.rows[1:]: + markdown_table += "| " + " | ".join(cell.text for cell in row.cells) + " |\n" + text.append(markdown_table) + except Exception as e: + logger.warning(f"Failed to extract table from DOC/DOCX: {e}") + continue + + return "\n".join(text) except Exception as e: raise TextExtractionError(f"Failed to extract text from DOC/DOCX: {str(e)}") from e From 2681bafb76dc3488cfc3d6305b9ada6a63597f9c Mon Sep 17 00:00:00 2001 From: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:23:42 +0900 Subject: [PATCH 069/138] fix: handle document fetching from URL in Anthropic LLM model, solving base64 decoding error (#11858) --- api/core/model_runtime/model_providers/anthropic/llm/llm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/model_runtime/model_providers/anthropic/llm/llm.py b/api/core/model_runtime/model_providers/anthropic/llm/llm.py index e1d35ff872..c0ea8c6325 100644 --- a/api/core/model_runtime/model_providers/anthropic/llm/llm.py +++ b/api/core/model_runtime/model_providers/anthropic/llm/llm.py @@ -531,7 +531,7 @@ class AnthropicLargeLanguageModel(LargeLanguageModel): "source": { "type": "base64", "media_type": message_content.mime_type, - "data": message_content.data, + "data": message_content.base64_data, }, } sub_messages.append(sub_message_dict) From f53741c5b9a084a6117d0e3f6f6be615fa4c2741 Mon Sep 17 00:00:00 2001 From: crazywoola <100913391+crazywoola@users.noreply.github.com> Date: Fri, 20 Dec 2024 18:24:47 +0800 Subject: [PATCH 070/138] revert: these 2 settings (#11906) Signed-off-by: -LAN- Co-authored-by: -LAN- --- docker/.env.example | 3 --- docker/docker-compose.yaml | 2 -- 2 files changed, 5 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index e8ec246ae2..43e67a8db4 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -923,6 +923,3 @@ CREATE_TIDB_SERVICE_JOB_ENABLED=false # Maximum number of submitted thread count in a ThreadPool for parallel node execution MAX_SUBMIT_COUNT=100 -# Proxy -HTTP_PROXY= -HTTPS_PROXY= diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d0738d9305..99bc14c717 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -386,8 +386,6 @@ x-shared-env: &shared-api-worker-env CSP_WHITELIST: ${CSP_WHITELIST:-} CREATE_TIDB_SERVICE_JOB_ENABLED: ${CREATE_TIDB_SERVICE_JOB_ENABLED:-false} MAX_SUBMIT_COUNT: ${MAX_SUBMIT_COUNT:-100} - HTTP_PROXY: ${HTTP_PROXY:-} - HTTPS_PROXY: ${HTTPS_PROXY:-} services: # API service From 6ded06c6d96678c8499d0d20e51de68964bbfff6 Mon Sep 17 00:00:00 2001 From: zhu-an <70234959+xhdd123321@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:26:17 +0800 Subject: [PATCH 071/138] fix: dataset search-input compostion can't work in chrome (#11907) Co-authored-by: zhaoqingyu.1075 --- web/app/components/base/search-input/index.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/web/app/components/base/search-input/index.tsx b/web/app/components/base/search-input/index.tsx index 89345fbe32..556a7bdf49 100644 --- a/web/app/components/base/search-input/index.tsx +++ b/web/app/components/base/search-input/index.tsx @@ -23,6 +23,7 @@ const SearchInput: FC = ({ const { t } = useTranslation() const [focus, setFocus] = useState(false) const isComposing = useRef(false) + const [internalValue, setInternalValue] = useState(value) return (
= ({ white && '!bg-white hover:!bg-white group-hover:!bg-white placeholder:!text-gray-400', )} placeholder={placeholder || t('common.operation.search')!} - value={value} + value={internalValue} onChange={(e) => { + setInternalValue(e.target.value) if (!isComposing.current) onChange(e.target.value) }} onCompositionStart={() => { isComposing.current = true }} - onCompositionEnd={() => { + onCompositionEnd={(e) => { isComposing.current = false + onChange(e.data) }} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)} @@ -63,7 +66,10 @@ const SearchInput: FC = ({ {value && (
onChange('')} + onClick={() => { + onChange('') + setInternalValue('') + }} >
From 7a00798027324b23acd658862129c509d0b89ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B7=A6=E5=87=8C?= <68994887+Wuhu-dsm@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:54:12 +0800 Subject: [PATCH 072/138] Update input-var-list.tsx (#9987) --- .../workflow/nodes/tool/components/input-var-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx index db1a32e319..bab7c20d5b 100644 --- a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx +++ b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx @@ -162,7 +162,7 @@ const InputVarList: FC = ({ readonly={readOnly} isShowNodeName nodeId={nodeId} - value={varInput?.type === VarKindType.constant ? (varInput?.value || '') : (varInput?.value || [])} + value={varInput?.type === VarKindType.constant ? (varInput?.value ?? '') : (varInput?.value ?? [])} onChange={handleNotMixedTypeChange(variable)} onOpen={handleOpen(index)} defaultVarKindType={varInput?.type || (isNumber ? VarKindType.constant : VarKindType.variable)} From de8800f41ac17829562172d4b9a10ecbc2d9b9d3 Mon Sep 17 00:00:00 2001 From: ybalbert001 <120714773+ybalbert001@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:36:13 +0800 Subject: [PATCH 073/138] add three aws tools (#11905) Co-authored-by: Yuanbo Li --- .../builtin/aws/tools/bedrock_retrieve.py | 115 ++++++ .../builtin/aws/tools/bedrock_retrieve.yaml | 87 ++++ .../provider/builtin/aws/tools/nova_canvas.py | 357 +++++++++++++++++ .../builtin/aws/tools/nova_canvas.yaml | 175 +++++++++ .../provider/builtin/aws/tools/nova_reel.py | 371 ++++++++++++++++++ .../provider/builtin/aws/tools/nova_reel.yaml | 124 ++++++ .../provider/builtin/aws/tools/s3_operator.py | 80 ++++ .../builtin/aws/tools/s3_operator.yaml | 98 +++++ 8 files changed, 1407 insertions(+) create mode 100644 api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py create mode 100644 api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml create mode 100644 api/core/tools/provider/builtin/aws/tools/nova_canvas.py create mode 100644 api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml create mode 100644 api/core/tools/provider/builtin/aws/tools/nova_reel.py create mode 100644 api/core/tools/provider/builtin/aws/tools/nova_reel.yaml create mode 100644 api/core/tools/provider/builtin/aws/tools/s3_operator.py create mode 100644 api/core/tools/provider/builtin/aws/tools/s3_operator.yaml diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py new file mode 100644 index 0000000000..050b468b74 --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.py @@ -0,0 +1,115 @@ +import json +import operator +from typing import Any, Optional, Union + +import boto3 + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class BedrockRetrieveTool(BuiltinTool): + bedrock_client: Any = None + knowledge_base_id: str = None + topk: int = None + + def _bedrock_retrieve( + self, query_input: str, knowledge_base_id: str, num_results: int, metadata_filter: Optional[dict] = None + ): + try: + retrieval_query = {"text": query_input} + + retrieval_configuration = {"vectorSearchConfiguration": {"numberOfResults": num_results}} + + # 如果有元数据过滤条件,则添加到检索配置中 + if metadata_filter: + retrieval_configuration["vectorSearchConfiguration"]["filter"] = metadata_filter + + response = self.bedrock_client.retrieve( + knowledgeBaseId=knowledge_base_id, + retrievalQuery=retrieval_query, + retrievalConfiguration=retrieval_configuration, + ) + + results = [] + for result in response.get("retrievalResults", []): + results.append( + { + "content": result.get("content", {}).get("text", ""), + "score": result.get("score", 0.0), + "metadata": result.get("metadata", {}), + } + ) + + return results + except Exception as e: + raise Exception(f"Error retrieving from knowledge base: {str(e)}") + + def _invoke( + self, + user_id: str, + tool_parameters: dict[str, Any], + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + invoke tools + """ + line = 0 + try: + if not self.bedrock_client: + aws_region = tool_parameters.get("aws_region") + if aws_region: + self.bedrock_client = boto3.client("bedrock-agent-runtime", region_name=aws_region) + else: + self.bedrock_client = boto3.client("bedrock-agent-runtime") + + line = 1 + if not self.knowledge_base_id: + self.knowledge_base_id = tool_parameters.get("knowledge_base_id") + if not self.knowledge_base_id: + return self.create_text_message("Please provide knowledge_base_id") + + line = 2 + if not self.topk: + self.topk = tool_parameters.get("topk", 5) + + line = 3 + query = tool_parameters.get("query", "") + if not query: + return self.create_text_message("Please input query") + + # 获取元数据过滤条件(如果存在) + metadata_filter_str = tool_parameters.get("metadata_filter") + metadata_filter = json.loads(metadata_filter_str) if metadata_filter_str else None + + line = 4 + retrieved_docs = self._bedrock_retrieve( + query_input=query, + knowledge_base_id=self.knowledge_base_id, + num_results=self.topk, + metadata_filter=metadata_filter, # 将元数据过滤条件传递给检索方法 + ) + + line = 5 + # Sort results by score in descending order + sorted_docs = sorted(retrieved_docs, key=operator.itemgetter("score"), reverse=True) + + line = 6 + return [self.create_json_message(res) for res in sorted_docs] + + except Exception as e: + return self.create_text_message(f"Exception {str(e)}, line : {line}") + + def validate_parameters(self, parameters: dict[str, Any]) -> None: + """ + Validate the parameters + """ + if not parameters.get("knowledge_base_id"): + raise ValueError("knowledge_base_id is required") + + if not parameters.get("query"): + raise ValueError("query is required") + + # 可选:可以验证元数据过滤条件是否为有效的 JSON 字符串(如果提供) + metadata_filter_str = parameters.get("metadata_filter") + if metadata_filter_str and not isinstance(json.loads(metadata_filter_str), dict): + raise ValueError("metadata_filter must be a valid JSON object") diff --git a/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml new file mode 100644 index 0000000000..9e51d52def --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/bedrock_retrieve.yaml @@ -0,0 +1,87 @@ +identity: + name: bedrock_retrieve + author: AWS + label: + en_US: Bedrock Retrieve + zh_Hans: Bedrock检索 + pt_BR: Bedrock Retrieve + icon: icon.svg + +description: + human: + en_US: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base. You can find deploy instructions on Github Repo - https://github.com/aws-samples/dify-aws-tool + zh_Hans: Amazon Bedrock知识库检索工具, 请参考 Github Repo - https://github.com/aws-samples/dify-aws-tool上的部署说明 + pt_BR: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base. + llm: A tool for retrieving relevant information from Amazon Bedrock Knowledge Base. You can find deploy instructions on Github Repo - https://github.com/aws-samples/dify-aws-tool + +parameters: + - name: knowledge_base_id + type: string + required: true + label: + en_US: Bedrock Knowledge Base ID + zh_Hans: Bedrock知识库ID + pt_BR: Bedrock Knowledge Base ID + human_description: + en_US: ID of the Bedrock Knowledge Base to retrieve from + zh_Hans: 用于检索的Bedrock知识库ID + pt_BR: ID of the Bedrock Knowledge Base to retrieve from + llm_description: ID of the Bedrock Knowledge Base to retrieve from + form: form + + - name: query + type: string + required: true + label: + en_US: Query string + zh_Hans: 查询语句 + pt_BR: Query string + human_description: + en_US: The search query to retrieve relevant information + zh_Hans: 用于检索相关信息的查询语句 + pt_BR: The search query to retrieve relevant information + llm_description: The search query to retrieve relevant information + form: llm + + - name: topk + type: number + required: false + form: form + label: + en_US: Limit for results count + zh_Hans: 返回结果数量限制 + pt_BR: Limit for results count + human_description: + en_US: Maximum number of results to return + zh_Hans: 最大返回结果数量 + pt_BR: Maximum number of results to return + min: 1 + max: 10 + default: 5 + + - name: aws_region + type: string + required: false + label: + en_US: AWS Region + zh_Hans: AWS 区域 + pt_BR: AWS Region + human_description: + en_US: AWS region where the Bedrock Knowledge Base is located + zh_Hans: Bedrock知识库所在的AWS区域 + pt_BR: AWS region where the Bedrock Knowledge Base is located + llm_description: AWS region where the Bedrock Knowledge Base is located + form: form + + - name: metadata_filter + type: string + required: false + label: + en_US: Metadata Filter + zh_Hans: 元数据过滤器 + pt_BR: Metadata Filter + human_description: + en_US: 'JSON formatted filter conditions for metadata (e.g., {"greaterThan": {"key: "aaa", "value": 10}})' + zh_Hans: '元数据的JSON格式过滤条件(例如,{{"greaterThan": {"key: "aaa", "value": 10}})' + pt_BR: 'JSON formatted filter conditions for metadata (e.g., {"greaterThan": {"key: "aaa", "value": 10}})' + form: form diff --git a/api/core/tools/provider/builtin/aws/tools/nova_canvas.py b/api/core/tools/provider/builtin/aws/tools/nova_canvas.py new file mode 100644 index 0000000000..954dbe35a4 --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/nova_canvas.py @@ -0,0 +1,357 @@ +import base64 +import json +import logging +import re +from datetime import datetime +from typing import Any, Union +from urllib.parse import urlparse + +import boto3 + +from core.tools.entities.common_entities import I18nObject +from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter +from core.tools.tool.builtin_tool import BuiltinTool + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class NovaCanvasTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + Invoke AWS Bedrock Nova Canvas model for image generation + """ + # Get common parameters + prompt = tool_parameters.get("prompt", "") + image_output_s3uri = tool_parameters.get("image_output_s3uri", "").strip() + if not prompt: + return self.create_text_message("Please provide a text prompt for image generation.") + if not image_output_s3uri or urlparse(image_output_s3uri).scheme != "s3": + return self.create_text_message("Please provide an valid S3 URI for image output.") + + task_type = tool_parameters.get("task_type", "TEXT_IMAGE") + aws_region = tool_parameters.get("aws_region", "us-east-1") + + # Get common image generation config parameters + width = tool_parameters.get("width", 1024) + height = tool_parameters.get("height", 1024) + cfg_scale = tool_parameters.get("cfg_scale", 8.0) + negative_prompt = tool_parameters.get("negative_prompt", "") + seed = tool_parameters.get("seed", 0) + quality = tool_parameters.get("quality", "standard") + + # Handle S3 image if provided + image_input_s3uri = tool_parameters.get("image_input_s3uri", "") + if task_type != "TEXT_IMAGE": + if not image_input_s3uri or urlparse(image_input_s3uri).scheme != "s3": + return self.create_text_message("Please provide a valid S3 URI for image to image generation.") + + # Parse S3 URI + parsed_uri = urlparse(image_input_s3uri) + bucket = parsed_uri.netloc + key = parsed_uri.path.lstrip("/") + + # Initialize S3 client and download image + s3_client = boto3.client("s3") + response = s3_client.get_object(Bucket=bucket, Key=key) + image_data = response["Body"].read() + + # Base64 encode the image + input_image = base64.b64encode(image_data).decode("utf-8") + + try: + # Initialize Bedrock client + bedrock = boto3.client(service_name="bedrock-runtime", region_name=aws_region) + + # Base image generation config + image_generation_config = { + "width": width, + "height": height, + "cfgScale": cfg_scale, + "seed": seed, + "numberOfImages": 1, + "quality": quality, + } + + # Prepare request body based on task type + body = {"imageGenerationConfig": image_generation_config} + + if task_type == "TEXT_IMAGE": + body["taskType"] = "TEXT_IMAGE" + body["textToImageParams"] = {"text": prompt} + if negative_prompt: + body["textToImageParams"]["negativeText"] = negative_prompt + + elif task_type == "COLOR_GUIDED_GENERATION": + colors = tool_parameters.get("colors", "#ff8080-#ffb280-#ffe680-#ffe680") + if not self._validate_color_string(colors): + return self.create_text_message("Please provide valid colors in hexadecimal format.") + + body["taskType"] = "COLOR_GUIDED_GENERATION" + body["colorGuidedGenerationParams"] = { + "colors": colors.split("-"), + "referenceImage": input_image, + "text": prompt, + } + if negative_prompt: + body["colorGuidedGenerationParams"]["negativeText"] = negative_prompt + + elif task_type == "IMAGE_VARIATION": + similarity_strength = tool_parameters.get("similarity_strength", 0.5) + + body["taskType"] = "IMAGE_VARIATION" + body["imageVariationParams"] = { + "images": [input_image], + "similarityStrength": similarity_strength, + "text": prompt, + } + if negative_prompt: + body["imageVariationParams"]["negativeText"] = negative_prompt + + elif task_type == "INPAINTING": + mask_prompt = tool_parameters.get("mask_prompt") + if not mask_prompt: + return self.create_text_message("Please provide a mask prompt for image inpainting.") + + body["taskType"] = "INPAINTING" + body["inPaintingParams"] = {"image": input_image, "maskPrompt": mask_prompt, "text": prompt} + if negative_prompt: + body["inPaintingParams"]["negativeText"] = negative_prompt + + elif task_type == "OUTPAINTING": + mask_prompt = tool_parameters.get("mask_prompt") + if not mask_prompt: + return self.create_text_message("Please provide a mask prompt for image outpainting.") + outpainting_mode = tool_parameters.get("outpainting_mode", "DEFAULT") + + body["taskType"] = "OUTPAINTING" + body["outPaintingParams"] = { + "image": input_image, + "maskPrompt": mask_prompt, + "outPaintingMode": outpainting_mode, + "text": prompt, + } + if negative_prompt: + body["outPaintingParams"]["negativeText"] = negative_prompt + + elif task_type == "BACKGROUND_REMOVAL": + body["taskType"] = "BACKGROUND_REMOVAL" + body["backgroundRemovalParams"] = {"image": input_image} + + else: + return self.create_text_message(f"Unsupported task type: {task_type}") + + # Call Nova Canvas model + response = bedrock.invoke_model( + body=json.dumps(body), + modelId="amazon.nova-canvas-v1:0", + accept="application/json", + contentType="application/json", + ) + + # Process response + response_body = json.loads(response.get("body").read()) + if response_body.get("error"): + raise Exception(f"Error in model response: {response_body.get('error')}") + base64_image = response_body.get("images")[0] + + # Upload to S3 if image_output_s3uri is provided + try: + # Parse S3 URI for output + parsed_uri = urlparse(image_output_s3uri) + output_bucket = parsed_uri.netloc + output_base_path = parsed_uri.path.lstrip("/") + # Generate filename with timestamp + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + output_key = f"{output_base_path}/canvas-output-{timestamp}.png" + + # Initialize S3 client if not already done + s3_client = boto3.client("s3", region_name=aws_region) + + # Decode base64 image and upload to S3 + image_data = base64.b64decode(base64_image) + s3_client.put_object(Bucket=output_bucket, Key=output_key, Body=image_data, ContentType="image/png") + logger.info(f"Image uploaded to s3://{output_bucket}/{output_key}") + except Exception as e: + logger.exception("Failed to upload image to S3") + # Return image + return [ + self.create_text_message(f"Image is available at: s3://{output_bucket}/{output_key}"), + self.create_blob_message( + blob=base64.b64decode(base64_image), + meta={"mime_type": "image/png"}, + save_as=self.VariableKey.IMAGE.value, + ), + ] + + except Exception as e: + return self.create_text_message(f"Failed to generate image: {str(e)}") + + def _validate_color_string(self, color_string) -> bool: + color_pattern = r"^#[0-9a-fA-F]{6}(?:-#[0-9a-fA-F]{6})*$" + + if re.match(color_pattern, color_string): + return True + return False + + def get_runtime_parameters(self) -> list[ToolParameter]: + parameters = [ + ToolParameter( + name="prompt", + label=I18nObject(en_US="Prompt", zh_Hans="提示词"), + type=ToolParameter.ToolParameterType.STRING, + required=True, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="Text description of the image you want to generate or modify", + zh_Hans="您想要生成或修改的图像的文本描述", + ), + llm_description="Describe the image you want to generate or how you want to modify the input image", + ), + ToolParameter( + name="image_input_s3uri", + label=I18nObject(en_US="Input image s3 uri", zh_Hans="输入图片的s3 uri"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject(en_US="Image to be modified", zh_Hans="想要修改的图片"), + ), + ToolParameter( + name="image_output_s3uri", + label=I18nObject(en_US="Output Image S3 URI", zh_Hans="输出图片的S3 URI目录"), + type=ToolParameter.ToolParameterType.STRING, + required=True, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="S3 URI where the generated image should be uploaded", zh_Hans="生成的图像应该上传到的S3 URI" + ), + ), + ToolParameter( + name="width", + label=I18nObject(en_US="Width", zh_Hans="宽度"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=1024, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Width of the generated image", zh_Hans="生成图像的宽度"), + ), + ToolParameter( + name="height", + label=I18nObject(en_US="Height", zh_Hans="高度"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=1024, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Height of the generated image", zh_Hans="生成图像的高度"), + ), + ToolParameter( + name="cfg_scale", + label=I18nObject(en_US="CFG Scale", zh_Hans="CFG比例"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=8.0, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="How strongly the image should conform to the prompt", zh_Hans="图像应该多大程度上符合提示词" + ), + ), + ToolParameter( + name="negative_prompt", + label=I18nObject(en_US="Negative Prompt", zh_Hans="负面提示词"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default="", + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="Things you don't want in the generated image", zh_Hans="您不想在生成的图像中出现的内容" + ), + ), + ToolParameter( + name="seed", + label=I18nObject(en_US="Seed", zh_Hans="种子值"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=0, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Random seed for image generation", zh_Hans="图像生成的随机种子"), + ), + ToolParameter( + name="aws_region", + label=I18nObject(en_US="AWS Region", zh_Hans="AWS 区域"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default="us-east-1", + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="AWS region for Bedrock service", zh_Hans="Bedrock 服务的 AWS 区域"), + ), + ToolParameter( + name="task_type", + label=I18nObject(en_US="Task Type", zh_Hans="任务类型"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default="TEXT_IMAGE", + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject(en_US="Type of image generation task", zh_Hans="图像生成任务的类型"), + ), + ToolParameter( + name="quality", + label=I18nObject(en_US="Quality", zh_Hans="质量"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default="standard", + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="Quality of the generated image (standard or premium)", zh_Hans="生成图像的质量(标准或高级)" + ), + ), + ToolParameter( + name="colors", + label=I18nObject(en_US="Colors", zh_Hans="颜色"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="List of colors for color-guided generation, example: #ff8080-#ffb280-#ffe680-#ffe680", + zh_Hans="颜色引导生成的颜色列表, 例子: #ff8080-#ffb280-#ffe680-#ffe680", + ), + ), + ToolParameter( + name="similarity_strength", + label=I18nObject(en_US="Similarity Strength", zh_Hans="相似度强度"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=0.5, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="How similar the generated image should be to the input image (0.0 to 1.0)", + zh_Hans="生成的图像应该与输入图像的相似程度(0.0到1.0)", + ), + ), + ToolParameter( + name="mask_prompt", + label=I18nObject(en_US="Mask Prompt", zh_Hans="蒙版提示词"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="Text description to generate mask for inpainting/outpainting", + zh_Hans="用于生成内补绘制/外补绘制蒙版的文本描述", + ), + ), + ToolParameter( + name="outpainting_mode", + label=I18nObject(en_US="Outpainting Mode", zh_Hans="外补绘制模式"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default="DEFAULT", + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="Mode for outpainting (DEFAULT or other supported modes)", + zh_Hans="外补绘制的模式(DEFAULT或其他支持的模式)", + ), + ), + ] + + return parameters diff --git a/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml b/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml new file mode 100644 index 0000000000..a72fd9c8ef --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/nova_canvas.yaml @@ -0,0 +1,175 @@ +identity: + name: nova_canvas + author: AWS + label: + en_US: AWS Bedrock Nova Canvas + zh_Hans: AWS Bedrock Nova Canvas + icon: icon.svg +description: + human: + en_US: A tool for generating and modifying images using AWS Bedrock's Nova Canvas model. Supports text-to-image, color-guided generation, image variation, inpainting, outpainting, and background removal. Input parameters reference https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html + zh_Hans: 使用 AWS Bedrock 的 Nova Canvas 模型生成和修改图像的工具。支持文生图、颜色引导生成、图像变体、内补绘制、外补绘制和背景移除功能, 输入参数参考 https://docs.aws.amazon.com/nova/latest/userguide/image-gen-req-resp-structure.html。 + llm: Generate or modify images using AWS Bedrock's Nova Canvas model with multiple task types including text-to-image, color-guided generation, image variation, inpainting, outpainting, and background removal. +parameters: + - name: task_type + type: string + required: false + default: TEXT_IMAGE + label: + en_US: Task Type + zh_Hans: 任务类型 + human_description: + en_US: Type of image generation task (TEXT_IMAGE, COLOR_GUIDED_GENERATION, IMAGE_VARIATION, INPAINTING, OUTPAINTING, BACKGROUND_REMOVAL) + zh_Hans: 图像生成任务的类型(文生图、颜色引导生成、图像变体、内补绘制、外补绘制、背景移除) + form: llm + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: Text description of the image you want to generate or modify + zh_Hans: 您想要生成或修改的图像的文本描述 + llm_description: Describe the image you want to generate or how you want to modify the input image + form: llm + - name: image_input_s3uri + type: string + required: false + label: + en_US: Input image s3 uri + zh_Hans: 输入图片的s3 uri + human_description: + en_US: The input image to modify (required for all modes except TEXT_IMAGE) + zh_Hans: 要修改的输入图像(除文生图外的所有模式都需要) + llm_description: The input image you want to modify. Required for all modes except TEXT_IMAGE. + form: llm + - name: image_output_s3uri + type: string + required: true + label: + en_US: Output S3 URI + zh_Hans: 输出S3 URI + human_description: + en_US: The S3 URI where the generated image will be saved. If provided, the image will be uploaded with name format canvas-output-{timestamp}.png + zh_Hans: 生成的图像将保存到的S3 URI。如果提供,图像将以canvas-output-{timestamp}.png的格式上传 + llm_description: Optional S3 URI where the generated image will be uploaded. The image will be saved with a timestamp-based filename. + form: form + - name: negative_prompt + type: string + required: false + label: + en_US: Negative Prompt + zh_Hans: 负面提示词 + human_description: + en_US: Things you don't want in the generated image + zh_Hans: 您不想在生成的图像中出现的内容 + form: llm + - name: width + type: number + required: false + label: + en_US: Width + zh_Hans: 宽度 + human_description: + en_US: Width of the generated image + zh_Hans: 生成图像的宽度 + form: form + default: 1024 + - name: height + type: number + required: false + label: + en_US: Height + zh_Hans: 高度 + human_description: + en_US: Height of the generated image + zh_Hans: 生成图像的高度 + form: form + default: 1024 + - name: cfg_scale + type: number + required: false + label: + en_US: CFG Scale + zh_Hans: CFG比例 + human_description: + en_US: How strongly the image should conform to the prompt + zh_Hans: 图像应该多大程度上符合提示词 + form: form + default: 8.0 + - name: seed + type: number + required: false + label: + en_US: Seed + zh_Hans: 种子值 + human_description: + en_US: Random seed for image generation + zh_Hans: 图像生成的随机种子 + form: form + default: 0 + - name: aws_region + type: string + required: false + default: us-east-1 + label: + en_US: AWS Region + zh_Hans: AWS 区域 + human_description: + en_US: AWS region for Bedrock service + zh_Hans: Bedrock 服务的 AWS 区域 + form: form + - name: quality + type: string + required: false + default: standard + label: + en_US: Quality + zh_Hans: 质量 + human_description: + en_US: Quality of the generated image (standard or premium) + zh_Hans: 生成图像的质量(标准或高级) + form: form + - name: colors + type: string + required: false + label: + en_US: Colors + zh_Hans: 颜色 + human_description: + en_US: List of colors for color-guided generation + zh_Hans: 颜色引导生成的颜色列表 + form: form + - name: similarity_strength + type: number + required: false + default: 0.5 + label: + en_US: Similarity Strength + zh_Hans: 相似度强度 + human_description: + en_US: How similar the generated image should be to the input image (0.0 to 1.0) + zh_Hans: 生成的图像应该与输入图像的相似程度(0.0到1.0) + form: form + - name: mask_prompt + type: string + required: false + label: + en_US: Mask Prompt + zh_Hans: 蒙版提示词 + human_description: + en_US: Text description to generate mask for inpainting/outpainting + zh_Hans: 用于生成内补绘制/外补绘制蒙版的文本描述 + form: llm + - name: outpainting_mode + type: string + required: false + default: DEFAULT + label: + en_US: Outpainting Mode + zh_Hans: 外补绘制模式 + human_description: + en_US: Mode for outpainting (DEFAULT or other supported modes) + zh_Hans: 外补绘制的模式(DEFAULT或其他支持的模式) + form: form diff --git a/api/core/tools/provider/builtin/aws/tools/nova_reel.py b/api/core/tools/provider/builtin/aws/tools/nova_reel.py new file mode 100644 index 0000000000..bfd3d302b2 --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/nova_reel.py @@ -0,0 +1,371 @@ +import base64 +import logging +import time +from io import BytesIO +from typing import Any, Optional, Union +from urllib.parse import urlparse + +import boto3 +from botocore.exceptions import ClientError +from PIL import Image + +from core.tools.entities.common_entities import I18nObject +from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter +from core.tools.tool.builtin_tool import BuiltinTool + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +NOVA_REEL_DEFAULT_REGION = "us-east-1" +NOVA_REEL_DEFAULT_DIMENSION = "1280x720" +NOVA_REEL_DEFAULT_FPS = 24 +NOVA_REEL_DEFAULT_DURATION = 6 +NOVA_REEL_MODEL_ID = "amazon.nova-reel-v1:0" +NOVA_REEL_STATUS_CHECK_INTERVAL = 5 + +# Image requirements +NOVA_REEL_REQUIRED_IMAGE_WIDTH = 1280 +NOVA_REEL_REQUIRED_IMAGE_HEIGHT = 720 +NOVA_REEL_REQUIRED_IMAGE_MODE = "RGB" + + +class NovaReelTool(BuiltinTool): + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + Invoke AWS Bedrock Nova Reel model for video generation. + + Args: + user_id: The ID of the user making the request + tool_parameters: Dictionary containing the tool parameters + + Returns: + ToolInvokeMessage containing either the video content or status information + """ + try: + # Validate and extract parameters + params = self._validate_and_extract_parameters(tool_parameters) + if isinstance(params, ToolInvokeMessage): + return params + + # Initialize AWS clients + bedrock, s3_client = self._initialize_aws_clients(params["aws_region"]) + + # Prepare model input + model_input = self._prepare_model_input(params, s3_client) + if isinstance(model_input, ToolInvokeMessage): + return model_input + + # Start video generation + invocation = self._start_video_generation(bedrock, model_input, params["video_output_s3uri"]) + invocation_arn = invocation["invocationArn"] + + # Handle async/sync mode + return self._handle_generation_mode(bedrock, s3_client, invocation_arn, params["async_mode"]) + + except ClientError as e: + error_code = e.response.get("Error", {}).get("Code", "Unknown") + error_message = e.response.get("Error", {}).get("Message", str(e)) + logger.exception(f"AWS API error: {error_code} - {error_message}") + return self.create_text_message(f"AWS service error: {error_code} - {error_message}") + except Exception as e: + logger.error(f"Unexpected error in video generation: {str(e)}", exc_info=True) + return self.create_text_message(f"Failed to generate video: {str(e)}") + + def _validate_and_extract_parameters( + self, tool_parameters: dict[str, Any] + ) -> Union[dict[str, Any], ToolInvokeMessage]: + """Validate and extract parameters from the input dictionary.""" + prompt = tool_parameters.get("prompt", "") + video_output_s3uri = tool_parameters.get("video_output_s3uri", "").strip() + + # Validate required parameters + if not prompt: + return self.create_text_message("Please provide a text prompt for video generation.") + if not video_output_s3uri: + return self.create_text_message("Please provide an S3 URI for video output.") + + # Validate S3 URI format + if not video_output_s3uri.startswith("s3://"): + return self.create_text_message("Invalid S3 URI format. Must start with 's3://'") + + # Ensure S3 URI ends with '/' + video_output_s3uri = video_output_s3uri if video_output_s3uri.endswith("/") else video_output_s3uri + "/" + + return { + "prompt": prompt, + "video_output_s3uri": video_output_s3uri, + "image_input_s3uri": tool_parameters.get("image_input_s3uri", "").strip(), + "aws_region": tool_parameters.get("aws_region", NOVA_REEL_DEFAULT_REGION), + "dimension": tool_parameters.get("dimension", NOVA_REEL_DEFAULT_DIMENSION), + "seed": int(tool_parameters.get("seed", 0)), + "fps": int(tool_parameters.get("fps", NOVA_REEL_DEFAULT_FPS)), + "duration": int(tool_parameters.get("duration", NOVA_REEL_DEFAULT_DURATION)), + "async_mode": bool(tool_parameters.get("async", True)), + } + + def _initialize_aws_clients(self, region: str) -> tuple[Any, Any]: + """Initialize AWS Bedrock and S3 clients.""" + bedrock = boto3.client(service_name="bedrock-runtime", region_name=region) + s3_client = boto3.client("s3", region_name=region) + return bedrock, s3_client + + def _prepare_model_input(self, params: dict[str, Any], s3_client: Any) -> Union[dict[str, Any], ToolInvokeMessage]: + """Prepare the input for the Nova Reel model.""" + model_input = { + "taskType": "TEXT_VIDEO", + "textToVideoParams": {"text": params["prompt"]}, + "videoGenerationConfig": { + "durationSeconds": params["duration"], + "fps": params["fps"], + "dimension": params["dimension"], + "seed": params["seed"], + }, + } + + # Add image if provided + if params["image_input_s3uri"]: + try: + image_data = self._get_image_from_s3(s3_client, params["image_input_s3uri"]) + if not image_data: + return self.create_text_message("Failed to retrieve image from S3") + + # Process and validate image + processed_image = self._process_and_validate_image(image_data) + if isinstance(processed_image, ToolInvokeMessage): + return processed_image + + # Convert processed image to base64 + img_buffer = BytesIO() + processed_image.save(img_buffer, format="PNG") + img_buffer.seek(0) + input_image_base64 = base64.b64encode(img_buffer.getvalue()).decode("utf-8") + + model_input["textToVideoParams"]["images"] = [ + {"format": "png", "source": {"bytes": input_image_base64}} + ] + except Exception as e: + logger.error(f"Error processing input image: {str(e)}", exc_info=True) + return self.create_text_message(f"Failed to process input image: {str(e)}") + + return model_input + + def _process_and_validate_image(self, image_data: bytes) -> Union[Image.Image, ToolInvokeMessage]: + """ + Process and validate the input image according to Nova Reel requirements. + + Requirements: + - Must be 1280x720 pixels + - Must be RGB format (8 bits per channel) + - If PNG, alpha channel must not have transparent/translucent pixels + """ + try: + # Open image + img = Image.open(BytesIO(image_data)) + + # Convert RGBA to RGB if needed, ensuring no transparency + if img.mode == "RGBA": + # Check for transparency + if img.getchannel("A").getextrema()[0] < 255: + return self.create_text_message( + "PNG image contains transparent or translucent pixels, which is not supported. " + "Please provide an image without transparency." + ) + # Convert to RGB + img = img.convert("RGB") + elif img.mode != "RGB": + # Convert any other mode to RGB + img = img.convert("RGB") + + # Validate/adjust dimensions + if img.size != (NOVA_REEL_REQUIRED_IMAGE_WIDTH, NOVA_REEL_REQUIRED_IMAGE_HEIGHT): + logger.warning( + f"Image dimensions {img.size} do not match required dimensions " + f"({NOVA_REEL_REQUIRED_IMAGE_WIDTH}x{NOVA_REEL_REQUIRED_IMAGE_HEIGHT}). Resizing..." + ) + img = img.resize( + (NOVA_REEL_REQUIRED_IMAGE_WIDTH, NOVA_REEL_REQUIRED_IMAGE_HEIGHT), Image.Resampling.LANCZOS + ) + + # Validate bit depth + if img.mode != NOVA_REEL_REQUIRED_IMAGE_MODE: + return self.create_text_message( + f"Image must be in {NOVA_REEL_REQUIRED_IMAGE_MODE} mode with 8 bits per channel" + ) + + return img + + except Exception as e: + logger.error(f"Error processing image: {str(e)}", exc_info=True) + return self.create_text_message( + "Failed to process image. Please ensure the image is a valid JPEG or PNG file." + ) + + def _get_image_from_s3(self, s3_client: Any, s3_uri: str) -> Optional[bytes]: + """Download and return image data from S3.""" + parsed_uri = urlparse(s3_uri) + bucket = parsed_uri.netloc + key = parsed_uri.path.lstrip("/") + + response = s3_client.get_object(Bucket=bucket, Key=key) + return response["Body"].read() + + def _start_video_generation(self, bedrock: Any, model_input: dict[str, Any], output_s3uri: str) -> dict[str, Any]: + """Start the async video generation process.""" + return bedrock.start_async_invoke( + modelId=NOVA_REEL_MODEL_ID, + modelInput=model_input, + outputDataConfig={"s3OutputDataConfig": {"s3Uri": output_s3uri}}, + ) + + def _handle_generation_mode( + self, bedrock: Any, s3_client: Any, invocation_arn: str, async_mode: bool + ) -> ToolInvokeMessage: + """Handle async or sync video generation mode.""" + invocation_response = bedrock.get_async_invoke(invocationArn=invocation_arn) + video_path = invocation_response["outputDataConfig"]["s3OutputDataConfig"]["s3Uri"] + video_uri = f"{video_path}/output.mp4" + + if async_mode: + return self.create_text_message( + f"Video generation started.\nInvocation ARN: {invocation_arn}\n" + f"Video will be available at: {video_uri}" + ) + + return self._wait_for_completion(bedrock, s3_client, invocation_arn) + + def _wait_for_completion(self, bedrock: Any, s3_client: Any, invocation_arn: str) -> ToolInvokeMessage: + """Wait for video generation completion and handle the result.""" + while True: + status_response = bedrock.get_async_invoke(invocationArn=invocation_arn) + status = status_response["status"] + video_path = status_response["outputDataConfig"]["s3OutputDataConfig"]["s3Uri"] + + if status == "Completed": + return self._handle_completed_video(s3_client, video_path) + elif status == "Failed": + failure_message = status_response.get("failureMessage", "Unknown error") + return self.create_text_message(f"Video generation failed.\nError: {failure_message}") + elif status == "InProgress": + time.sleep(NOVA_REEL_STATUS_CHECK_INTERVAL) + else: + return self.create_text_message(f"Unexpected status: {status}") + + def _handle_completed_video(self, s3_client: Any, video_path: str) -> ToolInvokeMessage: + """Handle completed video generation and return the result.""" + parsed_uri = urlparse(video_path) + bucket = parsed_uri.netloc + key = parsed_uri.path.lstrip("/") + "/output.mp4" + + try: + response = s3_client.get_object(Bucket=bucket, Key=key) + video_content = response["Body"].read() + return [ + self.create_text_message(f"Video is available at: {video_path}/output.mp4"), + self.create_blob_message(blob=video_content, meta={"mime_type": "video/mp4"}, save_as="output.mp4"), + ] + except Exception as e: + logger.error(f"Error downloading video: {str(e)}", exc_info=True) + return self.create_text_message( + f"Video generation completed but failed to download video: {str(e)}\n" + f"Video is available at: s3://{bucket}/{key}" + ) + + def get_runtime_parameters(self) -> list[ToolParameter]: + """Define the tool's runtime parameters.""" + parameters = [ + ToolParameter( + name="prompt", + label=I18nObject(en_US="Prompt", zh_Hans="提示词"), + type=ToolParameter.ToolParameterType.STRING, + required=True, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="Text description of the video you want to generate", zh_Hans="您想要生成的视频的文本描述" + ), + llm_description="Describe the video you want to generate", + ), + ToolParameter( + name="video_output_s3uri", + label=I18nObject(en_US="Output S3 URI", zh_Hans="输出S3 URI"), + type=ToolParameter.ToolParameterType.STRING, + required=True, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="S3 URI where the generated video will be stored", zh_Hans="生成的视频将存储的S3 URI" + ), + ), + ToolParameter( + name="dimension", + label=I18nObject(en_US="Dimension", zh_Hans="尺寸"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default=NOVA_REEL_DEFAULT_DIMENSION, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Video dimensions (width x height)", zh_Hans="视频尺寸(宽 x 高)"), + ), + ToolParameter( + name="duration", + label=I18nObject(en_US="Duration", zh_Hans="时长"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=NOVA_REEL_DEFAULT_DURATION, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Video duration in seconds", zh_Hans="视频时长(秒)"), + ), + ToolParameter( + name="seed", + label=I18nObject(en_US="Seed", zh_Hans="种子值"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=0, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="Random seed for video generation", zh_Hans="视频生成的随机种子"), + ), + ToolParameter( + name="fps", + label=I18nObject(en_US="FPS", zh_Hans="帧率"), + type=ToolParameter.ToolParameterType.NUMBER, + required=False, + default=NOVA_REEL_DEFAULT_FPS, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject( + en_US="Frames per second for the generated video", zh_Hans="生成视频的每秒帧数" + ), + ), + ToolParameter( + name="async", + label=I18nObject(en_US="Async Mode", zh_Hans="异步模式"), + type=ToolParameter.ToolParameterType.BOOLEAN, + required=False, + default=True, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="Whether to run in async mode (return immediately) or sync mode (wait for completion)", + zh_Hans="是否以异步模式运行(立即返回)或同步模式(等待完成)", + ), + ), + ToolParameter( + name="aws_region", + label=I18nObject(en_US="AWS Region", zh_Hans="AWS 区域"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + default=NOVA_REEL_DEFAULT_REGION, + form=ToolParameter.ToolParameterForm.FORM, + human_description=I18nObject(en_US="AWS region for Bedrock service", zh_Hans="Bedrock 服务的 AWS 区域"), + ), + ToolParameter( + name="image_input_s3uri", + label=I18nObject(en_US="Input Image S3 URI", zh_Hans="输入图像S3 URI"), + type=ToolParameter.ToolParameterType.STRING, + required=False, + form=ToolParameter.ToolParameterForm.LLM, + human_description=I18nObject( + en_US="S3 URI of the input image (1280x720 JPEG/PNG) to use as first frame", + zh_Hans="用作第一帧的输入图像(1280x720 JPEG/PNG)的S3 URI", + ), + ), + ] + + return parameters diff --git a/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml b/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml new file mode 100644 index 0000000000..16df5ba5c9 --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/nova_reel.yaml @@ -0,0 +1,124 @@ +identity: + name: nova_reel + author: AWS + label: + en_US: AWS Bedrock Nova Reel + zh_Hans: AWS Bedrock Nova Reel + icon: icon.svg +description: + human: + en_US: A tool for generating videos using AWS Bedrock's Nova Reel model. Supports text-to-video generation and image-to-video generation with customizable parameters like duration, FPS, and dimensions. Input parameters reference https://docs.aws.amazon.com/nova/latest/userguide/video-generation.html + zh_Hans: 使用 AWS Bedrock 的 Nova Reel 模型生成视频的工具。支持文本生成视频和图像生成视频功能,可自定义持续时间、帧率和尺寸等参数。输入参数参考 https://docs.aws.amazon.com/nova/latest/userguide/video-generation.html + llm: Generate videos using AWS Bedrock's Nova Reel model with support for both text-to-video and image-to-video generation, allowing customization of video properties like duration, frame rate, and resolution. + +parameters: + - name: prompt + type: string + required: true + label: + en_US: Prompt + zh_Hans: 提示词 + human_description: + en_US: Text description of the video you want to generate + zh_Hans: 您想要生成的视频的文本描述 + llm_description: Describe the video you want to generate + form: llm + + - name: video_output_s3uri + type: string + required: true + label: + en_US: Output S3 URI + zh_Hans: 输出S3 URI + human_description: + en_US: S3 URI where the generated video will be stored + zh_Hans: 生成的视频将存储的S3 URI + form: form + + - name: dimension + type: string + required: false + default: 1280x720 + label: + en_US: Dimension + zh_Hans: 尺寸 + human_description: + en_US: Video dimensions (width x height) + zh_Hans: 视频尺寸(宽 x 高) + form: form + + - name: duration + type: number + required: false + default: 6 + label: + en_US: Duration + zh_Hans: 时长 + human_description: + en_US: Video duration in seconds + zh_Hans: 视频时长(秒) + form: form + + - name: seed + type: number + required: false + default: 0 + label: + en_US: Seed + zh_Hans: 种子值 + human_description: + en_US: Random seed for video generation + zh_Hans: 视频生成的随机种子 + form: form + + - name: fps + type: number + required: false + default: 24 + label: + en_US: FPS + zh_Hans: 帧率 + human_description: + en_US: Frames per second for the generated video + zh_Hans: 生成视频的每秒帧数 + form: form + + - name: async + type: boolean + required: false + default: true + label: + en_US: Async Mode + zh_Hans: 异步模式 + human_description: + en_US: Whether to run in async mode (return immediately) or sync mode (wait for completion) + zh_Hans: 是否以异步模式运行(立即返回)或同步模式(等待完成) + form: llm + + - name: aws_region + type: string + required: false + default: us-east-1 + label: + en_US: AWS Region + zh_Hans: AWS 区域 + human_description: + en_US: AWS region for Bedrock service + zh_Hans: Bedrock 服务的 AWS 区域 + form: form + + - name: image_input_s3uri + type: string + required: false + label: + en_US: Input Image S3 URI + zh_Hans: 输入图像S3 URI + human_description: + en_US: S3 URI of the input image (1280x720 JPEG/PNG) to use as first frame + zh_Hans: 用作第一帧的输入图像(1280x720 JPEG/PNG)的S3 URI + form: llm + +development: + dependencies: + - boto3 + - pillow diff --git a/api/core/tools/provider/builtin/aws/tools/s3_operator.py b/api/core/tools/provider/builtin/aws/tools/s3_operator.py new file mode 100644 index 0000000000..e4026b07a8 --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/s3_operator.py @@ -0,0 +1,80 @@ +from typing import Any, Union +from urllib.parse import urlparse + +import boto3 + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class S3Operator(BuiltinTool): + s3_client: Any = None + + def _invoke( + self, + user_id: str, + tool_parameters: dict[str, Any], + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + """ + invoke tools + """ + try: + # Initialize S3 client if not already done + if not self.s3_client: + aws_region = tool_parameters.get("aws_region") + if aws_region: + self.s3_client = boto3.client("s3", region_name=aws_region) + else: + self.s3_client = boto3.client("s3") + + # Parse S3 URI + s3_uri = tool_parameters.get("s3_uri") + if not s3_uri: + return self.create_text_message("s3_uri parameter is required") + + parsed_uri = urlparse(s3_uri) + if parsed_uri.scheme != "s3": + return self.create_text_message("Invalid S3 URI format. Must start with 's3://'") + + bucket = parsed_uri.netloc + # Remove leading slash from key + key = parsed_uri.path.lstrip("/") + + operation_type = tool_parameters.get("operation_type", "read") + generate_presign_url = tool_parameters.get("generate_presign_url", False) + presign_expiry = int(tool_parameters.get("presign_expiry", 3600)) # default 1 hour + + if operation_type == "write": + text_content = tool_parameters.get("text_content") + if not text_content: + return self.create_text_message("text_content parameter is required for write operation") + + # Write content to S3 + self.s3_client.put_object(Bucket=bucket, Key=key, Body=text_content.encode("utf-8")) + result = f"s3://{bucket}/{key}" + + # Generate presigned URL for the written object if requested + if generate_presign_url: + result = self.s3_client.generate_presigned_url( + "get_object", Params={"Bucket": bucket, "Key": key}, ExpiresIn=presign_expiry + ) + + else: # read operation + # Get object from S3 + response = self.s3_client.get_object(Bucket=bucket, Key=key) + result = response["Body"].read().decode("utf-8") + + # Generate presigned URL if requested + if generate_presign_url: + result = self.s3_client.generate_presigned_url( + "get_object", Params={"Bucket": bucket, "Key": key}, ExpiresIn=presign_expiry + ) + + return self.create_text_message(text=result) + + except self.s3_client.exceptions.NoSuchBucket: + return self.create_text_message(f"Bucket '{bucket}' does not exist") + except self.s3_client.exceptions.NoSuchKey: + return self.create_text_message(f"Object '{key}' does not exist in bucket '{bucket}'") + except Exception as e: + return self.create_text_message(f"Exception: {str(e)}") diff --git a/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml b/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml new file mode 100644 index 0000000000..642fc2966e --- /dev/null +++ b/api/core/tools/provider/builtin/aws/tools/s3_operator.yaml @@ -0,0 +1,98 @@ +identity: + name: s3_operator + author: AWS + label: + en_US: AWS S3 Operator + zh_Hans: AWS S3 读写器 + pt_BR: AWS S3 Operator + icon: icon.svg +description: + human: + en_US: AWS S3 Writer and Reader + zh_Hans: 读写S3 bucket中的文件 + pt_BR: AWS S3 Writer and Reader + llm: AWS S3 Writer and Reader +parameters: + - name: text_content + type: string + required: false + label: + en_US: The text to write + zh_Hans: 待写入的文本 + pt_BR: The text to write + human_description: + en_US: The text to write + zh_Hans: 待写入的文本 + pt_BR: The text to write + llm_description: The text to write + form: llm + - name: s3_uri + type: string + required: true + label: + en_US: s3 uri + zh_Hans: s3 uri + pt_BR: s3 uri + human_description: + en_US: s3 uri + zh_Hans: s3 uri + pt_BR: s3 uri + llm_description: s3 uri + form: llm + - name: aws_region + type: string + required: true + label: + en_US: region of bucket + zh_Hans: bucket 所在的region + pt_BR: region of bucket + human_description: + en_US: region of bucket + zh_Hans: bucket 所在的region + pt_BR: region of bucket + llm_description: region of bucket + form: form + - name: operation_type + type: select + required: true + label: + en_US: operation type + zh_Hans: 操作类型 + pt_BR: operation type + human_description: + en_US: operation type + zh_Hans: 操作类型 + pt_BR: operation type + default: read + options: + - value: read + label: + en_US: read + zh_Hans: 读 + - value: write + label: + en_US: write + zh_Hans: 写 + form: form + - name: generate_presign_url + type: boolean + required: false + label: + en_US: Generate presigned URL + zh_Hans: 生成预签名URL + human_description: + en_US: Whether to generate a presigned URL for the S3 object + zh_Hans: 是否生成S3对象的预签名URL + default: false + form: form + - name: presign_expiry + type: number + required: false + label: + en_US: Presigned URL expiration time + zh_Hans: 预签名URL有效期 + human_description: + en_US: Expiration time in seconds for the presigned URL + zh_Hans: 预签名URL的有效期(秒) + default: 3600 + form: form From 786cb6859b0fbd056104d1c67cf47d8fcf517b0f Mon Sep 17 00:00:00 2001 From: Kalo Chin <91766386+fdb02983rhy@users.noreply.github.com> Date: Sat, 21 Dec 2024 16:05:04 +0900 Subject: [PATCH 074/138] =?UTF-8?q?fix:=20ensure=20WorkflowRun=20attribute?= =?UTF-8?q?s=20are=20refreshed=20in=20WorkflowCycleMana=E2=80=A6=20(#11913?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/core/app/task_pipeline/workflow_cycle_manage.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index e2fa12b1cd..0e6425fa14 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -505,6 +505,12 @@ class WorkflowCycleManage: :param workflow_run: workflow run :return: """ + # Attach WorkflowRun to an active session so "created_by_role" can be accessed. + workflow_run = db.session.merge(workflow_run) + + # Refresh to ensure any expired attributes are fully loaded + db.session.refresh(workflow_run) + created_by = None if workflow_run.created_by_role == CreatedByRole.ACCOUNT.value: created_by_account = workflow_run.created_by_account From 8c559d6231506ba677ce3652cf574f484d356da2 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:19:46 +0800 Subject: [PATCH 075/138] fix(retrieval_service): avoid to use exception (#11925) Signed-off-by: -LAN- --- api/core/rag/datasource/retrieval_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/rag/datasource/retrieval_service.py b/api/core/rag/datasource/retrieval_service.py index b2141396d6..18f8d4e839 100644 --- a/api/core/rag/datasource/retrieval_service.py +++ b/api/core/rag/datasource/retrieval_service.py @@ -103,7 +103,7 @@ class RetrievalService: if exceptions: exception_message = ";\n".join(exceptions) - raise Exception(exception_message) + raise ValueError(exception_message) if retrieval_method == RetrievalMethod.HYBRID_SEARCH.value: data_post_processor = DataPostProcessor( From 8f73670925fc1bf4b45252a84f63f0a10ba54023 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:21:00 +0800 Subject: [PATCH 076/138] fix(file_factory): validate upload_file_id before querying UploadFile (#11937) Signed-off-by: -LAN- --- api/factories/file_factory.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/factories/file_factory.py b/api/factories/file_factory.py index 01d95dcfb3..13034f5cf5 100644 --- a/api/factories/file_factory.py +++ b/api/factories/file_factory.py @@ -116,8 +116,11 @@ def _build_from_local_file( tenant_id: str, transfer_method: FileTransferMethod, ) -> File: + upload_file_id = mapping.get("upload_file_id") + if not upload_file_id: + raise ValueError("Invalid upload file id") stmt = select(UploadFile).where( - UploadFile.id == mapping.get("upload_file_id"), + UploadFile.id == upload_file_id, UploadFile.tenant_id == tenant_id, ) From 606aadb891b607f359530ad33542093fc18488ec Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:21:09 +0800 Subject: [PATCH 077/138] refactor: update builtin tool provider methods to use session management (#11938) Signed-off-by: -LAN- --- .../console/workspace/tool_providers.py | 24 ++++++++------ .../tools/builtin_tools_manage_service.py | 33 +++++++++---------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 2cd6dcda3b..9e62a54699 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -3,12 +3,14 @@ import io from flask import send_file from flask_login import current_user from flask_restful import Resource, reqparse +from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden from configs import dify_config from controllers.console import api from controllers.console.wraps import account_initialization_required, enterprise_license_required, setup_required from core.model_runtime.utils.encoders import jsonable_encoder +from extensions.ext_database import db from libs.helper import alphanumeric, uuid_value from libs.login import login_required from services.tools.api_tools_manage_service import ApiToolManageService @@ -91,12 +93,16 @@ class ToolBuiltinProviderUpdateApi(Resource): args = parser.parse_args() - return BuiltinToolManageService.update_builtin_tool_provider( - user_id, - tenant_id, - provider, - args["credentials"], - ) + with Session(db.engine) as session: + result = BuiltinToolManageService.update_builtin_tool_provider( + session=session, + user_id=user_id, + tenant_id=tenant_id, + provider_name=provider, + credentials=args["credentials"], + ) + session.commit() + return result class ToolBuiltinProviderGetCredentialsApi(Resource): @@ -104,13 +110,11 @@ class ToolBuiltinProviderGetCredentialsApi(Resource): @login_required @account_initialization_required def get(self, provider): - user_id = current_user.id tenant_id = current_user.current_tenant_id return BuiltinToolManageService.get_builtin_tool_provider_credentials( - user_id, - tenant_id, - provider, + tenant_id=tenant_id, + provider_name=provider, ) diff --git a/api/services/tools/builtin_tools_manage_service.py b/api/services/tools/builtin_tools_manage_service.py index e2e49d017e..fada881fde 100644 --- a/api/services/tools/builtin_tools_manage_service.py +++ b/api/services/tools/builtin_tools_manage_service.py @@ -2,6 +2,9 @@ import json import logging from pathlib import Path +from sqlalchemy import select +from sqlalchemy.orm import Session + from configs import dify_config from core.helper.position_helper import is_filtered from core.model_runtime.utils.encoders import jsonable_encoder @@ -32,7 +35,7 @@ class BuiltinToolManageService: tenant_id=tenant_id, provider_controller=provider_controller ) # check if user has added the provider - builtin_provider: BuiltinToolProvider = ( + builtin_provider = ( db.session.query(BuiltinToolProvider) .filter( BuiltinToolProvider.tenant_id == tenant_id, @@ -71,19 +74,18 @@ class BuiltinToolManageService: return jsonable_encoder([v for _, v in (provider.credentials_schema or {}).items()]) @staticmethod - def update_builtin_tool_provider(user_id: str, tenant_id: str, provider_name: str, credentials: dict): + def update_builtin_tool_provider( + session: Session, user_id: str, tenant_id: str, provider_name: str, credentials: dict + ): """ update builtin tool provider """ # get if the provider exists - provider: BuiltinToolProvider = ( - db.session.query(BuiltinToolProvider) - .filter( - BuiltinToolProvider.tenant_id == tenant_id, - BuiltinToolProvider.provider == provider_name, - ) - .first() + stmt = select(BuiltinToolProvider).where( + BuiltinToolProvider.tenant_id == tenant_id, + BuiltinToolProvider.provider == provider_name, ) + provider = session.scalar(stmt) try: # get provider @@ -115,13 +117,10 @@ class BuiltinToolManageService: encrypted_credentials=json.dumps(credentials), ) - db.session.add(provider) - db.session.commit() + session.add(provider) else: provider.encrypted_credentials = json.dumps(credentials) - db.session.add(provider) - db.session.commit() # delete cache tool_configuration.delete_tool_credentials_cache() @@ -129,15 +128,15 @@ class BuiltinToolManageService: return {"result": "success"} @staticmethod - def get_builtin_tool_provider_credentials(user_id: str, tenant_id: str, provider: str): + def get_builtin_tool_provider_credentials(tenant_id: str, provider_name: str): """ get builtin tool provider credentials """ - provider: BuiltinToolProvider = ( + provider = ( db.session.query(BuiltinToolProvider) .filter( BuiltinToolProvider.tenant_id == tenant_id, - BuiltinToolProvider.provider == provider, + BuiltinToolProvider.provider == provider_name, ) .first() ) @@ -156,7 +155,7 @@ class BuiltinToolManageService: """ delete tool provider """ - provider: BuiltinToolProvider = ( + provider = ( db.session.query(BuiltinToolProvider) .filter( BuiltinToolProvider.tenant_id == tenant_id, From 810adb8a94ecb0e63ddb6416eaeb786b7c7f248e Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:21:30 +0800 Subject: [PATCH 078/138] fix: change OutputParserError to inherit from ValueError (#11935) Signed-off-by: -LAN- --- api/core/llm_generator/output_parser/errors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/llm_generator/output_parser/errors.py b/api/core/llm_generator/output_parser/errors.py index 1e743f1757..0922806ca8 100644 --- a/api/core/llm_generator/output_parser/errors.py +++ b/api/core/llm_generator/output_parser/errors.py @@ -1,2 +1,2 @@ -class OutputParserError(Exception): +class OutputParserError(ValueError): pass From c07d9e96ce6d3c0e0d135f000150f9d6b03d319d Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:21:57 +0800 Subject: [PATCH 079/138] fix(nodes): handle errors in question_classifier and parameter_extractor (#11927) Signed-off-by: -LAN- --- .../parameter_extractor/parameter_extractor_node.py | 9 +++++++++ .../question_classifier/question_classifier_node.py | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py index 5b960ea615..c8c854a43b 100644 --- a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py +++ b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py @@ -179,6 +179,15 @@ class ParameterExtractorNode(LLMNode): error=str(e), metadata={}, ) + except Exception as e: + return NodeRunResult( + status=WorkflowNodeExecutionStatus.FAILED, + inputs=inputs, + process_data=process_data, + outputs={"__is_success": 0, "__reason": "Failed to invoke model", "__error": str(e)}, + error=str(e), + metadata={}, + ) error = None diff --git a/api/core/workflow/nodes/question_classifier/question_classifier_node.py b/api/core/workflow/nodes/question_classifier/question_classifier_node.py index 15cfabe478..5043e25e2b 100644 --- a/api/core/workflow/nodes/question_classifier/question_classifier_node.py +++ b/api/core/workflow/nodes/question_classifier/question_classifier_node.py @@ -154,8 +154,7 @@ class QuestionClassifierNode(LLMNode): }, llm_usage=usage, ) - - except ValueError as e: + except Exception as e: return NodeRunResult( status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, From 90323cd35595c229d715e37600035a5204813751 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:22:06 +0800 Subject: [PATCH 080/138] fix(tool_file_manager): raise ValueError when get timeout (#11928) Signed-off-by: -LAN- --- api/core/tools/tool_file_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/core/tools/tool_file_manager.py b/api/core/tools/tool_file_manager.py index 5052f0897a..2aaca6d82e 100644 --- a/api/core/tools/tool_file_manager.py +++ b/api/core/tools/tool_file_manager.py @@ -8,9 +8,10 @@ from mimetypes import guess_extension, guess_type from typing import Optional, Union from uuid import uuid4 -from httpx import get +import httpx from configs import dify_config +from core.helper import ssrf_proxy from extensions.ext_database import db from extensions.ext_storage import storage from models.model import MessageFile @@ -94,12 +95,11 @@ class ToolFileManager: ) -> ToolFile: # try to download image try: - response = get(file_url) + response = ssrf_proxy.get(file_url) response.raise_for_status() blob = response.content - except Exception as e: - logger.exception(f"Failed to download file from {file_url}") - raise + except httpx.TimeoutException as e: + raise ValueError(f"timeout when downloading file from {file_url}") mimetype = guess_type(file_url)[0] or "octet/stream" extension = guess_extension(mimetype) or ".bin" From 455791b710c72585b41d9dfadac0deb04c751940 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:22:14 +0800 Subject: [PATCH 081/138] fix(model_runtime): make invoke as ValueError (#11929) Signed-off-by: -LAN- --- api/core/model_runtime/errors/invoke.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/model_runtime/errors/invoke.py b/api/core/model_runtime/errors/invoke.py index edfb19c7d0..7675425361 100644 --- a/api/core/model_runtime/errors/invoke.py +++ b/api/core/model_runtime/errors/invoke.py @@ -1,7 +1,7 @@ from typing import Optional -class InvokeError(Exception): +class InvokeError(ValueError): """Base class for all LLM exceptions.""" description: Optional[str] = None From b8d42cdea74306b79002c1d6a35e0ec76d173cc9 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:22:47 +0800 Subject: [PATCH 082/138] fix: change MaxRetriesExceededError to inherit from ValueError (#11934) Signed-off-by: -LAN- --- api/core/helper/ssrf_proxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py index d8aa805364..ce15a9667d 100644 --- a/api/core/helper/ssrf_proxy.py +++ b/api/core/helper/ssrf_proxy.py @@ -24,7 +24,7 @@ BACKOFF_FACTOR = 0.5 STATUS_FORCELIST = [429, 500, 502, 503, 504] -class MaxRetriesExceededError(Exception): +class MaxRetriesExceededError(ValueError): """Raised when the maximum number of retries is exceeded.""" pass From 0b06235527806e5651f01272bd5e16b214e9234b Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:22:57 +0800 Subject: [PATCH 083/138] =?UTF-8?q?fix:=20add=20RemoteFileUploadError=20fo?= =?UTF-8?q?r=20better=20error=20handling=20in=20remote=20fi=E2=80=A6=20(#1?= =?UTF-8?q?1933)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: -LAN- --- api/controllers/common/errors.py | 5 +++++ api/controllers/console/remote_files.py | 13 +++++++++---- api/controllers/web/remote_files.py | 13 +++++++++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/api/controllers/common/errors.py b/api/controllers/common/errors.py index c71f1ce5a3..9f762b3135 100644 --- a/api/controllers/common/errors.py +++ b/api/controllers/common/errors.py @@ -4,3 +4,8 @@ from werkzeug.exceptions import HTTPException class FilenameNotExistsError(HTTPException): code = 400 description = "The specified filename does not exist." + + +class RemoteFileUploadError(HTTPException): + code = 400 + description = "Error uploading remote file." diff --git a/api/controllers/console/remote_files.py b/api/controllers/console/remote_files.py index fac1341b39..b8cf019e4f 100644 --- a/api/controllers/console/remote_files.py +++ b/api/controllers/console/remote_files.py @@ -7,6 +7,7 @@ from flask_restful import Resource, marshal_with, reqparse import services from controllers.common import helpers +from controllers.common.errors import RemoteFileUploadError from core.file import helpers as file_helpers from core.helper import ssrf_proxy from fields.file_fields import file_fields_with_signed_url, remote_file_info_fields @@ -43,10 +44,14 @@ class RemoteFileUploadApi(Resource): url = args["url"] - resp = ssrf_proxy.head(url=url) - if resp.status_code != httpx.codes.OK: - resp = ssrf_proxy.get(url=url, timeout=3, follow_redirects=True) - resp.raise_for_status() + try: + resp = ssrf_proxy.head(url=url) + if resp.status_code != httpx.codes.OK: + resp = ssrf_proxy.get(url=url, timeout=3, follow_redirects=True) + if resp.status_code != httpx.codes.OK: + raise RemoteFileUploadError(f"Failed to fetch file from {url}: {resp.text}") + except httpx.RequestError as e: + raise RemoteFileUploadError(f"Failed to fetch file from {url}: {str(e)}") file_info = helpers.guess_file_info_from_response(resp) diff --git a/api/controllers/web/remote_files.py b/api/controllers/web/remote_files.py index d6b8eb2855..ae68df6bdc 100644 --- a/api/controllers/web/remote_files.py +++ b/api/controllers/web/remote_files.py @@ -5,6 +5,7 @@ from flask_restful import marshal_with, reqparse import services from controllers.common import helpers +from controllers.common.errors import RemoteFileUploadError from controllers.web.wraps import WebApiResource from core.file import helpers as file_helpers from core.helper import ssrf_proxy @@ -38,10 +39,14 @@ class RemoteFileUploadApi(WebApiResource): url = args["url"] - resp = ssrf_proxy.head(url=url) - if resp.status_code != httpx.codes.OK: - resp = ssrf_proxy.get(url=url, timeout=3) - resp.raise_for_status() + try: + resp = ssrf_proxy.head(url=url) + if resp.status_code != httpx.codes.OK: + resp = ssrf_proxy.get(url=url, timeout=3, follow_redirects=True) + if resp.status_code != httpx.codes.OK: + raise RemoteFileUploadError(f"Failed to fetch file from {url}: {resp.text}") + except httpx.RequestError as e: + raise RemoteFileUploadError(f"Failed to fetch file from {url}: {str(e)}") file_info = helpers.guess_file_info_from_response(resp) From 5e37ab60d8c6f9aded0a72e0d4180bdac9c3795b Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:23:03 +0800 Subject: [PATCH 084/138] fix: validate response type in transform_response method (#11931) Signed-off-by: -LAN- --- api/core/helper/code_executor/template_transformer.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/core/helper/code_executor/template_transformer.py b/api/core/helper/code_executor/template_transformer.py index b7a07b21e1..cf422fd023 100644 --- a/api/core/helper/code_executor/template_transformer.py +++ b/api/core/helper/code_executor/template_transformer.py @@ -33,13 +33,16 @@ class TemplateTransformer(ABC): return result @classmethod - def transform_response(cls, response: str) -> dict: + def transform_response(cls, response: str): """ Transform response to dict :param response: response :return: """ - return json.loads(cls.extract_result_str_from_response(response)) + result = json.loads(cls.extract_result_str_from_response(response)) + if not isinstance(result, dict): + raise ValueError("Result must be a dict") + return result @classmethod @abstractmethod From 599d410d9999785c70f3f2353700c9057a334611 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:23:12 +0800 Subject: [PATCH 085/138] fix: validate reranking model attributes before processing (#11930) Signed-off-by: -LAN- --- api/core/rag/data_post_processor/data_post_processor.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/core/rag/data_post_processor/data_post_processor.py b/api/core/rag/data_post_processor/data_post_processor.py index 992415657e..d17d76333e 100644 --- a/api/core/rag/data_post_processor/data_post_processor.py +++ b/api/core/rag/data_post_processor/data_post_processor.py @@ -83,11 +83,15 @@ class DataPostProcessor: if reranking_model: try: model_manager = ModelManager() + reranking_provider_name = reranking_model.get("reranking_provider_name") + reranking_model_name = reranking_model.get("reranking_model_name") + if not reranking_provider_name or not reranking_model_name: + return None rerank_model_instance = model_manager.get_model_instance( tenant_id=tenant_id, - provider=reranking_model["reranking_provider_name"], + provider=reranking_provider_name, model_type=ModelType.RERANK, - model=reranking_model["reranking_model_name"], + model=reranking_model_name, ) return rerank_model_instance except InvokeAuthorizationError: From a227af3664e3108a3c2956d7d363b93c7d08bc9a Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sat, 21 Dec 2024 21:24:22 +0800 Subject: [PATCH 086/138] =?UTF-8?q?fix(code=5Fnode):=20update=20type=20hin?= =?UTF-8?q?ts=20for=20string=20and=20number=20checks=20in=20Cod=E2=80=A6?= =?UTF-8?q?=20(#11936)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: -LAN- --- api/core/workflow/nodes/code/code_node.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index 19b9078a5c..6adf82c455 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -1,5 +1,5 @@ from collections.abc import Mapping, Sequence -from typing import Any, Optional, Union +from typing import Any, Optional from configs import dify_config from core.helper.code_executor.code_executor import CodeExecutionError, CodeExecutor, CodeLanguage @@ -67,18 +67,17 @@ class CodeNode(BaseNode[CodeNodeData]): return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=variables, outputs=result) - def _check_string(self, value: str, variable: str) -> str: + def _check_string(self, value: str | None, variable: str) -> str | None: """ Check string :param value: value :param variable: variable :return: """ + if value is None: + return None if not isinstance(value, str): - if value is None: - return None - else: - raise OutputValidationError(f"Output variable `{variable}` must be a string") + raise OutputValidationError(f"Output variable `{variable}` must be a string") if len(value) > dify_config.CODE_MAX_STRING_LENGTH: raise OutputValidationError( @@ -88,18 +87,17 @@ class CodeNode(BaseNode[CodeNodeData]): return value.replace("\x00", "") - def _check_number(self, value: Union[int, float], variable: str) -> Union[int, float]: + def _check_number(self, value: int | float | None, variable: str) -> int | float | None: """ Check number :param value: value :param variable: variable :return: """ + if value is None: + return None if not isinstance(value, int | float): - if value is None: - return None - else: - raise OutputValidationError(f"Output variable `{variable}` must be a number") + raise OutputValidationError(f"Output variable `{variable}` must be a number") if value > dify_config.CODE_MAX_NUMBER or value < dify_config.CODE_MIN_NUMBER: raise OutputValidationError( From e22cc2811462de33757e688c3e93ec042e897909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=86=E8=90=8C=E9=97=B7=E6=B2=B9=E7=93=B6?= <253605712@qq.com> Date: Sat, 21 Dec 2024 21:24:33 +0800 Subject: [PATCH 087/138] fix:log error(#11942) (#11943) --- api/models/model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/models/model.py b/api/models/model.py index 8608b12af1..d3c69cfbe1 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -700,8 +700,10 @@ class Conversation(db.Model): def status_count(self): messages = db.session.query(Message).filter(Message.conversation_id == self.id).all() status_counts = { + WorkflowRunStatus.RUNNING: 0, WorkflowRunStatus.SUCCEEDED: 0, WorkflowRunStatus.FAILED: 0, + WorkflowRunStatus.STOPPED: 0, WorkflowRunStatus.PARTIAL_SUCCESSED: 0, } From 9ee9e9c6de52aa01a8840bfc7f7a2feed6af6312 Mon Sep 17 00:00:00 2001 From: yihong Date: Sat, 21 Dec 2024 21:24:59 +0800 Subject: [PATCH 088/138] fix: self.method should method in api_tool.py (#11926) Signed-off-by: yihong0618 --- api/core/tools/tool/api_tool.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/tools/tool/api_tool.py b/api/core/tools/tool/api_tool.py index 636debffd4..48aac75dbb 100644 --- a/api/core/tools/tool/api_tool.py +++ b/api/core/tools/tool/api_tool.py @@ -210,7 +210,7 @@ class ApiTool(Tool): ) return response else: - raise ValueError(f"Invalid http method {self.method}") + raise ValueError(f"Invalid http method {method}") def _convert_body_property_any_of( self, property: dict[str, Any], value: Any, any_of: list[dict[str, Any]], max_recursive=10 From 9578246bbb2029251880fd4745e26360aa14813c Mon Sep 17 00:00:00 2001 From: jiangbo721 <365065261@qq.com> Date: Sat, 21 Dec 2024 23:13:58 +0800 Subject: [PATCH 089/138] fix: The default updated_at when a workflow is created (#11709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 刘江波 --- api/models/account.py | 21 ++++----- api/models/api_based_extension.py | 4 +- api/models/dataset.py | 32 +++++++------- api/models/model.py | 72 +++++++++++++++---------------- api/models/provider.py | 30 +++++++------ api/models/source.py | 9 ++-- api/models/tools.py | 26 +++++------ api/models/web.py | 6 ++- api/models/workflow.py | 16 +++---- 9 files changed, 110 insertions(+), 106 deletions(-) diff --git a/api/models/account.py b/api/models/account.py index ce17b90def..932ba1da57 100644 --- a/api/models/account.py +++ b/api/models/account.py @@ -2,6 +2,7 @@ import enum import json from flask_login import UserMixin +from sqlalchemy import func from .engine import db from .types import StringUUID @@ -30,11 +31,11 @@ class Account(UserMixin, db.Model): timezone = db.Column(db.String(255)) last_login_at = db.Column(db.DateTime) last_login_ip = db.Column(db.String(255)) - last_active_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + last_active_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) status = db.Column(db.String(16), nullable=False, server_default=db.text("'active'::character varying")) initialized_at = db.Column(db.DateTime) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def is_password_set(self): @@ -187,8 +188,8 @@ class Tenant(db.Model): plan = db.Column(db.String(255), nullable=False, server_default=db.text("'basic'::character varying")) status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying")) custom_config = db.Column(db.Text) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) def get_accounts(self) -> list[Account]: return ( @@ -228,8 +229,8 @@ class TenantAccountJoin(db.Model): current = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) role = db.Column(db.String(16), nullable=False, server_default="normal") invited_by = db.Column(StringUUID, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class AccountIntegrate(db.Model): @@ -245,8 +246,8 @@ class AccountIntegrate(db.Model): provider = db.Column(db.String(16), nullable=False) open_id = db.Column(db.String(255), nullable=False) encrypted_token = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class InvitationCode(db.Model): @@ -265,4 +266,4 @@ class InvitationCode(db.Model): used_by_tenant_id = db.Column(StringUUID) used_by_account_id = db.Column(StringUUID) deprecated_at = db.Column(db.DateTime) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/models/api_based_extension.py b/api/models/api_based_extension.py index 4d4182cabd..fbffe7a3b2 100644 --- a/api/models/api_based_extension.py +++ b/api/models/api_based_extension.py @@ -1,5 +1,7 @@ import enum +from sqlalchemy import func + from .engine import db from .types import StringUUID @@ -23,4 +25,4 @@ class APIBasedExtension(db.Model): name = db.Column(db.String(255), nullable=False) api_endpoint = db.Column(db.String(255), nullable=False) api_key = db.Column(db.Text, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/models/dataset.py b/api/models/dataset.py index 97e4d6c0ef..7279e8d5b3 100644 --- a/api/models/dataset.py +++ b/api/models/dataset.py @@ -50,9 +50,9 @@ class Dataset(db.Model): indexing_technique = db.Column(db.String(255), nullable=True) index_struct = db.Column(db.Text, nullable=True) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) embedding_model = db.Column(db.String(255), nullable=True) embedding_model_provider = db.Column(db.String(255), nullable=True) collection_binding_id = db.Column(StringUUID, nullable=True) @@ -212,7 +212,7 @@ class DatasetProcessRule(db.Model): mode = db.Column(db.String(255), nullable=False, server_default=db.text("'automatic'::character varying")) rules = db.Column(db.Text, nullable=True) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) MODES = ["automatic", "custom"] PRE_PROCESSING_RULES = ["remove_stopwords", "remove_extra_spaces", "remove_urls_emails"] @@ -264,7 +264,7 @@ class Document(db.Model): created_from = db.Column(db.String(255), nullable=False) created_by = db.Column(StringUUID, nullable=False) created_api_request_id = db.Column(StringUUID, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) # start processing processing_started_at = db.Column(db.DateTime, nullable=True) @@ -303,7 +303,7 @@ class Document(db.Model): archived_reason = db.Column(db.String(255), nullable=True) archived_by = db.Column(StringUUID, nullable=True) archived_at = db.Column(db.DateTime, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) doc_type = db.Column(db.String(40), nullable=True) doc_metadata = db.Column(db.JSON, nullable=True) doc_form = db.Column(db.String(255), nullable=False, server_default=db.text("'text_model'::character varying")) @@ -527,9 +527,9 @@ class DocumentSegment(db.Model): disabled_by = db.Column(StringUUID, nullable=True) status = db.Column(db.String(255), nullable=False, server_default=db.text("'waiting'::character varying")) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) indexing_at = db.Column(db.DateTime, nullable=True) completed_at = db.Column(db.DateTime, nullable=True) error = db.Column(db.Text, nullable=True) @@ -697,7 +697,7 @@ class Embedding(db.Model): ) hash = db.Column(db.String(64), nullable=False) embedding = db.Column(db.LargeBinary, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) provider_name = db.Column(db.String(255), nullable=False, server_default=db.text("''::character varying")) def set_embedding(self, embedding_data: list[float]): @@ -719,7 +719,7 @@ class DatasetCollectionBinding(db.Model): model_name = db.Column(db.String(255), nullable=False) type = db.Column(db.String(40), server_default=db.text("'dataset'::character varying"), nullable=False) collection_name = db.Column(db.String(64), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class TidbAuthBinding(db.Model): @@ -739,7 +739,7 @@ class TidbAuthBinding(db.Model): status = db.Column(db.String(255), nullable=False, server_default=db.text("CREATING")) account = db.Column(db.String(255), nullable=False) password = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class Whitelist(db.Model): @@ -751,7 +751,7 @@ class Whitelist(db.Model): id = db.Column(StringUUID, primary_key=True, server_default=db.text("uuid_generate_v4()")) tenant_id = db.Column(StringUUID, nullable=True) category = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class DatasetPermission(db.Model): @@ -768,7 +768,7 @@ class DatasetPermission(db.Model): account_id = db.Column(StringUUID, nullable=False) tenant_id = db.Column(StringUUID, nullable=False) has_permission = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class ExternalKnowledgeApis(db.Model): @@ -785,9 +785,9 @@ class ExternalKnowledgeApis(db.Model): tenant_id = db.Column(StringUUID, nullable=False) settings = db.Column(db.Text, nullable=True) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) def to_dict(self): return { @@ -840,6 +840,6 @@ class ExternalKnowledgeBindings(db.Model): dataset_id = db.Column(StringUUID, nullable=False) external_knowledge_id = db.Column(db.Text, nullable=False) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/models/model.py b/api/models/model.py index d3c69cfbe1..04bb0a947a 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -30,7 +30,7 @@ class DifySetup(db.Model): __table_args__ = (db.PrimaryKeyConstraint("version", name="dify_setup_pkey"),) version = db.Column(db.String(255), nullable=False) - setup_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + setup_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class AppMode(StrEnum): @@ -85,9 +85,9 @@ class App(db.Model): tracing = db.Column(db.Text, nullable=True) max_active_requests = db.Column(db.Integer, nullable=True) created_by = db.Column(StringUUID, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) use_icon_as_answer_icon = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) @property @@ -226,9 +226,9 @@ class AppModelConfig(db.Model): model_id = db.Column(db.String(255), nullable=True) configs = db.Column(db.JSON, nullable=True) created_by = db.Column(StringUUID, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) opening_statement = db.Column(db.Text) suggested_questions = db.Column(db.Text) suggested_questions_after_answer = db.Column(db.Text) @@ -482,8 +482,8 @@ class RecommendedApp(db.Model): is_listed = db.Column(db.Boolean, nullable=False, default=True) install_count = db.Column(db.Integer, nullable=False, default=0) language = db.Column(db.String(255), nullable=False, server_default=db.text("'en-US'::character varying")) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def app(self): @@ -507,7 +507,7 @@ class InstalledApp(db.Model): position = db.Column(db.Integer, nullable=False, default=0) is_pinned = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) last_used_at = db.Column(db.DateTime, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def app(self): @@ -548,8 +548,8 @@ class Conversation(db.Model): read_at = db.Column(db.DateTime) read_account_id = db.Column(StringUUID) dialogue_count: Mapped[int] = mapped_column(default=0) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) messages = db.relationship("Message", backref="conversation", lazy="select", passive_deletes="all") message_annotations = db.relationship( @@ -791,8 +791,8 @@ class Message(db.Model): from_source = db.Column(db.String(255), nullable=False) from_end_user_id: Mapped[Optional[str]] = db.Column(StringUUID) from_account_id: Mapped[Optional[str]] = db.Column(StringUUID) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) agent_based = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) workflow_run_id = db.Column(StringUUID) @@ -1117,8 +1117,8 @@ class MessageFeedback(db.Model): from_source = db.Column(db.String(255), nullable=False) from_end_user_id = db.Column(StringUUID) from_account_id = db.Column(StringUUID) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def from_account(self): @@ -1164,9 +1164,7 @@ class MessageFile(db.Model): upload_file_id: Mapped[Optional[str]] = db.Column(StringUUID, nullable=True) created_by_role: Mapped[str] = db.Column(db.String(255), nullable=False) created_by: Mapped[str] = db.Column(StringUUID, nullable=False) - created_at: Mapped[datetime] = db.Column( - db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)") - ) + created_at: Mapped[datetime] = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class MessageAnnotation(db.Model): @@ -1186,8 +1184,8 @@ class MessageAnnotation(db.Model): content = db.Column(db.Text, nullable=False) hit_count = db.Column(db.Integer, nullable=False, server_default=db.text("0")) account_id = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def account(self): @@ -1216,7 +1214,7 @@ class AppAnnotationHitHistory(db.Model): source = db.Column(db.Text, nullable=False) question = db.Column(db.Text, nullable=False) account_id = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) score = db.Column(Float, nullable=False, server_default=db.text("0")) message_id = db.Column(StringUUID, nullable=False) annotation_question = db.Column(db.Text, nullable=False) @@ -1250,9 +1248,9 @@ class AppAnnotationSetting(db.Model): score_threshold = db.Column(Float, nullable=False, server_default=db.text("0")) collection_binding_id = db.Column(StringUUID, nullable=False) created_user_id = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_user_id = db.Column(StringUUID, nullable=False) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def created_account(self): @@ -1298,9 +1296,9 @@ class OperationLog(db.Model): account_id = db.Column(StringUUID, nullable=False) action = db.Column(db.String(255), nullable=False) content = db.Column(db.JSON) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) created_ip = db.Column(db.String(255), nullable=False) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class EndUser(UserMixin, db.Model): @@ -1319,8 +1317,8 @@ class EndUser(UserMixin, db.Model): name = db.Column(db.String(255)) is_anonymous = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) session_id = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class Site(db.Model): @@ -1351,9 +1349,9 @@ class Site(db.Model): prompt_public = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) status = db.Column(db.String(255), nullable=False, server_default=db.text("'normal'::character varying")) created_by = db.Column(StringUUID, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) code = db.Column(db.String(255)) @property @@ -1395,7 +1393,7 @@ class ApiToken(db.Model): type = db.Column(db.String(16), nullable=False) token = db.Column(db.String(255), nullable=False) last_used_at = db.Column(db.DateTime, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @staticmethod def generate_api_key(prefix, n): @@ -1426,9 +1424,7 @@ class UploadFile(db.Model): db.String(255), nullable=False, server_default=db.text("'account'::character varying") ) created_by: Mapped[str] = db.Column(StringUUID, nullable=False) - created_at: Mapped[datetime] = db.Column( - db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)") - ) + created_at: Mapped[datetime] = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) used: Mapped[bool] = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) used_by: Mapped[str | None] = db.Column(StringUUID, nullable=True) used_at: Mapped[datetime | None] = db.Column(db.DateTime, nullable=True) @@ -1485,7 +1481,7 @@ class ApiRequest(db.Model): request = db.Column(db.Text, nullable=True) response = db.Column(db.Text, nullable=True) ip = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class MessageChain(db.Model): @@ -1657,7 +1653,7 @@ class Tag(db.Model): type = db.Column(db.String(16), nullable=False) name = db.Column(db.String(255), nullable=False) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class TagBinding(db.Model): @@ -1673,7 +1669,7 @@ class TagBinding(db.Model): tag_id = db.Column(StringUUID, nullable=True) target_id = db.Column(StringUUID, nullable=True) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class TraceAppConfig(db.Model): @@ -1687,8 +1683,10 @@ class TraceAppConfig(db.Model): app_id = db.Column(StringUUID, nullable=False) tracing_provider = db.Column(db.String(255), nullable=True) tracing_config = db.Column(db.JSON, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=func.now()) - updated_at = db.Column(db.DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column( + db.DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp() + ) is_active = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) @property diff --git a/api/models/provider.py b/api/models/provider.py index 65f70b76e9..fdd3e802d7 100644 --- a/api/models/provider.py +++ b/api/models/provider.py @@ -1,5 +1,7 @@ from enum import Enum +from sqlalchemy import func + from .engine import db from .types import StringUUID @@ -60,8 +62,8 @@ class Provider(db.Model): quota_limit = db.Column(db.BigInteger, nullable=True) quota_used = db.Column(db.BigInteger, default=0) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) def __repr__(self): return ( @@ -108,8 +110,8 @@ class ProviderModel(db.Model): model_type = db.Column(db.String(40), nullable=False) encrypted_config = db.Column(db.Text, nullable=True) is_valid = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class TenantDefaultModel(db.Model): @@ -124,8 +126,8 @@ class TenantDefaultModel(db.Model): provider_name = db.Column(db.String(255), nullable=False) model_name = db.Column(db.String(255), nullable=False) model_type = db.Column(db.String(40), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class TenantPreferredModelProvider(db.Model): @@ -139,8 +141,8 @@ class TenantPreferredModelProvider(db.Model): tenant_id = db.Column(StringUUID, nullable=False) provider_name = db.Column(db.String(255), nullable=False) preferred_provider_type = db.Column(db.String(40), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class ProviderOrder(db.Model): @@ -164,8 +166,8 @@ class ProviderOrder(db.Model): paid_at = db.Column(db.DateTime) pay_failed_at = db.Column(db.DateTime) refunded_at = db.Column(db.DateTime) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class ProviderModelSetting(db.Model): @@ -186,8 +188,8 @@ class ProviderModelSetting(db.Model): model_type = db.Column(db.String(40), nullable=False) enabled = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) load_balancing_enabled = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class LoadBalancingModelConfig(db.Model): @@ -209,5 +211,5 @@ class LoadBalancingModelConfig(db.Model): name = db.Column(db.String(255), nullable=False) encrypted_config = db.Column(db.Text, nullable=True) enabled = db.Column(db.Boolean, nullable=False, server_default=db.text("true")) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/models/source.py b/api/models/source.py index 4d98572ef8..114db8e110 100644 --- a/api/models/source.py +++ b/api/models/source.py @@ -1,5 +1,6 @@ import json +from sqlalchemy import func from sqlalchemy.dialects.postgresql import JSONB from .engine import db @@ -19,8 +20,8 @@ class DataSourceOauthBinding(db.Model): access_token = db.Column(db.String(255), nullable=False) provider = db.Column(db.String(255), nullable=False) source_info = db.Column(JSONB, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) disabled = db.Column(db.Boolean, nullable=True, server_default=db.text("false")) @@ -37,8 +38,8 @@ class DataSourceApiKeyAuthBinding(db.Model): category = db.Column(db.String(255), nullable=False) provider = db.Column(db.String(255), nullable=False) credentials = db.Column(db.Text, nullable=True) # JSON - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) disabled = db.Column(db.Boolean, nullable=True, server_default=db.text("false")) def to_dict(self): diff --git a/api/models/tools.py b/api/models/tools.py index c390be4625..e90ab669c6 100644 --- a/api/models/tools.py +++ b/api/models/tools.py @@ -2,7 +2,7 @@ import json from typing import Optional import sqlalchemy as sa -from sqlalchemy import ForeignKey +from sqlalchemy import ForeignKey, func from sqlalchemy.orm import Mapped, mapped_column from core.tools.entities.common_entities import I18nObject @@ -36,8 +36,8 @@ class BuiltinToolProvider(db.Model): provider = db.Column(db.String(40), nullable=False) # credential of the tool provider encrypted_credentials = db.Column(db.Text, nullable=True) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def credentials(self) -> dict: @@ -74,8 +74,8 @@ class PublishedAppTool(db.Model): tool_name = db.Column(db.String(40), nullable=False) # author author = db.Column(db.String(40), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def description_i18n(self) -> I18nObject: @@ -120,8 +120,8 @@ class ApiToolProvider(db.Model): # custom_disclaimer custom_disclaimer: Mapped[str] = mapped_column(sa.TEXT, default="") - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def schema_type(self) -> ApiProviderSchemaType: @@ -198,8 +198,8 @@ class WorkflowToolProvider(db.Model): # privacy policy privacy_policy = db.Column(db.String(255), nullable=True, server_default="") - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def user(self) -> Account | None: @@ -251,8 +251,8 @@ class ToolModelInvoke(db.Model): provider_response_latency = db.Column(db.Float, nullable=False, server_default=db.text("0")) total_price = db.Column(db.Numeric(10, 7)) currency = db.Column(db.String(255), nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) class ToolConversationVariables(db.Model): @@ -278,8 +278,8 @@ class ToolConversationVariables(db.Model): # variables pool variables_str = db.Column(db.Text, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) - updated_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) + updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def variables(self) -> dict: diff --git a/api/models/web.py b/api/models/web.py index a0f87cf456..f5d54630e1 100644 --- a/api/models/web.py +++ b/api/models/web.py @@ -1,3 +1,5 @@ +from sqlalchemy import func + from .engine import db from .model import Message from .types import StringUUID @@ -15,7 +17,7 @@ class SavedMessage(db.Model): message_id = db.Column(StringUUID, nullable=False) created_by_role = db.Column(db.String(255), nullable=False, server_default=db.text("'end_user'::character varying")) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def message(self): @@ -34,4 +36,4 @@ class PinnedConversation(db.Model): conversation_id = db.Column(StringUUID, nullable=False) created_by_role = db.Column(db.String(255), nullable=False, server_default=db.text("'end_user'::character varying")) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/models/workflow.py b/api/models/workflow.py index e933382a84..0e5cf38d25 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -1,6 +1,6 @@ import json from collections.abc import Mapping, Sequence -from datetime import UTC, datetime +from datetime import datetime from enum import Enum, StrEnum from typing import Any, Optional, Union @@ -103,12 +103,10 @@ class Workflow(db.Model): graph: Mapped[str] = mapped_column(sa.Text) _features: Mapped[str] = mapped_column("features", sa.TEXT) created_by: Mapped[str] = mapped_column(StringUUID, nullable=False) - created_at: Mapped[datetime] = mapped_column( - db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)") - ) + created_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by: Mapped[Optional[str]] = mapped_column(StringUUID) updated_at: Mapped[datetime] = mapped_column( - sa.DateTime, nullable=False, default=datetime.now(tz=UTC), server_onupdate=func.current_timestamp() + db.DateTime, nullable=False, server_default=func.current_timestamp(), server_onupdate=func.current_timestamp() ) _environment_variables: Mapped[str] = mapped_column( "environment_variables", db.Text, nullable=False, server_default="{}" @@ -406,7 +404,7 @@ class WorkflowRun(db.Model): total_steps = db.Column(db.Integer, server_default=db.text("0")) created_by_role = db.Column(db.String(255), nullable=False) # account, end_user created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) finished_at = db.Column(db.DateTime) exceptions_count = db.Column(db.Integer, server_default=db.text("0")) @@ -636,7 +634,7 @@ class WorkflowNodeExecution(db.Model): error = db.Column(db.Text) elapsed_time = db.Column(db.Float, nullable=False, server_default=db.text("0")) execution_metadata = db.Column(db.Text) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) created_by_role = db.Column(db.String(255), nullable=False) created_by = db.Column(StringUUID, nullable=False) finished_at = db.Column(db.DateTime) @@ -755,7 +753,7 @@ class WorkflowAppLog(db.Model): created_from = db.Column(db.String(255), nullable=False) created_by_role = db.Column(db.String(255), nullable=False) created_by = db.Column(StringUUID, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) @property def workflow_run(self): @@ -781,7 +779,7 @@ class ConversationVariable(db.Model): conversation_id: Mapped[str] = db.Column(StringUUID, nullable=False, primary_key=True) app_id: Mapped[str] = db.Column(StringUUID, nullable=False, index=True) data = db.Column(db.Text, nullable=False) - created_at = db.Column(db.DateTime, nullable=False, index=True, server_default=db.text("CURRENT_TIMESTAMP(0)")) + created_at = db.Column(db.DateTime, nullable=False, index=True, server_default=func.current_timestamp()) updated_at = db.Column( db.DateTime, nullable=False, server_default=func.current_timestamp(), onupdate=func.current_timestamp() ) From 366857cd26e563d66c3802524f690e80b4a7fc81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Sat, 21 Dec 2024 23:14:05 +0800 Subject: [PATCH 090/138] fix: gemini system prompt with variable raise error (#11946) --- .../model_providers/google/llm/llm.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/api/core/model_runtime/model_providers/google/llm/llm.py b/api/core/model_runtime/model_providers/google/llm/llm.py index b54668a12d..7d19ccbb74 100644 --- a/api/core/model_runtime/model_providers/google/llm/llm.py +++ b/api/core/model_runtime/model_providers/google/llm/llm.py @@ -21,6 +21,7 @@ from core.model_runtime.entities.message_entities import ( PromptMessageContentType, PromptMessageTool, SystemPromptMessage, + TextPromptMessageContent, ToolPromptMessage, UserPromptMessage, ) @@ -143,7 +144,7 @@ class GoogleLargeLanguageModel(LargeLanguageModel): """ try: - ping_message = SystemPromptMessage(content="ping") + ping_message = UserPromptMessage(content="ping") self._generate(model, credentials, [ping_message], {"max_output_tokens": 5}) except Exception as ex: @@ -187,17 +188,23 @@ class GoogleLargeLanguageModel(LargeLanguageModel): config_kwargs["stop_sequences"] = stop genai.configure(api_key=credentials["google_api_key"]) - google_model = genai.GenerativeModel(model_name=model) history = [] + system_instruction = None for msg in prompt_messages: # makes message roles strictly alternating content = self._format_message_to_glm_content(msg) if history and history[-1]["role"] == content["role"]: history[-1]["parts"].extend(content["parts"]) + elif content["role"] == "system": + system_instruction = content["parts"][0] else: history.append(content) + if not history: + raise InvokeError("The user prompt message is required. You only add a system prompt message.") + + google_model = genai.GenerativeModel(model_name=model, system_instruction=system_instruction) response = google_model.generate_content( contents=history, generation_config=genai.types.GenerationConfig(**config_kwargs), @@ -404,7 +411,10 @@ class GoogleLargeLanguageModel(LargeLanguageModel): ) return glm_content elif isinstance(message, SystemPromptMessage): - return {"role": "user", "parts": [to_part(message.content)]} + if isinstance(message.content, list): + text_contents = filter(lambda c: isinstance(c, TextPromptMessageContent), message.content) + message.content = "".join(c.data for c in text_contents) + return {"role": "system", "parts": [to_part(message.content)]} elif isinstance(message, ToolPromptMessage): return { "role": "function", From 3d07a94bd74cc3eddf4d2e530e575c422d5a1675 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:39:29 +0800 Subject: [PATCH 091/138] =?UTF-8?q?fix:=20refactor=20conversation=20pagina?= =?UTF-8?q?tion=20to=20use=20SQLAlchemy=20session=20manag=E2=80=A6=20(#119?= =?UTF-8?q?56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: -LAN- --- .../console/explore/conversation.py | 20 +++++--- .../service_api/app/conversation.py | 20 +++++--- api/controllers/web/conversation.py | 22 ++++---- api/models/web.py | 3 +- api/services/conversation_service.py | 50 ++++++++++--------- api/services/web_conversation_service.py | 18 ++++--- 6 files changed, 78 insertions(+), 55 deletions(-) diff --git a/api/controllers/console/explore/conversation.py b/api/controllers/console/explore/conversation.py index 6f9d7769b9..5e7a3da017 100644 --- a/api/controllers/console/explore/conversation.py +++ b/api/controllers/console/explore/conversation.py @@ -1,12 +1,14 @@ from flask_login import current_user from flask_restful import marshal_with, reqparse from flask_restful.inputs import int_range +from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound from controllers.console import api from controllers.console.explore.error import NotChatAppError from controllers.console.explore.wraps import InstalledAppResource from core.app.entities.app_invoke_entities import InvokeFrom +from extensions.ext_database import db from fields.conversation_fields import conversation_infinite_scroll_pagination_fields, simple_conversation_fields from libs.helper import uuid_value from models.model import AppMode @@ -34,14 +36,16 @@ class ConversationListApi(InstalledAppResource): pinned = True if args["pinned"] == "true" else False try: - return WebConversationService.pagination_by_last_id( - app_model=app_model, - user=current_user, - last_id=args["last_id"], - limit=args["limit"], - invoke_from=InvokeFrom.EXPLORE, - pinned=pinned, - ) + with Session(db.engine) as session: + return WebConversationService.pagination_by_last_id( + session=session, + app_model=app_model, + user=current_user, + last_id=args["last_id"], + limit=args["limit"], + invoke_from=InvokeFrom.EXPLORE, + pinned=pinned, + ) except LastConversationNotExistsError: raise NotFound("Last Conversation Not Exists.") diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index c62fd77d36..32940cbc29 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -1,5 +1,6 @@ from flask_restful import Resource, marshal_with, reqparse from flask_restful.inputs import int_range +from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound import services @@ -7,6 +8,7 @@ from controllers.service_api import api from controllers.service_api.app.error import NotChatAppError from controllers.service_api.wraps import FetchUserArg, WhereisUserArg, validate_app_token from core.app.entities.app_invoke_entities import InvokeFrom +from extensions.ext_database import db from fields.conversation_fields import ( conversation_delete_fields, conversation_infinite_scroll_pagination_fields, @@ -39,14 +41,16 @@ class ConversationApi(Resource): args = parser.parse_args() try: - return ConversationService.pagination_by_last_id( - app_model=app_model, - user=end_user, - last_id=args["last_id"], - limit=args["limit"], - invoke_from=InvokeFrom.SERVICE_API, - sort_by=args["sort_by"], - ) + with Session(db.engine) as session: + return ConversationService.pagination_by_last_id( + session=session, + app_model=app_model, + user=end_user, + last_id=args["last_id"], + limit=args["limit"], + invoke_from=InvokeFrom.SERVICE_API, + sort_by=args["sort_by"], + ) except services.errors.conversation.LastConversationNotExistsError: raise NotFound("Last Conversation Not Exists.") diff --git a/api/controllers/web/conversation.py b/api/controllers/web/conversation.py index c3b0cd4f44..fe0d7c74f3 100644 --- a/api/controllers/web/conversation.py +++ b/api/controllers/web/conversation.py @@ -1,11 +1,13 @@ from flask_restful import marshal_with, reqparse from flask_restful.inputs import int_range +from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound from controllers.web import api from controllers.web.error import NotChatAppError from controllers.web.wraps import WebApiResource from core.app.entities.app_invoke_entities import InvokeFrom +from extensions.ext_database import db from fields.conversation_fields import conversation_infinite_scroll_pagination_fields, simple_conversation_fields from libs.helper import uuid_value from models.model import AppMode @@ -40,15 +42,17 @@ class ConversationListApi(WebApiResource): pinned = True if args["pinned"] == "true" else False try: - return WebConversationService.pagination_by_last_id( - app_model=app_model, - user=end_user, - last_id=args["last_id"], - limit=args["limit"], - invoke_from=InvokeFrom.WEB_APP, - pinned=pinned, - sort_by=args["sort_by"], - ) + with Session(db.engine) as session: + return WebConversationService.pagination_by_last_id( + session=session, + app_model=app_model, + user=end_user, + last_id=args["last_id"], + limit=args["limit"], + invoke_from=InvokeFrom.WEB_APP, + pinned=pinned, + sort_by=args["sort_by"], + ) except LastConversationNotExistsError: raise NotFound("Last Conversation Not Exists.") diff --git a/api/models/web.py b/api/models/web.py index f5d54630e1..028a768519 100644 --- a/api/models/web.py +++ b/api/models/web.py @@ -1,4 +1,5 @@ from sqlalchemy import func +from sqlalchemy.orm import Mapped, mapped_column from .engine import db from .model import Message @@ -33,7 +34,7 @@ class PinnedConversation(db.Model): id = db.Column(StringUUID, server_default=db.text("uuid_generate_v4()")) app_id = db.Column(StringUUID, nullable=False) - conversation_id = db.Column(StringUUID, nullable=False) + conversation_id: Mapped[str] = mapped_column(StringUUID) created_by_role = db.Column(db.String(255), nullable=False, server_default=db.text("'end_user'::character varying")) created_by = db.Column(StringUUID, nullable=False) created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py index 8642972710..456dc3ebeb 100644 --- a/api/services/conversation_service.py +++ b/api/services/conversation_service.py @@ -1,8 +1,9 @@ -from collections.abc import Callable +from collections.abc import Callable, Sequence from datetime import UTC, datetime from typing import Optional, Union -from sqlalchemy import asc, desc, or_ +from sqlalchemy import asc, desc, func, or_, select +from sqlalchemy.orm import Session from core.app.entities.app_invoke_entities import InvokeFrom from core.llm_generator.llm_generator import LLMGenerator @@ -18,19 +19,21 @@ class ConversationService: @classmethod def pagination_by_last_id( cls, + *, + session: Session, app_model: App, user: Optional[Union[Account, EndUser]], last_id: Optional[str], limit: int, invoke_from: InvokeFrom, - include_ids: Optional[list] = None, - exclude_ids: Optional[list] = None, + include_ids: Optional[Sequence[str]] = None, + exclude_ids: Optional[Sequence[str]] = None, sort_by: str = "-updated_at", ) -> InfiniteScrollPagination: if not user: return InfiniteScrollPagination(data=[], limit=limit, has_more=False) - base_query = db.session.query(Conversation).filter( + stmt = select(Conversation).where( Conversation.is_deleted == False, Conversation.app_id == app_model.id, Conversation.from_source == ("api" if isinstance(user, EndUser) else "console"), @@ -38,37 +41,40 @@ class ConversationService: Conversation.from_account_id == (user.id if isinstance(user, Account) else None), or_(Conversation.invoke_from.is_(None), Conversation.invoke_from == invoke_from.value), ) - if include_ids is not None: - base_query = base_query.filter(Conversation.id.in_(include_ids)) - + stmt = stmt.where(Conversation.id.in_(include_ids)) if exclude_ids is not None: - base_query = base_query.filter(~Conversation.id.in_(exclude_ids)) + stmt = stmt.where(~Conversation.id.in_(exclude_ids)) # define sort fields and directions sort_field, sort_direction = cls._get_sort_params(sort_by) if last_id: - last_conversation = base_query.filter(Conversation.id == last_id).first() + last_conversation = session.scalar(stmt.where(Conversation.id == last_id)) if not last_conversation: raise LastConversationNotExistsError() # build filters based on sorting - filter_condition = cls._build_filter_condition(sort_field, sort_direction, last_conversation) - base_query = base_query.filter(filter_condition) - - base_query = base_query.order_by(sort_direction(getattr(Conversation, sort_field))) - - conversations = base_query.limit(limit).all() + filter_condition = cls._build_filter_condition( + sort_field=sort_field, + sort_direction=sort_direction, + reference_conversation=last_conversation, + ) + stmt = stmt.where(filter_condition) + query_stmt = stmt.order_by(sort_direction(getattr(Conversation, sort_field))).limit(limit) + conversations = session.scalars(query_stmt).all() has_more = False if len(conversations) == limit: current_page_last_conversation = conversations[-1] rest_filter_condition = cls._build_filter_condition( - sort_field, sort_direction, current_page_last_conversation, is_next_page=True + sort_field=sort_field, + sort_direction=sort_direction, + reference_conversation=current_page_last_conversation, ) - rest_count = base_query.filter(rest_filter_condition).count() - + count_stmt = stmt.where(rest_filter_condition) + count_stmt = select(func.count()).select_from(count_stmt.subquery()) + rest_count = session.scalar(count_stmt) or 0 if rest_count > 0: has_more = True @@ -81,11 +87,9 @@ class ConversationService: return sort_by, asc @classmethod - def _build_filter_condition( - cls, sort_field: str, sort_direction: Callable, reference_conversation: Conversation, is_next_page: bool = False - ): + def _build_filter_condition(cls, sort_field: str, sort_direction: Callable, reference_conversation: Conversation): field_value = getattr(reference_conversation, sort_field) - if (sort_direction == desc and not is_next_page) or (sort_direction == asc and is_next_page): + if sort_direction == desc: return getattr(Conversation, sort_field) < field_value else: return getattr(Conversation, sort_field) > field_value diff --git a/api/services/web_conversation_service.py b/api/services/web_conversation_service.py index d7ccc964cb..508fe20970 100644 --- a/api/services/web_conversation_service.py +++ b/api/services/web_conversation_service.py @@ -1,5 +1,8 @@ from typing import Optional, Union +from sqlalchemy import select +from sqlalchemy.orm import Session + from core.app.entities.app_invoke_entities import InvokeFrom from extensions.ext_database import db from libs.infinite_scroll_pagination import InfiniteScrollPagination @@ -13,6 +16,8 @@ class WebConversationService: @classmethod def pagination_by_last_id( cls, + *, + session: Session, app_model: App, user: Optional[Union[Account, EndUser]], last_id: Optional[str], @@ -23,24 +28,25 @@ class WebConversationService: ) -> InfiniteScrollPagination: include_ids = None exclude_ids = None - if pinned is not None: - pinned_conversations = ( - db.session.query(PinnedConversation) - .filter( + if pinned is not None and user: + stmt = ( + select(PinnedConversation.conversation_id) + .where( PinnedConversation.app_id == app_model.id, PinnedConversation.created_by_role == ("account" if isinstance(user, Account) else "end_user"), PinnedConversation.created_by == user.id, ) .order_by(PinnedConversation.created_at.desc()) - .all() ) - pinned_conversation_ids = [pc.conversation_id for pc in pinned_conversations] + pinned_conversation_ids = session.scalars(stmt).all() + if pinned: include_ids = pinned_conversation_ids else: exclude_ids = pinned_conversation_ids return ConversationService.pagination_by_last_id( + session=session, app_model=app_model, user=user, last_id=last_id, From 2ad2a402fb1c582b6318b463b7e926cfed2aff77 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:40:12 +0800 Subject: [PATCH 092/138] fix(app_dsl_service): handle missing app mode with a ValueError (#11945) Signed-off-by: -LAN- --- api/services/app_dsl_service.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index 8180c3b400..0478903fa4 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -340,7 +340,10 @@ class AppDslService: ) -> App: """Create a new app or update an existing one.""" app_data = data.get("app", {}) - app_mode = AppMode(app_data["mode"]) + app_mode = app_data.get("mode") + if not app_mode: + raise ValueError("loss app mode") + app_mode = AppMode(app_mode) # Set icon type icon_type_value = icon_type or app_data.get("icon_type") From a056a9d6010a2dfb5bb9ced1f6d50104016b2176 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:40:43 +0800 Subject: [PATCH 093/138] feat(code_node): add more check (#11949) Signed-off-by: -LAN- --- api/core/helper/code_executor/code_executor.py | 2 +- .../helper/code_executor/template_transformer.py | 13 +++++++++---- api/core/workflow/nodes/code/code_node.py | 16 +++++++--------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 011ff382ea..584e3e9698 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -118,7 +118,7 @@ class CodeExecutor: return response.data.stdout or "" @classmethod - def execute_workflow_code_template(cls, language: CodeLanguage, code: str, inputs: Mapping[str, Any]) -> dict: + def execute_workflow_code_template(cls, language: CodeLanguage, code: str, inputs: Mapping[str, Any]): """ Execute code :param language: code language diff --git a/api/core/helper/code_executor/template_transformer.py b/api/core/helper/code_executor/template_transformer.py index cf422fd023..605719747a 100644 --- a/api/core/helper/code_executor/template_transformer.py +++ b/api/core/helper/code_executor/template_transformer.py @@ -25,7 +25,7 @@ class TemplateTransformer(ABC): return runner_script, preload_script @classmethod - def extract_result_str_from_response(cls, response: str) -> str: + def extract_result_str_from_response(cls, response: str): result = re.search(rf"{cls._result_tag}(.*){cls._result_tag}", response, re.DOTALL) if not result: raise ValueError("Failed to parse result") @@ -33,15 +33,20 @@ class TemplateTransformer(ABC): return result @classmethod - def transform_response(cls, response: str): + def transform_response(cls, response: str) -> Mapping[str, Any]: """ Transform response to dict :param response: response :return: """ - result = json.loads(cls.extract_result_str_from_response(response)) + try: + result = json.loads(cls.extract_result_str_from_response(response)) + except json.JSONDecodeError: + raise ValueError("failed to parse response") if not isinstance(result, dict): - raise ValueError("Result must be a dict") + raise ValueError("result must be a dict") + if not all(isinstance(k, str) for k in result): + raise ValueError("result keys must be strings") return result @classmethod diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index 6adf82c455..4e371ca436 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -59,7 +59,7 @@ class CodeNode(BaseNode[CodeNodeData]): ) # Transform result - result = self._transform_result(result, self.node_data.outputs) + result = self._transform_result(result=result, output_schema=self.node_data.outputs) except (CodeExecutionError, CodeNodeError) as e: return NodeRunResult( status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error=str(e), error_type=type(e).__name__ @@ -116,14 +116,12 @@ class CodeNode(BaseNode[CodeNodeData]): return value def _transform_result( - self, result: dict, output_schema: Optional[dict[str, CodeNodeData.Output]], prefix: str = "", depth: int = 1 - ) -> dict: - """ - Transform result - :param result: result - :param output_schema: output schema - :return: - """ + self, + result: Mapping[str, Any], + output_schema: Optional[dict[str, CodeNodeData.Output]], + prefix: str = "", + depth: int = 1, + ): if depth > dify_config.CODE_MAX_DEPTH: raise DepthLimitError(f"Depth limit ${dify_config.CODE_MAX_DEPTH} reached, object too deep.") From 90f093eb671ae8aef693f2e41bfb2780d8369181 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:40:56 +0800 Subject: [PATCH 094/138] fix(json_in_md_parser): improve error messages for JSON parsing failures (#11948) Signed-off-by: -LAN- --- api/libs/json_in_md_parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/libs/json_in_md_parser.py b/api/libs/json_in_md_parser.py index 41c5d20c4b..267af611f5 100644 --- a/api/libs/json_in_md_parser.py +++ b/api/libs/json_in_md_parser.py @@ -27,7 +27,7 @@ def parse_json_markdown(json_string: str) -> dict: extracted_content = json_string[start_index:end_index].strip() parsed = json.loads(extracted_content) else: - raise Exception("Could not find JSON block in the output.") + raise ValueError("could not find json block in the output.") return parsed @@ -36,10 +36,10 @@ def parse_and_check_json_markdown(text: str, expected_keys: list[str]) -> dict: try: json_obj = parse_json_markdown(text) except json.JSONDecodeError as e: - raise OutputParserError(f"Got invalid JSON object. Error: {e}") + raise OutputParserError(f"got invalid json object. error: {e}") for key in expected_keys: if key not in json_obj: raise OutputParserError( - f"Got invalid return object. Expected key `{key}` to be present, but got {json_obj}" + f"got invalid return object. expected key `{key}` to be present, but got {json_obj}" ) return json_obj From dd0e81d0940c492df5ea5b4cc17bf259b8a33cca Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:41:06 +0800 Subject: [PATCH 095/138] =?UTF-8?q?fix:=20enhance=20type=20hints=20and=20i?= =?UTF-8?q?mprove=20audio=20message=20handling=20in=20TTS=20pub=E2=80=A6?= =?UTF-8?q?=20(#11947)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: -LAN- --- .../app_generator_tts_publisher.py | 32 +++++++++++-------- .../advanced_chat/generate_task_pipeline.py | 10 +++--- api/core/app/apps/base_app_queue_manager.py | 7 ++-- .../apps/workflow/generate_task_pipeline.py | 10 +++--- .../easy_ui_based_generate_task_pipeline.py | 6 ++-- 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py index 18b115dfe4..29709914b7 100644 --- a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py +++ b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py @@ -4,14 +4,17 @@ import logging import queue import re import threading +from collections.abc import Iterable from core.app.entities.queue_entities import ( + MessageQueueMessage, QueueAgentMessageEvent, QueueLLMChunkEvent, QueueNodeSucceededEvent, QueueTextChunkEvent, + WorkflowQueueMessage, ) -from core.model_manager import ModelManager +from core.model_manager import ModelInstance, ModelManager from core.model_runtime.entities.model_entities import ModelType @@ -21,7 +24,7 @@ class AudioTrunk: self.status = status -def _invoice_tts(text_content: str, model_instance, tenant_id: str, voice: str): +def _invoice_tts(text_content: str, model_instance: ModelInstance, tenant_id: str, voice: str): if not text_content or text_content.isspace(): return return model_instance.invoke_tts( @@ -29,13 +32,19 @@ def _invoice_tts(text_content: str, model_instance, tenant_id: str, voice: str): ) -def _process_future(future_queue, audio_queue): +def _process_future( + future_queue: queue.Queue[concurrent.futures.Future[Iterable[bytes] | None] | None], + audio_queue: queue.Queue[AudioTrunk], +): while True: try: future = future_queue.get() if future is None: break - for audio in future.result(): + invoke_result = future.result() + if not invoke_result: + continue + for audio in invoke_result: audio_base64 = base64.b64encode(bytes(audio)) audio_queue.put(AudioTrunk("responding", audio=audio_base64)) except Exception as e: @@ -49,8 +58,8 @@ class AppGeneratorTTSPublisher: self.logger = logging.getLogger(__name__) self.tenant_id = tenant_id self.msg_text = "" - self._audio_queue = queue.Queue() - self._msg_queue = queue.Queue() + self._audio_queue: queue.Queue[AudioTrunk] = queue.Queue() + self._msg_queue: queue.Queue[WorkflowQueueMessage | MessageQueueMessage | None] = queue.Queue() self.match = re.compile(r"[。.!?]") self.model_manager = ModelManager() self.model_instance = self.model_manager.get_default_model_instance( @@ -66,14 +75,11 @@ class AppGeneratorTTSPublisher: self._runtime_thread = threading.Thread(target=self._runtime).start() self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) - def publish(self, message): - try: - self._msg_queue.put(message) - except Exception as e: - self.logger.warning(e) + def publish(self, message: WorkflowQueueMessage | MessageQueueMessage | None, /): + self._msg_queue.put(message) def _runtime(self): - future_queue = queue.Queue() + future_queue: queue.Queue[concurrent.futures.Future[Iterable[bytes] | None] | None] = queue.Queue() threading.Thread(target=_process_future, args=(future_queue, self._audio_queue)).start() while True: try: @@ -110,7 +116,7 @@ class AppGeneratorTTSPublisher: break future_queue.put(None) - def check_and_get_audio(self) -> AudioTrunk | None: + def check_and_get_audio(self): try: if self._last_audio_event and self._last_audio_event.status == "finish": if self.executor: diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index ce0e959627..7c8f9f3813 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -197,11 +197,11 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc stream_response=stream_response, ) - def _listen_audio_msg(self, publisher, task_id: str): + def _listen_audio_msg(self, publisher: AppGeneratorTTSPublisher | None, task_id: str): if not publisher: return None - audio_msg: AudioTrunk = publisher.check_and_get_audio() - if audio_msg and audio_msg.status != "finish": + audio_msg = publisher.check_and_get_audio() + if audio_msg and isinstance(audio_msg, AudioTrunk) and audio_msg.status != "finish": return MessageAudioStreamResponse(audio=audio_msg.audio, task_id=task_id) return None @@ -222,7 +222,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc for response in self._process_stream_response(tts_publisher=tts_publisher, trace_manager=trace_manager): while True: - audio_response = self._listen_audio_msg(tts_publisher, task_id=task_id) + audio_response = self._listen_audio_msg(publisher=tts_publisher, task_id=task_id) if audio_response: yield audio_response else: @@ -511,7 +511,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc # only publish tts message at text chunk streaming if tts_publisher: - tts_publisher.publish(message=queue_message) + tts_publisher.publish(queue_message) self._task_state.answer += delta_text yield self._message_to_stream_response( diff --git a/api/core/app/apps/base_app_queue_manager.py b/api/core/app/apps/base_app_queue_manager.py index 4c4d282e99..3725c6e6dd 100644 --- a/api/core/app/apps/base_app_queue_manager.py +++ b/api/core/app/apps/base_app_queue_manager.py @@ -1,7 +1,6 @@ import queue import time from abc import abstractmethod -from collections.abc import Generator from enum import Enum from typing import Any @@ -11,9 +10,11 @@ from configs import dify_config from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.queue_entities import ( AppQueueEvent, + MessageQueueMessage, QueueErrorEvent, QueuePingEvent, QueueStopEvent, + WorkflowQueueMessage, ) from extensions.ext_redis import redis_client @@ -37,11 +38,11 @@ class AppQueueManager: AppQueueManager._generate_task_belong_cache_key(self._task_id), 1800, f"{user_prefix}-{self._user_id}" ) - q = queue.Queue() + q: queue.Queue[WorkflowQueueMessage | MessageQueueMessage | None] = queue.Queue() self._q = q - def listen(self) -> Generator: + def listen(self): """ Listen to queue :return: diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py index 79e5e2bcb9..d279002285 100644 --- a/api/core/app/apps/workflow/generate_task_pipeline.py +++ b/api/core/app/apps/workflow/generate_task_pipeline.py @@ -171,11 +171,11 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa yield WorkflowAppStreamResponse(workflow_run_id=workflow_run_id, stream_response=stream_response) - def _listen_audio_msg(self, publisher, task_id: str): + def _listen_audio_msg(self, publisher: AppGeneratorTTSPublisher | None, task_id: str): if not publisher: return None - audio_msg: AudioTrunk = publisher.check_and_get_audio() - if audio_msg and audio_msg.status != "finish": + audio_msg = publisher.check_and_get_audio() + if audio_msg and isinstance(audio_msg, AudioTrunk) and audio_msg.status != "finish": return MessageAudioStreamResponse(audio=audio_msg.audio, task_id=task_id) return None @@ -196,7 +196,7 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa for response in self._process_stream_response(tts_publisher=tts_publisher, trace_manager=trace_manager): while True: - audio_response = self._listen_audio_msg(tts_publisher, task_id=task_id) + audio_response = self._listen_audio_msg(publisher=tts_publisher, task_id=task_id) if audio_response: yield audio_response else: @@ -421,7 +421,7 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa # only publish tts message at text chunk streaming if tts_publisher: - tts_publisher.publish(message=queue_message) + tts_publisher.publish(queue_message) self._task_state.answer += delta_text yield self._text_chunk_to_stream_response( diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index 917649f34e..4216cd46cf 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -201,11 +201,11 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan stream_response=stream_response, ) - def _listen_audio_msg(self, publisher, task_id: str): + def _listen_audio_msg(self, publisher: AppGeneratorTTSPublisher | None, task_id: str): if publisher is None: return None - audio_msg: AudioTrunk = publisher.check_and_get_audio() - if audio_msg and audio_msg.status != "finish": + audio_msg = publisher.check_and_get_audio() + if audio_msg and isinstance(audio_msg, AudioTrunk) and audio_msg.status != "finish": # audio_str = audio_msg.audio.decode('utf-8', errors='ignore') return MessageAudioStreamResponse(audio=audio_msg.audio, task_id=task_id) return None From 5db8addcc61221e600ab9869e135c577e51f43ce Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:41:34 +0800 Subject: [PATCH 096/138] fix(core/errors): change base class of custom exceptions to ValueError (#11955) Signed-off-by: -LAN- --- api/core/errors/error.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/core/errors/error.py b/api/core/errors/error.py index 3b186476eb..ad921bc255 100644 --- a/api/core/errors/error.py +++ b/api/core/errors/error.py @@ -1,7 +1,7 @@ from typing import Optional -class LLMError(Exception): +class LLMError(ValueError): """Base class for all LLM exceptions.""" description: Optional[str] = None @@ -16,7 +16,7 @@ class LLMBadRequestError(LLMError): description = "Bad Request" -class ProviderTokenNotInitError(Exception): +class ProviderTokenNotInitError(ValueError): """ Custom exception raised when the provider token is not initialized. """ @@ -27,7 +27,7 @@ class ProviderTokenNotInitError(Exception): self.description = args[0] if args else self.description -class QuotaExceededError(Exception): +class QuotaExceededError(ValueError): """ Custom exception raised when the quota for a provider has been exceeded. """ @@ -35,7 +35,7 @@ class QuotaExceededError(Exception): description = "Quota Exceeded" -class AppInvokeQuotaExceededError(Exception): +class AppInvokeQuotaExceededError(ValueError): """ Custom exception raised when the quota for an app has been exceeded. """ @@ -43,7 +43,7 @@ class AppInvokeQuotaExceededError(Exception): description = "App Invoke Quota Exceeded" -class ModelCurrentlyNotSupportError(Exception): +class ModelCurrentlyNotSupportError(ValueError): """ Custom exception raised when the model not support """ @@ -51,7 +51,7 @@ class ModelCurrentlyNotSupportError(Exception): description = "Model Currently Not Support" -class InvokeRateLimitError(Exception): +class InvokeRateLimitError(ValueError): """Raised when the Invoke returns rate limit error.""" description = "Rate Limit Error" From 2c4df108e56a54ff0d8b44e4cabf50273cfc2fae Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:41:53 +0800 Subject: [PATCH 097/138] fix: raise http request node error on httpx.request error (#11954) Signed-off-by: -LAN- --- api/core/ops/ops_trace_manager.py | 8 +++++++- api/core/workflow/nodes/http_request/executor.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index b7799ce1fb..a04fc6ee78 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -355,7 +355,13 @@ class TraceTask: def conversation_trace(self, **kwargs): return kwargs - def workflow_trace(self, workflow_run: WorkflowRun, conversation_id, user_id): + def workflow_trace(self, workflow_run: WorkflowRun | None, conversation_id, user_id): + if not workflow_run: + raise ValueError("Workflow run not found") + + db.session.merge(workflow_run) + db.sessoin.refresh(workflow_run) + workflow_id = workflow_run.workflow_id tenant_id = workflow_run.tenant_id workflow_run_id = workflow_run.id diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index 92f190091b..b96402a76a 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -249,6 +249,8 @@ class Executor: # request_args = {k: v for k, v in request_args.items() if v is not None} try: response = getattr(ssrf_proxy, self.method)(**request_args) + except httpx.RequestError as e: + raise HttpRequestNodeError(str(e)) except ssrf_proxy.MaxRetriesExceededError as e: raise HttpRequestNodeError(str(e)) return response From 21a31d7f8bc4eb60080ff3cd7228a19fcabb14fa Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:42:30 +0800 Subject: [PATCH 098/138] fix(base_node): change BaseNodeError exception type from Exception to ValueError (#11952) Signed-off-by: -LAN- --- api/core/workflow/nodes/base/exc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/base/exc.py b/api/core/workflow/nodes/base/exc.py index ec134e031c..aeecf40640 100644 --- a/api/core/workflow/nodes/base/exc.py +++ b/api/core/workflow/nodes/base/exc.py @@ -1,4 +1,4 @@ -class BaseNodeError(Exception): +class BaseNodeError(ValueError): """Base class for node errors.""" pass From 10caab1729cbbdcb786c99454ba9b20413af364f Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:43:31 +0800 Subject: [PATCH 100/138] fix: change CredentialsValidateFailedError to inherit from ValueError (#11950) Signed-off-by: -LAN- --- api/core/model_runtime/errors/validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/model_runtime/errors/validate.py b/api/core/model_runtime/errors/validate.py index 7fcd2133f9..16bebcc67d 100644 --- a/api/core/model_runtime/errors/validate.py +++ b/api/core/model_runtime/errors/validate.py @@ -1,4 +1,4 @@ -class CredentialsValidateFailedError(Exception): +class CredentialsValidateFailedError(ValueError): """ Credentials validate failed error """ From 03ddee3663eedd9bfa41c998b5035c6f57775171 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 10:43:40 +0800 Subject: [PATCH 101/138] fix(variable_assigner): change VariableOperatorNodeError to inherit from ValueError (#11951) Signed-off-by: -LAN- --- api/core/workflow/nodes/variable_assigner/common/exc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/variable_assigner/common/exc.py b/api/core/workflow/nodes/variable_assigner/common/exc.py index a1178fb020..f8dbedc290 100644 --- a/api/core/workflow/nodes/variable_assigner/common/exc.py +++ b/api/core/workflow/nodes/variable_assigner/common/exc.py @@ -1,4 +1,4 @@ -class VariableOperatorNodeError(Exception): +class VariableOperatorNodeError(ValueError): """Base error type, don't use directly.""" pass From 6b49889041f5d25cd2c5bc2e0f7915abb7027027 Mon Sep 17 00:00:00 2001 From: liuhaoran <75237518+liuhaoran1212@users.noreply.github.com> Date: Sun, 22 Dec 2024 10:45:55 +0800 Subject: [PATCH 102/138] fix: messagefeedbackapi support content (#11716) Signed-off-by: weiyang <24080293@smb956101.com> Co-authored-by: weiyang <24080293@smb956101.com> --- api/controllers/service_api/app/message.py | 3 ++- api/services/message_service.py | 9 ++++++++- web/app/components/develop/template/template.en.mdx | 8 ++++++-- web/app/components/develop/template/template.ja.mdx | 8 ++++++-- web/app/components/develop/template/template.zh.mdx | 8 ++++++-- .../develop/template/template_advanced_chat.en.mdx | 8 ++++++-- .../develop/template/template_advanced_chat.ja.mdx | 8 ++++++-- .../develop/template/template_advanced_chat.zh.mdx | 8 ++++++-- web/app/components/develop/template/template_chat.en.mdx | 8 ++++++-- web/app/components/develop/template/template_chat.ja.mdx | 8 ++++++-- web/app/components/develop/template/template_chat.zh.mdx | 8 ++++++-- 11 files changed, 64 insertions(+), 20 deletions(-) diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py index ada40ec9cb..599401bc6f 100644 --- a/api/controllers/service_api/app/message.py +++ b/api/controllers/service_api/app/message.py @@ -104,10 +104,11 @@ class MessageFeedbackApi(Resource): parser = reqparse.RequestParser() parser.add_argument("rating", type=str, choices=["like", "dislike", None], location="json") + parser.add_argument("content", type=str, location="json") args = parser.parse_args() try: - MessageService.create_feedback(app_model, message_id, end_user, args["rating"]) + MessageService.create_feedback(app_model, message_id, end_user, args["rating"], args["content"]) except services.errors.message.MessageNotExistsError: raise NotFound("Message Not Exists.") diff --git a/api/services/message_service.py b/api/services/message_service.py index f432a77c80..be2922f4c5 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -151,7 +151,12 @@ class MessageService: @classmethod def create_feedback( - cls, app_model: App, message_id: str, user: Optional[Union[Account, EndUser]], rating: Optional[str] + cls, + app_model: App, + message_id: str, + user: Optional[Union[Account, EndUser]], + rating: Optional[str], + content: Optional[str], ) -> MessageFeedback: if not user: raise ValueError("user cannot be None") @@ -164,6 +169,7 @@ class MessageService: db.session.delete(feedback) elif rating and feedback: feedback.rating = rating + feedback.content = content elif not rating and not feedback: raise ValueError("rating cannot be None when feedback not exists") else: @@ -172,6 +178,7 @@ class MessageService: conversation_id=message.conversation_id, message_id=message.id, rating=rating, + content=content, from_source=("user" if isinstance(user, EndUser) else "admin"), from_end_user_id=(user.id if isinstance(user, EndUser) else None), from_account_id=(user.id if isinstance(user, Account) else None), diff --git a/web/app/components/develop/template/template.en.mdx b/web/app/components/develop/template/template.en.mdx index f469076bf3..877955039c 100755 --- a/web/app/components/develop/template/template.en.mdx +++ b/web/app/components/develop/template/template.en.mdx @@ -346,6 +346,9 @@ The text generation application offers non-session support and is ideal for tran User identifier, defined by the developer's rules, must be unique within the application. + + The specific content of message feedback. + ### Response @@ -353,7 +356,7 @@ The text generation application offers non-session support and is ideal for tran - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -361,7 +364,8 @@ The text generation application offers non-session support and is ideal for tran --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template.ja.mdx b/web/app/components/develop/template/template.ja.mdx index bd92bd7f36..c3b376f2e7 100755 --- a/web/app/components/develop/template/template.ja.mdx +++ b/web/app/components/develop/template/template.ja.mdx @@ -345,6 +345,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from 開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。 + + メッセージのフィードバックです。 + ### レスポンス @@ -352,7 +355,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -360,7 +363,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx index 7b1bec3546..be7470480f 100755 --- a/web/app/components/develop/template/template.zh.mdx +++ b/web/app/components/develop/template/template.zh.mdx @@ -320,6 +320,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' 用户标识,由开发者定义规则,需保证用户标识在应用内唯一。 + + 消息反馈的具体信息。 + ### Response @@ -327,7 +330,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -335,7 +338,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 5f00977e75..5106b6f36a 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -444,6 +444,9 @@ Chat applications support session persistence, allowing previous chat history to User identifier, defined by the developer's rules, must be unique within the application. + + The specific content of message feedback. + ### Response @@ -451,7 +454,7 @@ Chat applications support session persistence, allowing previous chat history to - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -459,7 +462,8 @@ Chat applications support session persistence, allowing previous chat history to --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index 7c933598f9..cf65a29b44 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -444,6 +444,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。 + + メッセージのフィードバックです。 + ### 応答 @@ -451,7 +454,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -459,7 +462,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index fec0636d40..662309525b 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -450,6 +450,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' 用户标识,由开发者定义规则,需保证用户标识在应用内唯一。 + + 消息反馈的具体信息。 + ### Response @@ -457,7 +460,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -465,7 +468,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index 1eb289b3c1..d38e80407a 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -408,6 +408,9 @@ Chat applications support session persistence, allowing previous chat history to User identifier, defined by the developer's rules, must be unique within the application. + + The specific content of message feedback. + ### Response @@ -415,7 +418,7 @@ Chat applications support session persistence, allowing previous chat history to - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -423,7 +426,8 @@ Chat applications support session persistence, allowing previous chat history to --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_chat.ja.mdx b/web/app/components/develop/template/template_chat.ja.mdx index fb686e0cff..96db9912d5 100644 --- a/web/app/components/develop/template/template_chat.ja.mdx +++ b/web/app/components/develop/template/template_chat.ja.mdx @@ -408,6 +408,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。 + + メッセージのフィードバックです。 + ### 応答 @@ -415,7 +418,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -423,7 +426,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index af96cab5ff..3d6e3630be 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -423,6 +423,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' 用户标识,由开发者定义规则,需保证用户标识在应用内唯一。 + + 消息反馈的具体信息。 + ### Response @@ -430,7 +433,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - + ```bash {{ title: 'cURL' }} curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \ @@ -438,7 +441,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' --header 'Content-Type: application/json' \ --data-raw '{ "rating": "like", - "user": "abc-123" + "user": "abc-123", + "content": "message feedback information" }' ``` From 750662eb08670c7289b943fa515334e5a32883ad Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 22 Dec 2024 14:55:18 +0800 Subject: [PATCH 103/138] fix(workflow): update updated_at default to use UTC timezone (#11960) Signed-off-by: -LAN- --- api/models/workflow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/models/workflow.py b/api/models/workflow.py index 0e5cf38d25..7896339f37 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -1,6 +1,6 @@ import json from collections.abc import Mapping, Sequence -from datetime import datetime +from datetime import UTC, datetime from enum import Enum, StrEnum from typing import Any, Optional, Union @@ -106,7 +106,10 @@ class Workflow(db.Model): created_at: Mapped[datetime] = mapped_column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by: Mapped[Optional[str]] = mapped_column(StringUUID) updated_at: Mapped[datetime] = mapped_column( - db.DateTime, nullable=False, server_default=func.current_timestamp(), server_onupdate=func.current_timestamp() + db.DateTime, + nullable=False, + default=datetime.now(UTC).replace(tzinfo=None), + server_onupdate=func.current_timestamp(), ) _environment_variables: Mapped[str] = mapped_column( "environment_variables", db.Text, nullable=False, server_default="{}" From 26c10b9931bc2ea3ded8030c1de97760f178c522 Mon Sep 17 00:00:00 2001 From: yihong Date: Sun, 22 Dec 2024 14:58:33 +0800 Subject: [PATCH 104/138] fix: 'dict_keys' object is not subscriptable error (#11957) Signed-off-by: yihong0618 --- api/core/tools/provider/app_tool_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/tools/provider/app_tool_provider.py b/api/core/tools/provider/app_tool_provider.py index 09f328cd1f..582ad636b1 100644 --- a/api/core/tools/provider/app_tool_provider.py +++ b/api/core/tools/provider/app_tool_provider.py @@ -62,7 +62,7 @@ class AppToolProviderEntity(ToolProviderController): user_input_form_list = app_model_config.user_input_form_list for input_form in user_input_form_list: # get type - form_type = input_form.keys()[0] + form_type = list(input_form.keys())[0] default = input_form[form_type]["default"] required = input_form[form_type]["required"] label = input_form[form_type]["label"] From d9875fe232029bfd3f34c9be7bade1a7313ff9e1 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Mon, 23 Dec 2024 09:20:30 +0800 Subject: [PATCH 105/138] fix(commands): validate name encoding for non-Latin characters (#11965) --- api/commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/commands.py b/api/commands.py index 09548ac9f3..bf013cc77e 100644 --- a/api/commands.py +++ b/api/commands.py @@ -555,7 +555,8 @@ def create_tenant(email: str, language: Optional[str] = None, name: Optional[str if language not in languages: language = "en-US" - name = name.strip() + # Validates name encoding for non-Latin characters. + name = name.strip().encode("utf-8").decode("utf-8") if name else None # generate random password new_password = secrets.token_urlsafe(16) From 39df994ff9c48723d84fc78065ac3996c93c6084 Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 23 Dec 2024 09:20:47 +0800 Subject: [PATCH 106/138] fix: create_feedback args are wrong (#11962) Signed-off-by: yihong0618 --- api/controllers/console/explore/message.py | 2 +- api/controllers/web/message.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index 3d221ff30a..4e11d8005f 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -70,7 +70,7 @@ class MessageFeedbackApi(InstalledAppResource): args = parser.parse_args() try: - MessageService.create_feedback(app_model, message_id, current_user, args["rating"]) + MessageService.create_feedback(app_model, message_id, current_user, args["rating"], args["content"]) except services.errors.message.MessageNotExistsError: raise NotFound("Message Not Exists.") diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py index 98891f5d00..febaab5328 100644 --- a/api/controllers/web/message.py +++ b/api/controllers/web/message.py @@ -108,7 +108,7 @@ class MessageFeedbackApi(WebApiResource): args = parser.parse_args() try: - MessageService.create_feedback(app_model, message_id, end_user, args["rating"]) + MessageService.create_feedback(app_model, message_id, end_user, args["rating"], args["content"]) except services.errors.message.MessageNotExistsError: raise NotFound("Message Not Exists.") From 74b1b601253d328f3369d1df2d95531e6c251ab9 Mon Sep 17 00:00:00 2001 From: Yi Xiao <54782454+YIXIAO0@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:17:49 +0800 Subject: [PATCH 107/138] Feat: account page dark mode (#11977) --- web/app/account/account-page/index.tsx | 36 +++++++++---------- web/app/account/avatar.tsx | 12 +++---- web/app/account/layout.tsx | 2 +- web/app/components/base/modal/index.tsx | 12 +++---- web/app/components/base/select/index.tsx | 17 ++++----- .../header/account-dropdown/index.tsx | 35 +++++++++--------- .../header/account-setting/collapse/index.tsx | 10 +++--- .../data-source-page/index.tsx | 1 - .../data-source-page/panel/config-item.tsx | 14 ++++---- .../data-source-page/panel/index.tsx | 24 ++++++------- .../header/account-setting/index.tsx | 14 ++++---- .../account-setting/language-page/index.tsx | 2 +- .../account-setting/members-page/index.tsx | 22 ++++++------ web/app/components/header/header-wrapper.tsx | 2 +- web/app/components/header/index.tsx | 2 +- web/app/components/header/indicator/index.tsx | 20 +++++------ web/themes/manual-dark.css | 31 +++++++++++++--- web/themes/manual-light.css | 31 +++++++++++++--- web/themes/tailwind-theme-var-define.ts | 1 + 19 files changed, 165 insertions(+), 123 deletions(-) diff --git a/web/app/account/account-page/index.tsx b/web/app/account/account-page/index.tsx index 71540ce3b1..c7af05793f 100644 --- a/web/app/account/account-page/index.tsx +++ b/web/app/account/account-page/index.tsx @@ -18,10 +18,10 @@ import { IS_CE_EDITION } from '@/config' import Input from '@/app/components/base/input' const titleClassName = ` - text-sm font-medium text-gray-900 + system-sm-semibold text-text-secondary ` const descriptionClassName = ` - mt-1 text-xs font-normal text-gray-500 + mt-1 body-xs-regular text-text-tertiary ` const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/ @@ -122,7 +122,7 @@ export default function AccountPage() {
-
{item.name}
+
{item.name}
) } @@ -130,7 +130,7 @@ export default function AccountPage() { return ( <>
-

{t('common.account.myAccount')}

+

{t('common.account.myAccount')}

@@ -142,10 +142,10 @@ export default function AccountPage() {
{t('common.account.name')}
-
+
{userProfile.name}
-
+
{t('common.operation.edit')}
@@ -153,7 +153,7 @@ export default function AccountPage() {
{t('common.account.email')}
-
+
{userProfile.email}
@@ -162,14 +162,14 @@ export default function AccountPage() { systemFeatures.enable_email_password_login && (
-
{t('common.account.password')}
-
{t('common.account.passwordTip')}
+
{t('common.account.password')}
+
{t('common.account.passwordTip')}
) } -
+
{t('common.account.langGeniusAccount')}
{t('common.account.langGeniusAccountTip')}
@@ -181,7 +181,7 @@ export default function AccountPage() { wrapperClassName='mt-2' /> )} - {!IS_CE_EDITION && } + {!IS_CE_EDITION && }
{ editNameModalVisible && ( @@ -190,7 +190,7 @@ export default function AccountPage() { onClose={() => setEditNameModalVisible(false)} className={s.modal} > -
{t('common.account.editName')}
+
{t('common.account.editName')}
{t('common.account.name')}
-
{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}
+
{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}
{userProfile.is_password_set && ( <>
{t('common.account.currentPassword')}
@@ -242,7 +242,7 @@ export default function AccountPage() {
)} -
+
{userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')}
@@ -261,7 +261,7 @@ export default function AccountPage() {
-
{t('common.account.confirmPassword')}
+
{t('common.account.confirmPassword')}
-
+
{t('common.account.deleteTip')}
{t('common.account.deleteConfirmTip')}
-
{`${t('common.account.delete')}: ${userProfile.email}`}
+
{`${t('common.account.delete')}: ${userProfile.email}`}
} confirmText={t('common.operation.ok') as string} diff --git a/web/app/account/avatar.tsx b/web/app/account/avatar.tsx index 544e43ab27..8fdecc07bf 100644 --- a/web/app/account/avatar.tsx +++ b/web/app/account/avatar.tsx @@ -40,9 +40,9 @@ export default function AppSelector() { className={` inline-flex items-center rounded-[20px] p-1x text-sm - text-gray-700 hover:bg-gray-200 + text-text-primary mobile:px-1 - ${open && 'bg-gray-200'} + ${open && 'bg-components-panel-bg-blur'} `} > @@ -60,7 +60,7 @@ export default function AppSelector() { @@ -78,10 +78,10 @@ export default function AppSelector() {
handleLogout()}>
- -
{t('common.userProfile.logout')}
+ +
{t('common.userProfile.logout')}
diff --git a/web/app/account/layout.tsx b/web/app/account/layout.tsx index 5aa8b05cbf..11a6abeab4 100644 --- a/web/app/account/layout.tsx +++ b/web/app/account/layout.tsx @@ -21,7 +21,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
-
+
{children}
diff --git a/web/app/components/base/modal/index.tsx b/web/app/components/base/modal/index.tsx index 5b8c4be4b8..3040cdb00b 100644 --- a/web/app/components/base/modal/index.tsx +++ b/web/app/components/base/modal/index.tsx @@ -1,6 +1,6 @@ import { Dialog, Transition } from '@headlessui/react' import { Fragment } from 'react' -import { XMarkIcon } from '@heroicons/react/24/outline' +import { RiCloseLine } from '@remixicon/react' import classNames from '@/utils/classnames' // https://headlessui.com/react/dialog @@ -39,7 +39,7 @@ export default function Modal({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
{title && {title} } - {description && + {description && {description} } {closable - &&
- + { e.stopPropagation() onClose() diff --git a/web/app/components/base/select/index.tsx b/web/app/components/base/select/index.tsx index c70cf24661..221d70355f 100644 --- a/web/app/components/base/select/index.tsx +++ b/web/app/components/base/select/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import React, { Fragment, useEffect, useState } from 'react' import { Combobox, Listbox, Transition } from '@headlessui/react' -import { CheckIcon, ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/react/20/solid' +import { ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/react/20/solid' +import { RiCheckLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import classNames from '@/utils/classnames' import { @@ -152,7 +153,7 @@ const Select: FC = ({ 'absolute inset-y-0 right-0 flex items-center pr-4 text-gray-700', )} > -
@@ -113,7 +112,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { href='/account' target='_self' rel='noopener noreferrer'>
{t('common.account.account')}
- + @@ -127,7 +126,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { href={mailToSupport(userProfile.email, plan.type, langeniusVersionInfo.current_version)} target='_blank' rel='noopener noreferrer'>
{t('common.userProfile.emailSupport')}
- +
} @@ -136,7 +135,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { href='https://github.com/langgenius/dify/discussions/categories/feedbacks' target='_blank' rel='noopener noreferrer'>
{t('common.userProfile.communityFeedback')}
- +
@@ -145,7 +144,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { href='https://discord.gg/5AEfbxcd9k' target='_blank' rel='noopener noreferrer'>
{t('common.userProfile.community')}
- +
@@ -156,7 +155,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { } target='_blank' rel='noopener noreferrer'>
{t('common.userProfile.helpCenter')}
- +
@@ -165,7 +164,7 @@ export default function AppSelector({ isMobile }: IAppSelector) { href='https://roadmap.dify.ai' target='_blank' rel='noopener noreferrer'>
{t('common.userProfile.roadmap')}
- +
{ @@ -174,7 +173,7 @@ export default function AppSelector({ isMobile }: IAppSelector) {
setAboutVisible(true)}>
{t('common.userProfile.about')}
-
{langeniusVersionInfo.current_version}
+
{langeniusVersionInfo.current_version}
@@ -185,10 +184,10 @@ export default function AppSelector({ isMobile }: IAppSelector) {
handleLogout()}>
-
{t('common.userProfile.logout')}
- +
{t('common.userProfile.logout')}
+
diff --git a/web/app/components/header/account-setting/collapse/index.tsx b/web/app/components/header/account-setting/collapse/index.tsx index a70dca16e5..d0068dabed 100644 --- a/web/app/components/header/account-setting/collapse/index.tsx +++ b/web/app/components/header/account-setting/collapse/index.tsx @@ -25,18 +25,18 @@ const Collapse = ({ const toggle = () => setOpen(!open) return ( -
-
+
+
{title} { open - ? - : + ? + : }
{ open && ( -
+
{ items.map(item => (
onSelect && onSelect(item)}> diff --git a/web/app/components/header/account-setting/data-source-page/index.tsx b/web/app/components/header/account-setting/data-source-page/index.tsx index c3da977ca4..93dc2db854 100644 --- a/web/app/components/header/account-setting/data-source-page/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/index.tsx @@ -12,7 +12,6 @@ export default function DataSourcePage() { return (
-
{t('common.dataSource.add')}
diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx index 2a05808e2a..b7fd8193e2 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -44,22 +44,22 @@ const ConfigItem: FC = ({ const onChangeAuthorizedPage = notionActions?.onChangeAuthorizedPage || function () { } return ( -
+
-
{payload.name}
+
{payload.name}
{ payload.isActive - ? + ? : } -
+
{ payload.isActive ? t(isNotion ? 'common.dataSource.notion.connected' : 'common.dataSource.website.active') : t(isNotion ? 'common.dataSource.notion.disconnected' : 'common.dataSource.website.inactive') }
-
+
{isNotion && ( = ({ { isWebsite && !readOnly && ( -
- +
+
) } diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 4a810020b4..8d2ec0a8ca 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import { PlusIcon } from '@heroicons/react/24/solid' +import { RiAddLine } from '@remixicon/react' import type { ConfigItemType } from './config-item' import ConfigItem from './config-item' @@ -41,12 +41,12 @@ const Panel: FC = ({ const isWebsite = type === DataSourceType.website return ( -
+
-
+
-
{t(`common.dataSource.${type}.title`)}
+
{t(`common.dataSource.${type}.title`)}
{isWebsite && (
{t('common.dataSource.website.with')} { provider === DataSourceProvider.fireCrawl ? '🔥 Firecrawl' : 'Jina Reader'} @@ -55,7 +55,7 @@ const Panel: FC = ({
{ !isConfigured && ( -
+
{t(`common.dataSource.${type}.description`)}
) @@ -81,13 +81,13 @@ const Panel: FC = ({ <> {isSupportList &&
- - {t('common.dataSource.notion.addWorkspace')} + + {t('common.dataSource.connect')}
} ) @@ -98,8 +98,8 @@ const Panel: FC = ({ {isWebsite && !isConfigured && (
= ({ isConfigured && ( <>
-
+
{isNotion ? t('common.dataSource.notion.connectedWorkspace') : t('common.dataSource.website.configuredCrawlers')}
-
+
{ diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index d829f6b77b..4be7ec6ab7 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -152,14 +152,14 @@ export default function AccountSetting({ wrapperClassName='pt-[60px]' >
-
-
{t('common.userProfile.settings')}
+
+
{t('common.userProfile.settings')}
{ menuItems.map(menuItem => (
{!isCurrentWorkspaceDatasetOperator && ( -
{menuItem.name}
+
{menuItem.name}
)}
{ @@ -168,7 +168,7 @@ export default function AccountSetting({ key={item.key} className={` flex items-center h-[37px] mb-[2px] text-sm cursor-pointer rounded-lg - ${activeMenu === item.key ? 'font-semibold text-primary-600 bg-primary-50' : 'font-light text-gray-700'} + ${activeMenu === item.key ? 'system-sm-semibold text-components-menu-item-text-active bg-state-base-active' : 'system-sm-medium text-components-menu-item-text'} `} title={item.name} onClick={() => setActiveMenu(item.key)} @@ -185,7 +185,7 @@ export default function AccountSetting({
-
+
{activeItem?.name}
{ activeItem?.description && ( @@ -193,8 +193,8 @@ export default function AccountSetting({ ) }
-
- +
+
diff --git a/web/app/components/header/account-setting/language-page/index.tsx b/web/app/components/header/account-setting/language-page/index.tsx index fc8db86813..7d3e09fc21 100644 --- a/web/app/components/header/account-setting/language-page/index.tsx +++ b/web/app/components/header/account-setting/language-page/index.tsx @@ -13,7 +13,7 @@ import { timezones } from '@/utils/timezone' import { languages } from '@/i18n/language' const titleClassName = ` - mb-2 text-sm font-medium text-gray-900 + mb-2 system-sm-semibold text-text-secondary ` export default function LanguagePage() { diff --git a/web/app/components/header/account-setting/members-page/index.tsx b/web/app/components/header/account-setting/members-page/index.tsx index 03d65af7a4..c2b722b4a7 100644 --- a/web/app/components/header/account-setting/members-page/index.tsx +++ b/web/app/components/header/account-setting/members-page/index.tsx @@ -85,32 +85,32 @@ const MembersPage = () => {
-
-
{t('common.members.name')}
-
{t('common.members.lastActive')}
-
{t('common.members.role')}
+
+
{t('common.members.name')}
+
{t('common.members.lastActive')}
+
{t('common.members.role')}
{ accounts.map(account => ( -
+
-
+
{account.name} - {account.status === 'pending' && {t('common.members.pending')}} - {userProfile.email === account.email && {t('common.members.you')}} + {account.status === 'pending' && {t('common.members.pending')}} + {userProfile.email === account.email && {t('common.members.you')}}
-
{account.email}
+
{account.email}
-
{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
+
{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}
{ ((isCurrentWorkspaceOwner && account.role !== 'owner') || (isCurrentWorkspaceManager && !['owner', 'admin'].includes(account.role))) ? - :
{RoleMap[account.role] || RoleMap.normal}
+ :
{RoleMap[account.role] || RoleMap.normal}
}
diff --git a/web/app/components/header/header-wrapper.tsx b/web/app/components/header/header-wrapper.tsx index 52728bea87..dd0ec77b82 100644 --- a/web/app/components/header/header-wrapper.tsx +++ b/web/app/components/header/header-wrapper.tsx @@ -11,7 +11,7 @@ const HeaderWrapper = ({ children, }: HeaderWrapperProps) => { const pathname = usePathname() - const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools', '/account'].includes(pathname) + const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools'].includes(pathname) return (
{ // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedSegment]) return ( -
+
{isMobile &&
Date: Mon, 23 Dec 2024 13:53:05 +0800 Subject: [PATCH 108/138] fix: fix update external dataset error in dataset list (#11989) --- api/services/dataset_service.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index a1014e8e0a..4e99c73ad4 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -231,11 +231,15 @@ class DatasetService: DatasetService.check_dataset_permission(dataset, user) if dataset.provider == "external": - dataset.retrieval_model = data.get("external_retrieval_model", None) + external_retrieval_model = data.get("external_retrieval_model", None) + if external_retrieval_model: + dataset.retrieval_model = external_retrieval_model dataset.name = data.get("name", dataset.name) dataset.description = data.get("description", "") + permission = data.get("permission") + if permission: + dataset.permission = permission external_knowledge_id = data.get("external_knowledge_id", None) - dataset.permission = data.get("permission") db.session.add(dataset) if not external_knowledge_id: raise ValueError("External knowledge id is required.") From 4584eb305825a78cff8b6663d3fe5d840c5bd1fc Mon Sep 17 00:00:00 2001 From: yagiyuki <68677393+yagiyuki@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:53:46 +0900 Subject: [PATCH 109/138] Add custom to file types (#11966) Co-authored-by: yagiyuki --- .../components/develop/template/template_advanced_chat.en.mdx | 1 + .../components/develop/template/template_advanced_chat.ja.mdx | 1 + .../components/develop/template/template_advanced_chat.zh.mdx | 1 + web/app/components/develop/template/template_workflow.en.mdx | 1 + web/app/components/develop/template/template_workflow.ja.mdx | 1 + web/app/components/develop/template/template_workflow.zh.mdx | 1 + 6 files changed, 6 insertions(+) diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 5106b6f36a..c7b92acc52 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -71,6 +71,7 @@ Chat applications support session persistence, allowing previous chat history to - `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG') - `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR') - `video` ('MP4', 'MOV', 'MPEG', 'MPGA') + - `custom` (Other file types) - `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload - `url` (string) Image URL (when the transfer method is `remote_url`) - `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`) diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index cf65a29b44..67e8d8f7fe 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -71,6 +71,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG') - `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR') - `video` ('MP4', 'MOV', 'MPEG', 'MPGA') + - `custom` (他のファイルタイプ) - `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file` - `url` (string) 画像URL(転送方法が`remote_url`の場合) - `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合) diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index 662309525b..3bb17d8502 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -68,6 +68,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' - `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' - `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` 具体类型包含:其他文件类型 - `transfer_method` (string) 传递方式: - `remote_url`: 图片地址。 - `local_file`: 上传文件。 diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index cfa5a60d47..58c533c60b 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -60,6 +60,7 @@ Workflow applications offers non-session support and is ideal for translation, a - `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG') - `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR') - `video` ('MP4', 'MOV', 'MPEG', 'MPGA') + - `custom` (Other file types) - `transfer_method` (string) Transfer method, `remote_url` for image URL / `local_file` for file upload - `url` (string) Image URL (when the transfer method is `remote_url`) - `upload_file_id` (string) Uploaded file ID, which must be obtained by uploading through the File Upload API in advance (when the transfer method is `local_file`) diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index b6f8fb543f..2653b4913d 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -60,6 +60,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG') - `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR') - `video` ('MP4', 'MOV', 'MPEG', 'MPGA') + - `custom` (他のファイルタイプ) - `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file` - `url` (string) 画像URL(転送方法が`remote_url`の場合) - `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合) diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index 9cef3d18a5..ddffc0f02d 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -58,6 +58,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 - `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG' - `audio` 具体类型包含:'MP3', 'M4A', 'WAV', 'WEBM', 'AMR' - `video` 具体类型包含:'MP4', 'MOV', 'MPEG', 'MPGA' + - `custom` 具体类型包含:其他文件类型 - `transfer_method` (string) 传递方式,`remote_url` 图片地址 / `local_file` 上传文件 - `url` (string) 图片地址(仅当传递方式为 `remote_url` 时) - `upload_file_id` (string) (string) 上传文件 ID(仅当传递方式为 `local_file` 时) From 4b1e13e9824113c0f4f8d48fc53a93f76449d640 Mon Sep 17 00:00:00 2001 From: JasonVV Date: Mon, 23 Dec 2024 14:30:04 +0800 Subject: [PATCH 110/138] Fix 11979 (#11984) --- api/core/workflow/nodes/llm/node.py | 30 +++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py index bcba3f5a4d..55fac45576 100644 --- a/api/core/workflow/nodes/llm/node.py +++ b/api/core/workflow/nodes/llm/node.py @@ -860,14 +860,16 @@ class LLMNode(BaseNode[LLMNodeData]): ) -> Sequence[PromptMessage]: prompt_messages: list[PromptMessage] = [] for message in messages: - contents: list[PromptMessageContent] = [] if message.edition_type == "jinja2": result_text = _render_jinja2_message( template=message.jinja2_text or "", jinjia2_variables=jinja2_variables, variable_pool=variable_pool, ) - contents.append(TextPromptMessageContent(data=result_text)) + prompt_message = _combine_message_content_with_role( + contents=[TextPromptMessageContent(data=result_text)], role=message.role + ) + prompt_messages.append(prompt_message) else: # Get segment group from basic message if context: @@ -877,6 +879,7 @@ class LLMNode(BaseNode[LLMNodeData]): segment_group = variable_pool.convert_template(template) # Process segments for images + file_contents = [] for segment in segment_group.value: if isinstance(segment, ArrayFileSegment): for file in segment.value: @@ -884,20 +887,27 @@ class LLMNode(BaseNode[LLMNodeData]): file_content = file_manager.to_prompt_message_content( file, image_detail_config=vision_detail_config ) - contents.append(file_content) + file_contents.append(file_content) elif isinstance(segment, FileSegment): file = segment.value if file.type in {FileType.IMAGE, FileType.VIDEO, FileType.AUDIO, FileType.DOCUMENT}: file_content = file_manager.to_prompt_message_content( file, image_detail_config=vision_detail_config ) - contents.append(file_content) - else: - plain_text = segment.markdown.strip() - if plain_text: - contents.append(TextPromptMessageContent(data=plain_text)) - prompt_message = _combine_message_content_with_role(contents=contents, role=message.role) - prompt_messages.append(prompt_message) + file_contents.append(file_content) + + # Create message with text from all segments + plain_text = segment_group.text + if plain_text: + prompt_message = _combine_message_content_with_role( + contents=[TextPromptMessageContent(data=plain_text)], role=message.role + ) + prompt_messages.append(prompt_message) + + if file_contents: + # Create message with image contents + prompt_message = _combine_message_content_with_role(contents=file_contents, role=message.role) + prompt_messages.append(prompt_message) return prompt_messages From c1aa55f3ea8ee674aa00689e97ba9ace60f5f295 Mon Sep 17 00:00:00 2001 From: Novice <857526207@qq.com> Date: Mon, 23 Dec 2024 14:32:11 +0800 Subject: [PATCH 111/138] fix: remove the unused retry index field (#11903) Signed-off-by: -LAN- Co-authored-by: Novice Lee Co-authored-by: -LAN- --- .../advanced_chat/generate_task_pipeline.py | 61 ++++---- .../apps/workflow/generate_task_pipeline.py | 62 ++++---- api/core/app/apps/workflow_app_runner.py | 92 ++++++------ api/core/app/entities/queue_entities.py | 68 ++++----- .../task_pipeline/workflow_cycle_manage.py | 5 +- api/core/helper/ssrf_proxy.py | 4 +- .../workflow/graph_engine/entities/event.py | 5 +- .../workflow/graph_engine/graph_engine.py | 7 +- api/core/workflow/nodes/event/event.py | 8 +- .../workflow/nodes/http_request/executor.py | 4 +- api/fields/workflow_run_fields.py | 13 +- ...dd_retry_index_field_to_node_execution_.py | 18 ++- ..._remove_workflow_node_executions_retry_.py | 34 +++++ api/models/model.py | 5 +- api/models/workflow.py | 1 - api/services/workflow_service.py | 137 ++++++------------ 16 files changed, 256 insertions(+), 268 deletions(-) create mode 100644 api/migrations/versions/2024_12_23_1154-d7999dfa4aae_remove_workflow_node_executions_retry_.py diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 7c8f9f3813..635e482ad9 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -180,7 +180,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc else: continue - raise Exception("Queue listening stopped unexpectedly.") + raise ValueError("queue listening stopped unexpectedly.") def _to_stream_response( self, generator: Generator[StreamResponse, None, None] @@ -291,9 +291,27 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc yield self._workflow_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run ) + elif isinstance( + event, + QueueNodeRetryEvent, + ): + if not workflow_run: + raise ValueError("workflow run not initialized.") + workflow_node_execution = self._handle_workflow_node_execution_retried( + workflow_run=workflow_run, event=event + ) + + response = self._workflow_node_retry_to_stream_response( + event=event, + task_id=self._application_generate_entity.task_id, + workflow_node_execution=workflow_node_execution, + ) + + if response: + yield response elif isinstance(event, QueueNodeStartedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") workflow_node_execution = self._handle_node_execution_start(workflow_run=workflow_run, event=event) @@ -331,63 +349,48 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc if response: yield response - elif isinstance( - event, - QueueNodeRetryEvent, - ): - workflow_node_execution = self._handle_workflow_node_execution_retried( - workflow_run=workflow_run, event=event - ) - response = self._workflow_node_retry_to_stream_response( - event=event, - task_id=self._application_generate_entity.task_id, - workflow_node_execution=workflow_node_execution, - ) - - if response: - yield response elif isinstance(event, QueueParallelBranchRunStartedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_parallel_branch_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueParallelBranchRunSucceededEvent | QueueParallelBranchRunFailedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_parallel_branch_finished_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationStartEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationNextEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_next_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationCompletedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_completed_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueWorkflowSucceededEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("workflow run not initialized.") workflow_run = self._handle_workflow_run_success( workflow_run=workflow_run, @@ -406,10 +409,10 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc self._queue_manager.publish(QueueAdvancedChatMessageEndEvent(), PublishFrom.TASK_PIPELINE) elif isinstance(event, QueueWorkflowPartialSuccessEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") workflow_run = self._handle_workflow_run_partial_success( workflow_run=workflow_run, @@ -429,10 +432,10 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc self._queue_manager.publish(QueueAdvancedChatMessageEndEvent(), PublishFrom.TASK_PIPELINE) elif isinstance(event, QueueWorkflowFailedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") workflow_run = self._handle_workflow_run_failed( workflow_run=workflow_run, @@ -522,7 +525,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc yield self._message_replace_to_stream_response(answer=event.text) elif isinstance(event, QueueAdvancedChatMessageEndEvent): if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") output_moderation_answer = self._handle_output_moderation_when_task_finished(self._task_state.answer) if output_moderation_answer: diff --git a/api/core/app/apps/workflow/generate_task_pipeline.py b/api/core/app/apps/workflow/generate_task_pipeline.py index d279002285..c47b38f560 100644 --- a/api/core/app/apps/workflow/generate_task_pipeline.py +++ b/api/core/app/apps/workflow/generate_task_pipeline.py @@ -155,7 +155,7 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa else: continue - raise Exception("Queue listening stopped unexpectedly.") + raise ValueError("queue listening stopped unexpectedly.") def _to_stream_response( self, generator: Generator[StreamResponse, None, None] @@ -218,7 +218,7 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa break else: yield MessageAudioStreamResponse(audio=audio_trunk.audio, task_id=task_id) - except Exception as e: + except Exception: logger.exception(f"Fails to get audio trunk, task_id: {task_id}") break if tts_publisher: @@ -254,9 +254,27 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa yield self._workflow_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run ) + elif isinstance( + event, + QueueNodeRetryEvent, + ): + if not workflow_run: + raise ValueError("workflow run not initialized.") + workflow_node_execution = self._handle_workflow_node_execution_retried( + workflow_run=workflow_run, event=event + ) + + response = self._workflow_node_retry_to_stream_response( + event=event, + task_id=self._application_generate_entity.task_id, + workflow_node_execution=workflow_node_execution, + ) + + if response: + yield response elif isinstance(event, QueueNodeStartedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") workflow_node_execution = self._handle_node_execution_start(workflow_run=workflow_run, event=event) @@ -289,64 +307,48 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa ) if node_failed_response: yield node_failed_response - elif isinstance( - event, - QueueNodeRetryEvent, - ): - workflow_node_execution = self._handle_workflow_node_execution_retried( - workflow_run=workflow_run, event=event - ) - - response = self._workflow_node_retry_to_stream_response( - event=event, - task_id=self._application_generate_entity.task_id, - workflow_node_execution=workflow_node_execution, - ) - - if response: - yield response elif isinstance(event, QueueParallelBranchRunStartedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_parallel_branch_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueParallelBranchRunSucceededEvent | QueueParallelBranchRunFailedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_parallel_branch_finished_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationStartEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_start_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationNextEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_next_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueIterationCompletedEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") yield self._workflow_iteration_completed_to_stream_response( task_id=self._application_generate_entity.task_id, workflow_run=workflow_run, event=event ) elif isinstance(event, QueueWorkflowSucceededEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") workflow_run = self._handle_workflow_run_success( workflow_run=workflow_run, @@ -366,10 +368,10 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa ) elif isinstance(event, QueueWorkflowPartialSuccessEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") workflow_run = self._handle_workflow_run_partial_success( workflow_run=workflow_run, @@ -390,10 +392,10 @@ class WorkflowAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCycleMa ) elif isinstance(event, QueueWorkflowFailedEvent | QueueStopEvent): if not workflow_run: - raise Exception("Workflow run not initialized.") + raise ValueError("workflow run not initialized.") if not graph_runtime_state: - raise Exception("Graph runtime state not initialized.") + raise ValueError("graph runtime state not initialized.") workflow_run = self._handle_workflow_run_failed( workflow_run=workflow_run, start_at=graph_runtime_state.start_at, diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py index 2fbf711175..885283504b 100644 --- a/api/core/app/apps/workflow_app_runner.py +++ b/api/core/app/apps/workflow_app_runner.py @@ -188,6 +188,41 @@ class WorkflowBasedAppRunner(AppRunner): ) elif isinstance(event, GraphRunFailedEvent): self._publish_event(QueueWorkflowFailedEvent(error=event.error, exceptions_count=event.exceptions_count)) + elif isinstance(event, NodeRunRetryEvent): + node_run_result = event.route_node_state.node_run_result + if node_run_result: + inputs = node_run_result.inputs + process_data = node_run_result.process_data + outputs = node_run_result.outputs + execution_metadata = node_run_result.metadata + else: + inputs = {} + process_data = {} + outputs = {} + execution_metadata = {} + self._publish_event( + QueueNodeRetryEvent( + node_execution_id=event.id, + node_id=event.node_id, + node_type=event.node_type, + node_data=event.node_data, + parallel_id=event.parallel_id, + parallel_start_node_id=event.parallel_start_node_id, + parent_parallel_id=event.parent_parallel_id, + parent_parallel_start_node_id=event.parent_parallel_start_node_id, + start_at=event.start_at, + node_run_index=event.route_node_state.index, + predecessor_node_id=event.predecessor_node_id, + in_iteration_id=event.in_iteration_id, + parallel_mode_run_id=event.parallel_mode_run_id, + inputs=inputs, + process_data=process_data, + outputs=outputs, + error=event.error, + execution_metadata=execution_metadata, + retry_index=event.retry_index, + ) + ) elif isinstance(event, NodeRunStartedEvent): self._publish_event( QueueNodeStartedEvent( @@ -207,6 +242,17 @@ class WorkflowBasedAppRunner(AppRunner): ) ) elif isinstance(event, NodeRunSucceededEvent): + node_run_result = event.route_node_state.node_run_result + if node_run_result: + inputs = node_run_result.inputs + process_data = node_run_result.process_data + outputs = node_run_result.outputs + execution_metadata = node_run_result.metadata + else: + inputs = {} + process_data = {} + outputs = {} + execution_metadata = {} self._publish_event( QueueNodeSucceededEvent( node_execution_id=event.id, @@ -218,18 +264,10 @@ class WorkflowBasedAppRunner(AppRunner): parent_parallel_id=event.parent_parallel_id, parent_parallel_start_node_id=event.parent_parallel_start_node_id, start_at=event.route_node_state.start_at, - inputs=event.route_node_state.node_run_result.inputs - if event.route_node_state.node_run_result - else {}, - process_data=event.route_node_state.node_run_result.process_data - if event.route_node_state.node_run_result - else {}, - outputs=event.route_node_state.node_run_result.outputs - if event.route_node_state.node_run_result - else {}, - execution_metadata=event.route_node_state.node_run_result.metadata - if event.route_node_state.node_run_result - else {}, + inputs=inputs, + process_data=process_data, + outputs=outputs, + execution_metadata=execution_metadata, in_iteration_id=event.in_iteration_id, ) ) @@ -422,36 +460,6 @@ class WorkflowBasedAppRunner(AppRunner): error=event.error if isinstance(event, IterationRunFailedEvent) else None, ) ) - elif isinstance(event, NodeRunRetryEvent): - self._publish_event( - QueueNodeRetryEvent( - node_execution_id=event.id, - node_id=event.node_id, - node_type=event.node_type, - node_data=event.node_data, - parallel_id=event.parallel_id, - parallel_start_node_id=event.parallel_start_node_id, - parent_parallel_id=event.parent_parallel_id, - parent_parallel_start_node_id=event.parent_parallel_start_node_id, - start_at=event.start_at, - inputs=event.route_node_state.node_run_result.inputs - if event.route_node_state.node_run_result - else {}, - process_data=event.route_node_state.node_run_result.process_data - if event.route_node_state.node_run_result - else {}, - outputs=event.route_node_state.node_run_result.outputs - if event.route_node_state.node_run_result - else {}, - error=event.error, - execution_metadata=event.route_node_state.node_run_result.metadata - if event.route_node_state.node_run_result - else {}, - in_iteration_id=event.in_iteration_id, - retry_index=event.retry_index, - start_index=event.start_index, - ) - ) def get_workflow(self, app_model: App, workflow_id: str) -> Optional[Workflow]: """ diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py index 49b7e80246..d73c2eb53b 100644 --- a/api/core/app/entities/queue_entities.py +++ b/api/core/app/entities/queue_entities.py @@ -1,3 +1,4 @@ +from collections.abc import Mapping from datetime import datetime from enum import Enum, StrEnum from typing import Any, Optional @@ -85,9 +86,9 @@ class QueueIterationStartEvent(AppQueueEvent): start_at: datetime node_run_index: int - inputs: Optional[dict[str, Any]] = None + inputs: Optional[Mapping[str, Any]] = None predecessor_node_id: Optional[str] = None - metadata: Optional[dict[str, Any]] = None + metadata: Optional[Mapping[str, Any]] = None class QueueIterationNextEvent(AppQueueEvent): @@ -139,9 +140,9 @@ class QueueIterationCompletedEvent(AppQueueEvent): start_at: datetime node_run_index: int - inputs: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None - metadata: Optional[dict[str, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None + metadata: Optional[Mapping[str, Any]] = None steps: int = 0 error: Optional[str] = None @@ -304,9 +305,9 @@ class QueueNodeSucceededEvent(AppQueueEvent): """iteration id if node is in iteration""" start_at: datetime - inputs: Optional[dict[str, Any]] = None - process_data: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + process_data: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None error: Optional[str] = None @@ -314,35 +315,18 @@ class QueueNodeSucceededEvent(AppQueueEvent): iteration_duration_map: Optional[dict[str, float]] = None -class QueueNodeRetryEvent(AppQueueEvent): +class QueueNodeRetryEvent(QueueNodeStartedEvent): """QueueNodeRetryEvent entity""" event: QueueEvent = QueueEvent.RETRY - node_execution_id: str - node_id: str - node_type: NodeType - node_data: BaseNodeData - parallel_id: Optional[str] = None - """parallel id if node is in parallel""" - parallel_start_node_id: Optional[str] = None - """parallel start node id if node is in parallel""" - parent_parallel_id: Optional[str] = None - """parent parallel id if node is in parallel""" - parent_parallel_start_node_id: Optional[str] = None - """parent parallel start node id if node is in parallel""" - in_iteration_id: Optional[str] = None - """iteration id if node is in iteration""" - start_at: datetime - - inputs: Optional[dict[str, Any]] = None - process_data: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None - execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + process_data: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None + execution_metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None error: str retry_index: int # retry index - start_index: int # start index class QueueNodeInIterationFailedEvent(AppQueueEvent): @@ -368,10 +352,10 @@ class QueueNodeInIterationFailedEvent(AppQueueEvent): """iteration id if node is in iteration""" start_at: datetime - inputs: Optional[dict[str, Any]] = None - process_data: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None - execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + process_data: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None + execution_metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None error: str @@ -399,10 +383,10 @@ class QueueNodeExceptionEvent(AppQueueEvent): """iteration id if node is in iteration""" start_at: datetime - inputs: Optional[dict[str, Any]] = None - process_data: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None - execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + process_data: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None + execution_metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None error: str @@ -430,10 +414,10 @@ class QueueNodeFailedEvent(AppQueueEvent): """iteration id if node is in iteration""" start_at: datetime - inputs: Optional[dict[str, Any]] = None - process_data: Optional[dict[str, Any]] = None - outputs: Optional[dict[str, Any]] = None - execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None + inputs: Optional[Mapping[str, Any]] = None + process_data: Optional[Mapping[str, Any]] = None + outputs: Optional[Mapping[str, Any]] = None + execution_metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None error: str diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index 0e6425fa14..5061804310 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -445,6 +445,7 @@ class WorkflowCycleManage: workflow_node_execution.workflow_id = workflow_run.workflow_id workflow_node_execution.triggered_from = WorkflowNodeExecutionTriggeredFrom.WORKFLOW_RUN.value workflow_node_execution.workflow_run_id = workflow_run.id + workflow_node_execution.predecessor_node_id = event.predecessor_node_id workflow_node_execution.node_execution_id = event.node_execution_id workflow_node_execution.node_id = event.node_id workflow_node_execution.node_type = event.node_type.value @@ -461,9 +462,11 @@ class WorkflowCycleManage: workflow_node_execution.execution_metadata = json.dumps( { NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id, + NodeRunMetadataKey.PARALLEL_MODE_RUN_ID: event.parallel_mode_run_id, + NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id, } ) - workflow_node_execution.index = event.start_index + workflow_node_execution.index = event.node_run_index db.session.add(workflow_node_execution) db.session.commit() diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py index ce15a9667d..424983a819 100644 --- a/api/core/helper/ssrf_proxy.py +++ b/api/core/helper/ssrf_proxy.py @@ -45,6 +45,7 @@ def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs): ) retries = 0 + stream = kwargs.pop("stream", False) while retries <= max_retries: try: if dify_config.SSRF_PROXY_ALL_URL: @@ -64,11 +65,12 @@ def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs): except httpx.RequestError as e: logging.warning(f"Request to URL {url} failed on attempt {retries + 1}: {e}") + if max_retries == 0: + raise retries += 1 if retries <= max_retries: time.sleep(BACKOFF_FACTOR * (2 ** (retries - 1))) - raise MaxRetriesExceededError(f"Reached maximum retries ({max_retries}) for URL {url}") diff --git a/api/core/workflow/graph_engine/entities/event.py b/api/core/workflow/graph_engine/entities/event.py index 9997153164..d591b68e7e 100644 --- a/api/core/workflow/graph_engine/entities/event.py +++ b/api/core/workflow/graph_engine/entities/event.py @@ -33,7 +33,7 @@ class GraphRunSucceededEvent(BaseGraphEvent): class GraphRunFailedEvent(BaseGraphEvent): error: str = Field(..., description="failed reason") - exceptions_count: Optional[int] = Field(description="exception count", default=0) + exceptions_count: int = Field(description="exception count", default=0) class GraphRunPartialSucceededEvent(BaseGraphEvent): @@ -97,11 +97,10 @@ class NodeInIterationFailedEvent(BaseNodeEvent): error: str = Field(..., description="error") -class NodeRunRetryEvent(BaseNodeEvent): +class NodeRunRetryEvent(NodeRunStartedEvent): error: str = Field(..., description="error") retry_index: int = Field(..., description="which retry attempt is about to be performed") start_at: datetime = Field(..., description="retry start time") - start_index: int = Field(..., description="retry start index") ########################################### diff --git a/api/core/workflow/graph_engine/graph_engine.py b/api/core/workflow/graph_engine/graph_engine.py index e292b09968..d7d33c65fc 100644 --- a/api/core/workflow/graph_engine/graph_engine.py +++ b/api/core/workflow/graph_engine/graph_engine.py @@ -641,7 +641,6 @@ class GraphEngine: run_result.status = WorkflowNodeExecutionStatus.SUCCEEDED if node_instance.should_retry and retries < max_retries: retries += 1 - self.graph_runtime_state.node_run_steps += 1 route_node_state.node_run_result = run_result yield NodeRunRetryEvent( id=node_instance.id, @@ -649,14 +648,14 @@ class GraphEngine: node_type=node_instance.node_type, node_data=node_instance.node_data, route_node_state=route_node_state, - error=run_result.error, - retry_index=retries, + predecessor_node_id=node_instance.previous_node_id, parallel_id=parallel_id, parallel_start_node_id=parallel_start_node_id, parent_parallel_id=parent_parallel_id, parent_parallel_start_node_id=parent_parallel_start_node_id, + error=run_result.error, + retry_index=retries, start_at=retry_start_at, - start_index=self.graph_runtime_state.node_run_steps, ) time.sleep(retry_interval) continue diff --git a/api/core/workflow/nodes/event/event.py b/api/core/workflow/nodes/event/event.py index 0dc35e7d77..137b476551 100644 --- a/api/core/workflow/nodes/event/event.py +++ b/api/core/workflow/nodes/event/event.py @@ -39,15 +39,9 @@ class RunRetryEvent(BaseModel): start_at: datetime = Field(..., description="Retry start time") -class SingleStepRetryEvent(BaseModel): +class SingleStepRetryEvent(NodeRunResult): """Single step retry event""" status: str = WorkflowNodeExecutionStatus.RETRY.value - inputs: dict | None = Field(..., description="input") - error: str = Field(..., description="error") - outputs: dict = Field(..., description="output") - retry_index: int = Field(..., description="Retry attempt number") - error: str = Field(..., description="error") elapsed_time: float = Field(..., description="elapsed time") - execution_metadata: dict | None = Field(..., description="execution metadata") diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index b96402a76a..3b7e193319 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -249,9 +249,7 @@ class Executor: # request_args = {k: v for k, v in request_args.items() if v is not None} try: response = getattr(ssrf_proxy, self.method)(**request_args) - except httpx.RequestError as e: - raise HttpRequestNodeError(str(e)) - except ssrf_proxy.MaxRetriesExceededError as e: + except (ssrf_proxy.MaxRetriesExceededError, httpx.RequestError) as e: raise HttpRequestNodeError(str(e)) return response diff --git a/api/fields/workflow_run_fields.py b/api/fields/workflow_run_fields.py index 7c01ffc2c6..74fdf8bd97 100644 --- a/api/fields/workflow_run_fields.py +++ b/api/fields/workflow_run_fields.py @@ -82,13 +82,15 @@ workflow_run_detail_fields = { } retry_event_field = { + "elapsed_time": fields.Float, + "status": fields.String, + "inputs": fields.Raw(attribute="inputs"), + "process_data": fields.Raw(attribute="process_data"), + "outputs": fields.Raw(attribute="outputs"), + "metadata": fields.Raw(attribute="metadata"), + "llm_usage": fields.Raw(attribute="llm_usage"), "error": fields.String, "retry_index": fields.Integer, - "inputs": fields.Raw(attribute="inputs"), - "elapsed_time": fields.Float, - "execution_metadata": fields.Raw(attribute="execution_metadata_dict"), - "status": fields.String, - "outputs": fields.Raw(attribute="outputs"), } @@ -112,7 +114,6 @@ workflow_run_node_execution_fields = { "created_by_account": fields.Nested(simple_account_fields, attribute="created_by_account", allow_null=True), "created_by_end_user": fields.Nested(simple_end_user_fields, attribute="created_by_end_user", allow_null=True), "finished_at": TimestampField, - "retry_events": fields.List(fields.Nested(retry_event_field)), } workflow_run_node_execution_list_fields = { diff --git a/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py b/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py index 3254c23c96..814dec423c 100644 --- a/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py +++ b/api/migrations/versions/2024_12_20_0628-e1944c35e15e_add_retry_index_field_to_node_execution_.py @@ -1,9 +1,7 @@ """add retry_index field to node-execution model - Revision ID: e1944c35e15e Revises: 11b07f66c737 Create Date: 2024-12-20 06:28:30.287197 - """ from alembic import op import models as models @@ -19,15 +17,21 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('workflow_node_executions', schema=None) as batch_op: - batch_op.add_column(sa.Column('retry_index', sa.Integer(), server_default=sa.text('0'), nullable=True)) + + # We don't need these fields anymore, but this file is already merged into the main branch, + # so we need to keep this file for the sake of history, and this change will be reverted in the next migration. + # with op.batch_alter_table('workflow_node_executions', schema=None) as batch_op: + # batch_op.add_column(sa.Column('retry_index', sa.Integer(), server_default=sa.text('0'), nullable=True)) + + pass # ### end Alembic commands ### def downgrade(): # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('workflow_node_executions', schema=None) as batch_op: - batch_op.drop_column('retry_index') + # with op.batch_alter_table('workflow_node_executions', schema=None) as batch_op: + # batch_op.drop_column('retry_index') + pass - # ### end Alembic commands ### + # ### end Alembic commands ### \ No newline at end of file diff --git a/api/migrations/versions/2024_12_23_1154-d7999dfa4aae_remove_workflow_node_executions_retry_.py b/api/migrations/versions/2024_12_23_1154-d7999dfa4aae_remove_workflow_node_executions_retry_.py new file mode 100644 index 0000000000..ea129d15f7 --- /dev/null +++ b/api/migrations/versions/2024_12_23_1154-d7999dfa4aae_remove_workflow_node_executions_retry_.py @@ -0,0 +1,34 @@ +"""remove workflow_node_executions.retry_index if exists + +Revision ID: d7999dfa4aae +Revises: e1944c35e15e +Create Date: 2024-12-23 11:54:15.344543 + +""" +from alembic import op +import models as models +import sqlalchemy as sa +from sqlalchemy import inspect + + +# revision identifiers, used by Alembic. +revision = 'd7999dfa4aae' +down_revision = 'e1944c35e15e' +branch_labels = None +depends_on = None + + +def upgrade(): + # Check if column exists before attempting to remove it + conn = op.get_bind() + inspector = inspect(conn) + has_column = 'retry_index' in [col['name'] for col in inspector.get_columns('workflow_node_executions')] + + if has_column: + with op.batch_alter_table('workflow_node_executions', schema=None) as batch_op: + batch_op.drop_column('retry_index') + + +def downgrade(): + # No downgrade needed as we don't want to restore the column + pass diff --git a/api/models/model.py b/api/models/model.py index 04bb0a947a..1417298c79 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -4,7 +4,7 @@ import uuid from collections.abc import Mapping from datetime import datetime from enum import Enum, StrEnum -from typing import Any, Literal, Optional +from typing import TYPE_CHECKING, Any, Literal, Optional import sqlalchemy as sa from flask import request @@ -24,6 +24,9 @@ from .account import Account, Tenant from .engine import db from .types import StringUUID +if TYPE_CHECKING: + from .workflow import Workflow + class DifySetup(db.Model): __tablename__ = "dify_setups" diff --git a/api/models/workflow.py b/api/models/workflow.py index 7896339f37..d5be949bf4 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -641,7 +641,6 @@ class WorkflowNodeExecution(db.Model): created_by_role = db.Column(db.String(255), nullable=False) created_by = db.Column(StringUUID, nullable=False) finished_at = db.Column(db.DateTime) - retry_index = db.Column(db.Integer, server_default=db.text("0")) @property def created_by_account(self): diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index ead552d6c2..84768d5af0 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -15,7 +15,6 @@ from core.workflow.nodes.base.entities import BaseNodeData from core.workflow.nodes.base.node import BaseNode from core.workflow.nodes.enums import ErrorStrategy from core.workflow.nodes.event import RunCompletedEvent -from core.workflow.nodes.event.event import SingleStepRetryEvent from core.workflow.nodes.node_mapping import LATEST_VERSION, NODE_TYPE_CLASSES_MAPPING from core.workflow.workflow_entry import WorkflowEntry from events.app_event import app_draft_workflow_was_synced, app_published_workflow_was_updated @@ -221,99 +220,56 @@ class WorkflowService: # run draft workflow node start_at = time.perf_counter() - retries = 0 - max_retries = 0 - should_retry = True - retry_events = [] try: - while retries <= max_retries and should_retry: - retry_start_at = time.perf_counter() - node_instance, generator = WorkflowEntry.single_step_run( - workflow=draft_workflow, - node_id=node_id, - user_inputs=user_inputs, - user_id=account.id, - ) - node_instance = cast(BaseNode[BaseNodeData], node_instance) - max_retries = ( - node_instance.node_data.retry_config.max_retries if node_instance.node_data.retry_config else 0 - ) - retry_interval = node_instance.node_data.retry_config.retry_interval_seconds - node_run_result: NodeRunResult | None = None - for event in generator: - if isinstance(event, RunCompletedEvent): - node_run_result = event.run_result + node_instance, generator = WorkflowEntry.single_step_run( + workflow=draft_workflow, + node_id=node_id, + user_inputs=user_inputs, + user_id=account.id, + ) + node_instance = cast(BaseNode[BaseNodeData], node_instance) + node_run_result: NodeRunResult | None = None + for event in generator: + if isinstance(event, RunCompletedEvent): + node_run_result = event.run_result - # sign output files - node_run_result.outputs = WorkflowEntry.handle_special_values(node_run_result.outputs) - break + # sign output files + node_run_result.outputs = WorkflowEntry.handle_special_values(node_run_result.outputs) + break - if not node_run_result: - raise ValueError("Node run failed with no run result") - # single step debug mode error handling return - if node_run_result.status == WorkflowNodeExecutionStatus.FAILED: - if ( - retries == max_retries - and node_instance.node_type == NodeType.HTTP_REQUEST - and node_run_result.outputs - and not node_instance.should_continue_on_error - ): - node_run_result.status = WorkflowNodeExecutionStatus.SUCCEEDED - should_retry = False - else: - if node_instance.should_retry: - node_run_result.status = WorkflowNodeExecutionStatus.RETRY - retries += 1 - node_run_result.retry_index = retries - retry_events.append( - SingleStepRetryEvent( - inputs=WorkflowEntry.handle_special_values(node_run_result.inputs) - if node_run_result.inputs - else None, - error=node_run_result.error, - outputs=WorkflowEntry.handle_special_values(node_run_result.outputs) - if node_run_result.outputs - else None, - retry_index=node_run_result.retry_index, - elapsed_time=time.perf_counter() - retry_start_at, - execution_metadata=WorkflowEntry.handle_special_values(node_run_result.metadata) - if node_run_result.metadata - else None, - ) - ) - time.sleep(retry_interval) - else: - should_retry = False - if node_instance.should_continue_on_error: - node_error_args = { - "status": WorkflowNodeExecutionStatus.EXCEPTION, - "error": node_run_result.error, - "inputs": node_run_result.inputs, - "metadata": {"error_strategy": node_instance.node_data.error_strategy}, - } - if node_instance.node_data.error_strategy is ErrorStrategy.DEFAULT_VALUE: - node_run_result = NodeRunResult( - **node_error_args, - outputs={ - **node_instance.node_data.default_value_dict, - "error_message": node_run_result.error, - "error_type": node_run_result.error_type, - }, - ) - else: - node_run_result = NodeRunResult( - **node_error_args, - outputs={ - "error_message": node_run_result.error, - "error_type": node_run_result.error_type, - }, - ) - run_succeeded = node_run_result.status in ( - WorkflowNodeExecutionStatus.SUCCEEDED, - WorkflowNodeExecutionStatus.EXCEPTION, - ) - error = node_run_result.error if not run_succeeded else None + if not node_run_result: + raise ValueError("Node run failed with no run result") + # single step debug mode error handling return + if node_run_result.status == WorkflowNodeExecutionStatus.FAILED and node_instance.should_continue_on_error: + node_error_args = { + "status": WorkflowNodeExecutionStatus.EXCEPTION, + "error": node_run_result.error, + "inputs": node_run_result.inputs, + "metadata": {"error_strategy": node_instance.node_data.error_strategy}, + } + if node_instance.node_data.error_strategy is ErrorStrategy.DEFAULT_VALUE: + node_run_result = NodeRunResult( + **node_error_args, + outputs={ + **node_instance.node_data.default_value_dict, + "error_message": node_run_result.error, + "error_type": node_run_result.error_type, + }, + ) + else: + node_run_result = NodeRunResult( + **node_error_args, + outputs={ + "error_message": node_run_result.error, + "error_type": node_run_result.error_type, + }, + ) + run_succeeded = node_run_result.status in ( + WorkflowNodeExecutionStatus.SUCCEEDED, + WorkflowNodeExecutionStatus.EXCEPTION, + ) + error = node_run_result.error if not run_succeeded else None except WorkflowNodeRunFailedError as e: node_instance = e.node_instance run_succeeded = False @@ -362,7 +318,6 @@ class WorkflowService: db.session.add(workflow_node_execution) db.session.commit() - workflow_node_execution.retry_events = retry_events return workflow_node_execution From 453f324f54db9c1099372806512987a97047cd07 Mon Sep 17 00:00:00 2001 From: Novice <857526207@qq.com> Date: Mon, 23 Dec 2024 15:06:01 +0800 Subject: [PATCH 112/138] fix: retry node in iteration logs wrong (#11995) Co-authored-by: Novice Lee --- .../app/task_pipeline/workflow_cycle_manage.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index 5061804310..72e4c796c3 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -438,6 +438,16 @@ class WorkflowCycleManage: elapsed_time = (finished_at - created_at).total_seconds() inputs = WorkflowEntry.handle_special_values(event.inputs) outputs = WorkflowEntry.handle_special_values(event.outputs) + origin_metadata = { + NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id, + NodeRunMetadataKey.PARALLEL_MODE_RUN_ID: event.parallel_mode_run_id, + } + merged_metadata = ( + {**jsonable_encoder(event.execution_metadata), **origin_metadata} + if event.execution_metadata is not None + else origin_metadata + ) + execution_metadata = json.dumps(merged_metadata) workflow_node_execution = WorkflowNodeExecution() workflow_node_execution.tenant_id = workflow_run.tenant_id @@ -459,13 +469,7 @@ class WorkflowCycleManage: workflow_node_execution.error = event.error workflow_node_execution.inputs = json.dumps(inputs) if inputs else None workflow_node_execution.outputs = json.dumps(outputs) if outputs else None - workflow_node_execution.execution_metadata = json.dumps( - { - NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id, - NodeRunMetadataKey.PARALLEL_MODE_RUN_ID: event.parallel_mode_run_id, - NodeRunMetadataKey.ITERATION_ID: event.in_iteration_id, - } - ) + workflow_node_execution.execution_metadata = execution_metadata workflow_node_execution.index = event.node_run_index db.session.add(workflow_node_execution) From 70dd69d533b58f4837eea00e87a789b8653931f9 Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 23 Dec 2024 15:09:02 +0800 Subject: [PATCH 113/138] fix: Multiple Paths Between IF/ELSE Branches Invalidate Conditions (#11544) Signed-off-by: yihong0618 --- api/core/workflow/nodes/answer/answer_stream_generate_router.py | 2 ++ api/core/workflow/nodes/end/end_stream_generate_router.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/api/core/workflow/nodes/answer/answer_stream_generate_router.py b/api/core/workflow/nodes/answer/answer_stream_generate_router.py index 1b948bf592..7d652d39f7 100644 --- a/api/core/workflow/nodes/answer/answer_stream_generate_router.py +++ b/api/core/workflow/nodes/answer/answer_stream_generate_router.py @@ -147,6 +147,8 @@ class AnswerStreamGeneratorRouter: reverse_edges = reverse_edge_mapping.get(current_node_id, []) for edge in reverse_edges: source_node_id = edge.source_node_id + if source_node_id not in node_id_config_mapping: + continue source_node_type = node_id_config_mapping[source_node_id].get("data", {}).get("type") source_node_data = node_id_config_mapping[source_node_id].get("data", {}) if ( diff --git a/api/core/workflow/nodes/end/end_stream_generate_router.py b/api/core/workflow/nodes/end/end_stream_generate_router.py index ea8b6b5042..0db1ba9f09 100644 --- a/api/core/workflow/nodes/end/end_stream_generate_router.py +++ b/api/core/workflow/nodes/end/end_stream_generate_router.py @@ -135,6 +135,8 @@ class EndStreamGeneratorRouter: reverse_edges = reverse_edge_mapping.get(current_node_id, []) for edge in reverse_edges: source_node_id = edge.source_node_id + if source_node_id not in node_id_config_mapping: + continue source_node_type = node_id_config_mapping[source_node_id].get("data", {}).get("type") if source_node_type in { NodeType.IF_ELSE.value, From 03548cdfbc0ed919c7db3d1626498727f022adc8 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 15:23:11 +0800 Subject: [PATCH 114/138] fix: handle broader request exceptions in OAuth process (#11997) Signed-off-by: -LAN- --- api/controllers/console/auth/oauth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/controllers/console/auth/oauth.py b/api/controllers/console/auth/oauth.py index 5de8c6766d..b9188aa079 100644 --- a/api/controllers/console/auth/oauth.py +++ b/api/controllers/console/auth/oauth.py @@ -76,7 +76,7 @@ class OAuthCallback(Resource): try: token = oauth_provider.get_access_token(code) user_info = oauth_provider.get_user_info(token) - except requests.exceptions.HTTPError as e: + except requests.exceptions.RequestException as e: logging.exception(f"An error occurred during the OAuth process with {provider}: {e.response.text}") return {"error": "OAuth process failed"}, 400 From 4e3d732934294ab2528b09855239375cc118b0cc Mon Sep 17 00:00:00 2001 From: Shun Miyazawa <34241526+miya@users.noreply.github.com> Date: Mon, 23 Dec 2024 16:27:49 +0900 Subject: [PATCH 115/138] feat: Warning on invite modal when mail setup is incomplete (#11809) --- .../account-setting/members-page/index.tsx | 3 ++- .../members-page/invite-modal/index.tsx | 21 ++++++++++++++++++- web/i18n/de-DE/common.ts | 1 + web/i18n/en-US/common.ts | 1 + web/i18n/es-ES/common.ts | 1 + web/i18n/fa-IR/common.ts | 1 + web/i18n/fr-FR/common.ts | 1 + web/i18n/hi-IN/common.ts | 1 + web/i18n/it-IT/common.ts | 1 + web/i18n/ja-JP/common.ts | 1 + web/i18n/ko-KR/common.ts | 1 + web/i18n/pl-PL/common.ts | 1 + web/i18n/pt-BR/common.ts | 1 + web/i18n/ro-RO/common.ts | 1 + web/i18n/ru-RU/common.ts | 1 + web/i18n/sl-SI/common.ts | 1 + web/i18n/th-TH/common.ts | 1 + web/i18n/tr-TR/common.ts | 1 + web/i18n/uk-UA/common.ts | 1 + web/i18n/vi-VN/common.ts | 1 + web/i18n/zh-Hans/common.ts | 1 + web/i18n/zh-Hant/common.ts | 1 + 22 files changed, 42 insertions(+), 2 deletions(-) diff --git a/web/app/components/header/account-setting/members-page/index.tsx b/web/app/components/header/account-setting/members-page/index.tsx index c2b722b4a7..808da454d1 100644 --- a/web/app/components/header/account-setting/members-page/index.tsx +++ b/web/app/components/header/account-setting/members-page/index.tsx @@ -34,7 +34,7 @@ const MembersPage = () => { } const { locale } = useContext(I18n) - const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext() + const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager, systemFeatures } = useAppContext() const { data, mutate } = useSWR({ url: '/workspaces/current/members' }, fetchMembers) const [inviteModalVisible, setInviteModalVisible] = useState(false) const [invitationResults, setInvitationResults] = useState([]) @@ -122,6 +122,7 @@ const MembersPage = () => { { inviteModalVisible && ( setInviteModalVisible(false)} onSend={(invitationResults) => { setInvitedModalVisible(true) diff --git a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx index 7d43495362..197e3ee867 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx @@ -4,6 +4,7 @@ import { useContext } from 'use-context-selector' import { XMarkIcon } from '@heroicons/react/24/outline' import { useTranslation } from 'react-i18next' import { ReactMultiEmail } from 'react-multi-email' +import { RiErrorWarningFill } from '@remixicon/react' import RoleSelector from './role-selector' import s from './index.module.css' import cn from '@/utils/classnames' @@ -17,11 +18,13 @@ import I18n from '@/context/i18n' import 'react-multi-email/dist/style.css' type IInviteModalProps = { + isEmailSetup: boolean onCancel: () => void onSend: (invitationResults: InvitationResult[]) => void } const InviteModal = ({ + isEmailSetup, onCancel, onSend, }: IInviteModalProps) => { @@ -59,7 +62,23 @@ const InviteModal = ({
{t('common.members.inviteTeamMember')}
-
{t('common.members.inviteTeamMemberTip')}
+
{t('common.members.inviteTeamMemberTip')}
+ {!isEmailSetup && ( +
+
+
+
+
+ +
+
+ {t('common.members.emailNotSetup')} +
+
+
+
+ )} +
{t('common.members.email')}
diff --git a/web/i18n/de-DE/common.ts b/web/i18n/de-DE/common.ts index 1d7ca955fa..f438b4f018 100644 --- a/web/i18n/de-DE/common.ts +++ b/web/i18n/de-DE/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Kann Apps erstellen & bearbeiten', inviteTeamMember: 'Teammitglied hinzufügen', inviteTeamMemberTip: 'Sie können direkt nach der Anmeldung auf Ihre Teamdaten zugreifen.', + emailNotSetup: 'E-Mail-Server ist nicht eingerichtet, daher können keine Einladungs-E-Mails versendet werden. Bitte informieren Sie die Benutzer über den Einladungslink, der nach der Einladung ausgestellt wird.', email: 'E-Mail', emailInvalid: 'Ungültiges E-Mail-Format', emailPlaceholder: 'Bitte E-Mails eingeben', diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index ce341a9148..ea0e4a88aa 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'Only can manage the knowledge base', inviteTeamMember: 'Add team member', inviteTeamMemberTip: 'They can access your team data directly after signing in.', + emailNotSetup: 'Email server is not set up, so invitation emails cannot be sent. Please notify users of the invitation link that will be issued after invitation instead.', email: 'Email', emailInvalid: 'Invalid Email Format', emailPlaceholder: 'Please input emails', diff --git a/web/i18n/es-ES/common.ts b/web/i18n/es-ES/common.ts index cc9fb47329..2540632758 100644 --- a/web/i18n/es-ES/common.ts +++ b/web/i18n/es-ES/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'Solo puede administrar la base de conocimiento', inviteTeamMember: 'Agregar miembro del equipo', inviteTeamMemberTip: 'Pueden acceder a tus datos del equipo directamente después de iniciar sesión.', + emailNotSetup: 'El servidor de correo no está configurado, por lo que no se pueden enviar correos de invitación. En su lugar, notifique a los usuarios el enlace de invitación que se emitirá después de la invitación.', email: 'Correo electrónico', emailInvalid: 'Formato de correo electrónico inválido', emailPlaceholder: 'Por favor ingresa correos electrónicos', diff --git a/web/i18n/fa-IR/common.ts b/web/i18n/fa-IR/common.ts index 2da6cdee8b..deab852ddb 100644 --- a/web/i18n/fa-IR/common.ts +++ b/web/i18n/fa-IR/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'فقط می‌تواند پایگاه دانش را مدیریت کند', inviteTeamMember: 'افزودن عضو تیم', inviteTeamMemberTip: 'آنها می‌توانند پس از ورود به سیستم، مستقیماً به داده‌های تیم شما دسترسی پیدا کنند.', + emailNotSetup: 'سرور ایمیل راه‌اندازی نشده است، بنابراین ایمیل‌های دعوت نمی‌توانند ارسال شوند. لطفاً کاربران را از لینک دعوت که پس از دعوت صادر خواهد شد مطلع کنید。', email: 'ایمیل', emailInvalid: 'فرمت ایمیل نامعتبر است', emailPlaceholder: 'لطفاً ایمیل‌ها را وارد کنید', diff --git a/web/i18n/fr-FR/common.ts b/web/i18n/fr-FR/common.ts index 326572916c..25142c11cc 100644 --- a/web/i18n/fr-FR/common.ts +++ b/web/i18n/fr-FR/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Peut construire des applications, mais ne peut pas gérer les paramètres de l\'équipe', inviteTeamMember: 'Ajouter un membre de l\'équipe', inviteTeamMemberTip: 'Ils peuvent accéder directement à vos données d\'équipe après s\'être connectés.', + emailNotSetup: 'Le serveur de messagerie n\'est pas configuré, les e-mails d\'invitation ne peuvent donc pas être envoyés. Veuillez informer les utilisateurs du lien d\'invitation qui sera émis après l\'invitation.', email: 'Courrier électronique', emailInvalid: 'Format de courriel invalide', emailPlaceholder: 'Veuillez entrer des emails', diff --git a/web/i18n/hi-IN/common.ts b/web/i18n/hi-IN/common.ts index a2c178cb18..aabcfc86e6 100644 --- a/web/i18n/hi-IN/common.ts +++ b/web/i18n/hi-IN/common.ts @@ -204,6 +204,7 @@ const translation = { inviteTeamMember: 'टीम सदस्य जोड़ें', inviteTeamMemberTip: 'वे साइन इन करने के बाद सीधे आपकी टीम डेटा तक पहुंच सकते हैं।', + emailNotSetup: 'ईमेल सर्वर सेट नहीं है, इसलिए आमंत्रण ईमेल नहीं भेजे जा सकते। कृपया उपयोगकर्ताओं को आमंत्रण के बाद जारी किए जाने वाले आमंत्रण लिंक के बारे में सूचित करें。', email: 'ईमेल', emailInvalid: 'अवैध ईमेल प्रारूप', emailPlaceholder: 'कृपया ईमेल दर्ज करें', diff --git a/web/i18n/it-IT/common.ts b/web/i18n/it-IT/common.ts index 35a01d7114..4cee6dec50 100644 --- a/web/i18n/it-IT/common.ts +++ b/web/i18n/it-IT/common.ts @@ -208,6 +208,7 @@ const translation = { inviteTeamMember: 'Aggiungi membro del team', inviteTeamMemberTip: 'Potranno accedere ai dati del tuo team direttamente dopo aver effettuato l\'accesso.', + emailNotSetup: 'Il server email non è configurato, quindi non è possibile inviare email di invito. Si prega di notificare agli utenti il link di invito che verrà emesso dopo l\'invito.', email: 'Email', emailInvalid: 'Formato Email non valido', emailPlaceholder: 'Per favore inserisci le email', diff --git a/web/i18n/ja-JP/common.ts b/web/i18n/ja-JP/common.ts index fa3fb223f4..9c23cb6f16 100644 --- a/web/i18n/ja-JP/common.ts +++ b/web/i18n/ja-JP/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'ナレッジベースのみを管理できる', inviteTeamMember: 'チームメンバーを招待する', inviteTeamMemberTip: '彼らはサインイン後、直接あなた様のチームデータにアクセスできます。', + emailNotSetup: 'メールサーバーがセットアップされていないので、招待メールを送信することはできません。代わりに招待後に発行される招待リンクをユーザーに通知してください。', email: 'メール', emailInvalid: '無効なメール形式', emailPlaceholder: 'メールを入力してください', diff --git a/web/i18n/ko-KR/common.ts b/web/i18n/ko-KR/common.ts index ce860e000e..a599aa9bd1 100644 --- a/web/i18n/ko-KR/common.ts +++ b/web/i18n/ko-KR/common.ts @@ -187,6 +187,7 @@ const translation = { editorTip: '앱 빌드만 가능하고 팀 설정 관리 불가능', inviteTeamMember: '팀 멤버 초대', inviteTeamMemberTip: '로그인 후에 바로 팀 데이터에 액세스할 수 있습니다.', + emailNotSetup: '이메일 서버가 설정되지 않아 초대 이메일을 보낼 수 없습니다. 대신 초대 후 발급되는 초대 링크를 사용자에게 알려주세요.', email: '이메일', emailInvalid: '유효하지 않은 이메일 형식', emailPlaceholder: '이메일 입력', diff --git a/web/i18n/pl-PL/common.ts b/web/i18n/pl-PL/common.ts index baaf5292c3..69441dbab3 100644 --- a/web/i18n/pl-PL/common.ts +++ b/web/i18n/pl-PL/common.ts @@ -198,6 +198,7 @@ const translation = { inviteTeamMember: 'Dodaj członka zespołu', inviteTeamMemberTip: 'Mogą uzyskać bezpośredni dostęp do danych Twojego zespołu po zalogowaniu.', + emailNotSetup: 'Serwer poczty nie jest skonfigurowany, więc nie można wysyłać zaproszeń e-mail. Proszę powiadomić użytkowników o linku do zaproszenia, który zostanie wydany po zaproszeniu.', email: 'Email', emailInvalid: 'Nieprawidłowy format e-maila', emailPlaceholder: 'Proszę podać adresy e-mail', diff --git a/web/i18n/pt-BR/common.ts b/web/i18n/pt-BR/common.ts index a2c74a7bee..6f66e65878 100644 --- a/web/i18n/pt-BR/common.ts +++ b/web/i18n/pt-BR/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Pode editar aplicativos, mas não pode gerenciar configurações da equipe', inviteTeamMember: 'Adicionar membro da equipe', inviteTeamMemberTip: 'Eles podem acessar os dados da sua equipe diretamente após fazer login.', + emailNotSetup: 'O servidor de e-mail não está configurado, então os e-mails de convite não podem ser enviados. Por favor, notifique os usuários sobre o link de convite que será emitido após o convite.', email: 'E-mail', emailInvalid: 'Formato de e-mail inválido', emailPlaceholder: 'Por favor, insira e-mails', diff --git a/web/i18n/ro-RO/common.ts b/web/i18n/ro-RO/common.ts index 27a0ab6bf3..0badaf5a13 100644 --- a/web/i18n/ro-RO/common.ts +++ b/web/i18n/ro-RO/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Poate construi aplicații, dar nu poate gestiona setările echipei', inviteTeamMember: 'Adaugă membru în echipă', inviteTeamMemberTip: 'Pot accesa direct datele echipei dvs. după autentificare.', + emailNotSetup: 'Serverul de e-mail nu este configurat, astfel încât e-mailurile de invitație nu pot fi trimise. Vă rugăm să notificați utilizatorii despre linkul de invitație care va fi emis după invitație.', email: 'Email', emailInvalid: 'Format de email invalid', emailPlaceholder: 'Vă rugăm să introduceți emailuri', diff --git a/web/i18n/ru-RU/common.ts b/web/i18n/ru-RU/common.ts index 6d9edf97c1..64a7c9375d 100644 --- a/web/i18n/ru-RU/common.ts +++ b/web/i18n/ru-RU/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'Может управлять только базой знаний', inviteTeamMember: 'Добавить участника команды', inviteTeamMemberTip: 'Они могут получить доступ к данным вашей команды сразу после входа в систему.', + emailNotSetup: 'Почтовый сервер не настроен, поэтому приглашения по электронной почте не могут быть отправлены. Пожалуйста, уведомите пользователей о ссылке для приглашения, которая будет выдана после приглашения.', email: 'Электронная почта', emailInvalid: 'Неверный формат электронной почты', emailPlaceholder: 'Пожалуйста, введите адреса электронной почты', diff --git a/web/i18n/sl-SI/common.ts b/web/i18n/sl-SI/common.ts index dc399bd3a4..0c5d1dfc4b 100644 --- a/web/i18n/sl-SI/common.ts +++ b/web/i18n/sl-SI/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'Lahko upravlja samo bazo znanja', inviteTeamMember: 'Dodaj člana ekipe', inviteTeamMemberTip: 'Do vaših podatkov bo lahko dostopal takoj po prijavi.', + emailNotSetup: 'E-poštni strežnik ni nastavljen, zato vabil po e-pošti ni mogoče poslati. Prosimo, obvestite uporabnike o povezavi za povabilo, ki bo izdana po povabilu.', email: 'E-pošta', emailInvalid: 'Neveljaven format e-pošte', emailPlaceholder: 'Vnesite e-poštne naslove', diff --git a/web/i18n/th-TH/common.ts b/web/i18n/th-TH/common.ts index 82eddae723..6aa1b30610 100644 --- a/web/i18n/th-TH/common.ts +++ b/web/i18n/th-TH/common.ts @@ -194,6 +194,7 @@ const translation = { datasetOperatorTip: 'สามารถจัดการฐานความรู้ได้เท่านั้น', inviteTeamMember: 'เพิ่มสมาชิกในทีม', inviteTeamMemberTip: 'พวกเขาสามารถเข้าถึงข้อมูลทีมของคุณได้โดยตรงหลังจากลงชื่อเข้าใช้', + emailNotSetup: 'เซิร์ฟเวอร์อีเมลไม่ได้ตั้งค่าไว้ จึงไม่สามารถส่งอีเมลเชิญได้ กรุณาแจ้งผู้ใช้เกี่ยวกับลิงก์เชิญที่จะออกหลังจากการเชิญแทน', email: 'อีเมล', emailInvalid: 'รูปแบบอีเมลไม่ถูกต้อง', emailPlaceholder: 'กรุณากรอกอีเมล', diff --git a/web/i18n/tr-TR/common.ts b/web/i18n/tr-TR/common.ts index 320517925a..9792f07e18 100644 --- a/web/i18n/tr-TR/common.ts +++ b/web/i18n/tr-TR/common.ts @@ -199,6 +199,7 @@ const translation = { datasetOperatorTip: 'Sadece bilgi tabanını yönetebilir', inviteTeamMember: 'Takım Üyesi Ekle', inviteTeamMemberTip: 'Giriş yaptıktan sonra takım verilerinize doğrudan erişebilirler.', + emailNotSetup: 'E-posta sunucusu kurulu değil, bu nedenle davet e-postaları gönderilemiyor. Lütfen kullanıcıları davetten sonra verilecek davet bağlantısı hakkında bilgilendirin.', email: 'E-posta', emailInvalid: 'Geçersiz E-posta Formatı', emailPlaceholder: 'Lütfen e-postaları girin', diff --git a/web/i18n/uk-UA/common.ts b/web/i18n/uk-UA/common.ts index 6bf6dafc4f..fbe9b67750 100644 --- a/web/i18n/uk-UA/common.ts +++ b/web/i18n/uk-UA/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Може створювати програми, але не може керувати налаштуваннями команди', inviteTeamMember: 'Додати учасника команди', inviteTeamMemberTip: 'Вони зможуть отримати доступ до даних вашої команди безпосередньо після входу.', + emailNotSetup: 'Поштовий сервер не налаштований, тому запрошення електронною поштою не можуть бути надіслані. Будь ласка, повідомте користувачів про посилання для запрошення, яке буде видано після запрошення.', email: 'Електронна пошта', emailInvalid: 'Недійсний формат електронної пошти', emailPlaceholder: 'Будь ласка, введіть адресу електронної пошти', diff --git a/web/i18n/vi-VN/common.ts b/web/i18n/vi-VN/common.ts index bf5339f40e..8bafd86854 100644 --- a/web/i18n/vi-VN/common.ts +++ b/web/i18n/vi-VN/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: 'Có thể xây dựng ứng dụng, không thể quản lý cài đặt nhóm', inviteTeamMember: 'Mời thành viên nhóm', inviteTeamMemberTip: 'Sau khi đăng nhập, họ có thể truy cập trực tiếp vào dữ liệu nhóm của bạn.', + emailNotSetup: 'Máy chủ email chưa được thiết lập, vì vậy không thể gửi email mời. Vui lòng thông báo cho người dùng về liên kết mời sẽ được phát hành sau khi mời.', email: 'Email', emailInvalid: 'Định dạng Email không hợp lệ', emailPlaceholder: 'Vui lòng nhập email', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 70caaa976a..96e08a9337 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -197,6 +197,7 @@ const translation = { datasetOperatorTip: '只能管理知识库', inviteTeamMember: '添加团队成员', inviteTeamMemberTip: '对方在登录后可以访问你的团队数据。', + emailNotSetup: '由于邮件服务器未设置,无法发送邀请邮件。请将邀请后生成的邀请链接通知用户。', email: '邮箱', emailInvalid: '邮箱格式无效', emailPlaceholder: '输入邮箱', diff --git a/web/i18n/zh-Hant/common.ts b/web/i18n/zh-Hant/common.ts index 09c1e9d839..8340650993 100644 --- a/web/i18n/zh-Hant/common.ts +++ b/web/i18n/zh-Hant/common.ts @@ -191,6 +191,7 @@ const translation = { editorTip: '能夠建立並編輯應用程式,不能管理團隊設定', inviteTeamMember: '新增團隊成員', inviteTeamMemberTip: '對方在登入後可以訪問你的團隊資料。', + emailNotSetup: '由於郵件伺服器未設置,無法發送邀請郵件。請將邀請後生成的邀請連結通知用戶。', email: '郵箱', emailInvalid: '郵箱格式無效', emailPlaceholder: '輸入郵箱', From 8978a6a3ffba04db8aba8f5c8fd08c1c54f9a37c Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 15:30:58 +0800 Subject: [PATCH 116/138] fix: remove unused credential validation logic in VectorizerProvider (#12000) Signed-off-by: -LAN- --- .../provider/builtin/vectorizer/vectorizer.py | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/api/core/tools/provider/builtin/vectorizer/vectorizer.py b/api/core/tools/provider/builtin/vectorizer/vectorizer.py index 211ec78f4d..9d7613f8ea 100644 --- a/api/core/tools/provider/builtin/vectorizer/vectorizer.py +++ b/api/core/tools/provider/builtin/vectorizer/vectorizer.py @@ -1,32 +1,8 @@ from typing import Any -from core.file import FileTransferMethod, FileType -from core.tools.errors import ToolProviderCredentialValidationError -from core.tools.provider.builtin.vectorizer.tools.vectorizer import VectorizerTool from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController -from factories import file_factory class VectorizerProvider(BuiltinToolProviderController): def _validate_credentials(self, credentials: dict[str, Any]) -> None: - mapping = { - "transfer_method": FileTransferMethod.TOOL_FILE, - "type": FileType.IMAGE, - "id": "test_id", - "url": "https://cloud.dify.ai/logo/logo-site.png", - } - test_img = file_factory.build_from_mapping( - mapping=mapping, - tenant_id="__test_123", - ) - try: - VectorizerTool().fork_tool_runtime( - runtime={ - "credentials": credentials, - } - ).invoke( - user_id="", - tool_parameters={"mode": "test", "image": test_img}, - ) - except Exception as e: - raise ToolProviderCredentialValidationError(str(e)) + return From 9cfd1c67b6c16ec199b1be740a91a47fd57791c6 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 15:52:50 +0800 Subject: [PATCH 117/138] fix: Introduce ArrayVariable and update iteration node to handle it (#12001) Signed-off-by: -LAN- --- api/core/variables/__init__.py | 2 ++ api/core/variables/variables.py | 13 +++++++++---- .../workflow/nodes/iteration/iteration_node.py | 15 +++++++++------ 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/api/core/variables/__init__.py b/api/core/variables/__init__.py index 2b1a58f93a..7a1cbf9940 100644 --- a/api/core/variables/__init__.py +++ b/api/core/variables/__init__.py @@ -21,6 +21,7 @@ from .variables import ( ArrayNumberVariable, ArrayObjectVariable, ArrayStringVariable, + ArrayVariable, FileVariable, FloatVariable, IntegerVariable, @@ -43,6 +44,7 @@ __all__ = [ "ArraySegment", "ArrayStringSegment", "ArrayStringVariable", + "ArrayVariable", "FileSegment", "FileVariable", "FloatSegment", diff --git a/api/core/variables/variables.py b/api/core/variables/variables.py index c902303eef..f9268b52e6 100644 --- a/api/core/variables/variables.py +++ b/api/core/variables/variables.py @@ -10,6 +10,7 @@ from .segments import ( ArrayFileSegment, ArrayNumberSegment, ArrayObjectSegment, + ArraySegment, ArrayStringSegment, FileSegment, FloatSegment, @@ -52,19 +53,23 @@ class ObjectVariable(ObjectSegment, Variable): pass -class ArrayAnyVariable(ArrayAnySegment, Variable): +class ArrayVariable(ArraySegment, Variable): pass -class ArrayStringVariable(ArrayStringSegment, Variable): +class ArrayAnyVariable(ArrayAnySegment, ArrayVariable): pass -class ArrayNumberVariable(ArrayNumberSegment, Variable): +class ArrayStringVariable(ArrayStringSegment, ArrayVariable): pass -class ArrayObjectVariable(ArrayObjectSegment, Variable): +class ArrayNumberVariable(ArrayNumberSegment, ArrayVariable): + pass + + +class ArrayObjectVariable(ArrayObjectSegment, ArrayVariable): pass diff --git a/api/core/workflow/nodes/iteration/iteration_node.py b/api/core/workflow/nodes/iteration/iteration_node.py index d935228c16..6a89cbfad6 100644 --- a/api/core/workflow/nodes/iteration/iteration_node.py +++ b/api/core/workflow/nodes/iteration/iteration_node.py @@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any, Optional, cast from flask import Flask, current_app from configs import dify_config -from core.variables import IntegerVariable +from core.variables import ArrayVariable, IntegerVariable, NoneVariable from core.workflow.entities.node_entities import ( NodeRunMetadataKey, NodeRunResult, @@ -75,12 +75,15 @@ class IterationNode(BaseNode[IterationNodeData]): """ Run the node. """ - iterator_list_segment = self.graph_runtime_state.variable_pool.get(self.node_data.iterator_selector) + variable = self.graph_runtime_state.variable_pool.get(self.node_data.iterator_selector) - if not iterator_list_segment: - raise IteratorVariableNotFoundError(f"Iterator variable {self.node_data.iterator_selector} not found") + if not variable: + raise IteratorVariableNotFoundError(f"iterator variable {self.node_data.iterator_selector} not found") - if len(iterator_list_segment.value) == 0: + if not isinstance(variable, ArrayVariable) and not isinstance(variable, NoneVariable): + raise InvalidIteratorValueError(f"invalid iterator value: {variable}, please provide a list.") + + if isinstance(variable, NoneVariable) or len(variable.value) == 0: yield RunCompletedEvent( run_result=NodeRunResult( status=WorkflowNodeExecutionStatus.SUCCEEDED, @@ -89,7 +92,7 @@ class IterationNode(BaseNode[IterationNodeData]): ) return - iterator_list_value = iterator_list_segment.to_object() + iterator_list_value = variable.to_object() if not isinstance(iterator_list_value, list): raise InvalidIteratorValueError(f"Invalid iterator value: {iterator_list_value}, please provide a list.") From e068bbec737ed649ba2294fb51b547bec6ea1c22 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 15:53:03 +0800 Subject: [PATCH 118/138] feat: add RequestBodyError for invalid request body handling (#11994) Signed-off-by: -LAN- --- api/core/workflow/nodes/http_request/exc.py | 4 ++++ api/core/workflow/nodes/http_request/executor.py | 9 +++++++++ api/core/workflow/nodes/http_request/node.py | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/http_request/exc.py b/api/core/workflow/nodes/http_request/exc.py index 7a5ab7dbc1..a815f277be 100644 --- a/api/core/workflow/nodes/http_request/exc.py +++ b/api/core/workflow/nodes/http_request/exc.py @@ -16,3 +16,7 @@ class InvalidHttpMethodError(HttpRequestNodeError): class ResponseSizeError(HttpRequestNodeError): """Raised when the response size exceeds the allowed threshold.""" + + +class RequestBodyError(HttpRequestNodeError): + """Raised when the request body is invalid.""" diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index 3b7e193319..575db15d36 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -23,6 +23,7 @@ from .exc import ( FileFetchError, HttpRequestNodeError, InvalidHttpMethodError, + RequestBodyError, ResponseSizeError, ) @@ -143,13 +144,19 @@ class Executor: case "none": self.content = "" case "raw-text": + if len(data) != 1: + raise RequestBodyError("raw-text body type should have exactly one item") self.content = self.variable_pool.convert_template(data[0].value).text case "json": + if len(data) != 1: + raise RequestBodyError("json body type should have exactly one item") json_string = self.variable_pool.convert_template(data[0].value).text json_object = json.loads(json_string, strict=False) self.json = json_object # self.json = self._parse_object_contains_variables(json_object) case "binary": + if len(data) != 1: + raise RequestBodyError("binary body type should have exactly one item") file_selector = data[0].file file_variable = self.variable_pool.get_file(file_selector) if file_variable is None: @@ -317,6 +324,8 @@ class Executor: elif self.json: body = json.dumps(self.json) elif self.node_data.body.type == "raw-text": + if len(self.node_data.body.data) != 1: + raise RequestBodyError("raw-text body type should have exactly one item") body = self.node_data.body.data[0].value if body: raw += f"Content-Length: {len(body)}\r\n" diff --git a/api/core/workflow/nodes/http_request/node.py b/api/core/workflow/nodes/http_request/node.py index 171389a34c..ebed690f6f 100644 --- a/api/core/workflow/nodes/http_request/node.py +++ b/api/core/workflow/nodes/http_request/node.py @@ -20,7 +20,7 @@ from .entities import ( HttpRequestNodeTimeout, Response, ) -from .exc import HttpRequestNodeError +from .exc import HttpRequestNodeError, RequestBodyError HTTP_REQUEST_DEFAULT_TIMEOUT = HttpRequestNodeTimeout( connect=dify_config.HTTP_REQUEST_MAX_CONNECT_TIMEOUT, @@ -136,9 +136,13 @@ class HttpRequestNode(BaseNode[HttpRequestNodeData]): data = node_data.body.data match body_type: case "binary": + if len(data) != 1: + raise RequestBodyError("invalid body data, should have only one item") selector = data[0].file selectors.append(VariableSelector(variable="#" + ".".join(selector) + "#", value_selector=selector)) case "json" | "raw-text": + if len(data) != 1: + raise RequestBodyError("invalid body data, should have only one item") selectors += variable_template_parser.extract_selectors_from_template(data[0].key) selectors += variable_template_parser.extract_selectors_from_template(data[0].value) case "x-www-form-urlencoded": From ef95b1268ebfbb9d9e77c4a62ebe97789b859020 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 23 Dec 2024 15:55:50 +0800 Subject: [PATCH 119/138] Fix/workflow retry (#11999) --- .../workflow/hooks/use-workflow-run.ts | 71 ++++++++++++++++--- .../_base/components/retry/retry-on-node.tsx | 4 +- web/app/components/workflow/run/index.tsx | 39 ++++++++-- .../workflow/run/iteration-result-panel.tsx | 40 ++++++++--- web/app/components/workflow/run/node.tsx | 5 ++ 5 files changed, 130 insertions(+), 29 deletions(-) diff --git a/web/app/components/workflow/hooks/use-workflow-run.ts b/web/app/components/workflow/hooks/use-workflow-run.ts index 822aa490db..cc1b0724a9 100644 --- a/web/app/components/workflow/hooks/use-workflow-run.ts +++ b/web/app/components/workflow/hooks/use-workflow-run.ts @@ -387,6 +387,9 @@ export const useWorkflowRun = () => { if (nodeIndex !== -1) { currIteration[nodeIndex] = { ...currIteration[nodeIndex], + ...(currIteration[nodeIndex].retryDetail + ? { retryDetail: currIteration[nodeIndex].retryDetail } + : {}), ...data, } as any } @@ -626,6 +629,8 @@ export const useWorkflowRun = () => { const { workflowRunningData, setWorkflowRunningData, + iterParallelLogMap, + setIterParallelLogMap, } = workflowStore.getState() const { getNodes, @@ -633,19 +638,65 @@ export const useWorkflowRun = () => { } = store.getState() const nodes = getNodes() - setWorkflowRunningData(produce(workflowRunningData!, (draft) => { - const tracing = draft.tracing! - const currentRetryNodeIndex = tracing.findIndex(trace => trace.node_id === data.node_id) + const currentNode = nodes.find(node => node.id === data.node_id)! + const nodeParent = nodes.find(node => node.id === currentNode.parentId) + if (nodeParent) { + if (!data.execution_metadata.parallel_mode_run_id) { + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + const tracing = draft.tracing! + const iteration = tracing.find(trace => trace.node_id === nodeParent.id) - if (currentRetryNodeIndex > -1) { - const currentRetryNode = tracing[currentRetryNodeIndex] - if (currentRetryNode.retryDetail) - draft.tracing![currentRetryNodeIndex].retryDetail!.push(data as NodeTracing) + if (iteration && iteration.details?.length) { + const currentNodeRetry = iteration.details[nodeParent.data._iterationIndex - 1]?.find(item => item.node_id === data.node_id) - else - draft.tracing![currentRetryNodeIndex].retryDetail = [data as NodeTracing] + if (currentNodeRetry) { + if (currentNodeRetry?.retryDetail) + currentNodeRetry?.retryDetail.push(data as NodeTracing) + else + currentNodeRetry.retryDetail = [data as NodeTracing] + } + } + })) } - })) + else { + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + const tracing = draft.tracing! + const iteration = tracing.find(trace => trace.node_id === nodeParent.id) + + if (iteration && iteration.details?.length) { + const iterRunID = data.execution_metadata?.parallel_mode_run_id + + const currIteration = iterParallelLogMap.get(iteration.node_id)?.get(iterRunID) + const currentNodeRetry = currIteration?.find(item => item.node_id === data.node_id) + + if (currentNodeRetry) { + if (currentNodeRetry?.retryDetail) + currentNodeRetry?.retryDetail.push(data as NodeTracing) + else + currentNodeRetry.retryDetail = [data as NodeTracing] + } + setIterParallelLogMap(iterParallelLogMap) + const iterLogMap = iterParallelLogMap.get(iteration.node_id) + if (iterLogMap) + iteration.details = Array.from(iterLogMap.values()) + } + })) + } + } + else { + setWorkflowRunningData(produce(workflowRunningData!, (draft) => { + const tracing = draft.tracing! + const currentRetryNodeIndex = tracing.findIndex(trace => trace.node_id === data.node_id) + + if (currentRetryNodeIndex > -1) { + const currentRetryNode = tracing[currentRetryNodeIndex] + if (currentRetryNode.retryDetail) + draft.tracing![currentRetryNodeIndex].retryDetail!.push(data as NodeTracing) + else + draft.tracing![currentRetryNodeIndex].retryDetail = [data as NodeTracing] + } + })) + } const newNodes = produce(nodes, (draft) => { const currentNode = draft.find(node => node.id === data.node_id)! diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx index f5d2f08ac8..9e1d8e1da6 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx @@ -31,7 +31,7 @@ const RetryOnNode = ({ }, [data._runningStatus, showSelectedBorder]) const showDefault = !isRunning && !isSuccessful && !isException && !isFailed - if (!retry_config) + if (!retry_config?.retry_enabled) return null return ( @@ -74,7 +74,7 @@ const RetryOnNode = ({ }
{ - !showDefault && ( + !showDefault && !!data._retryIndex && (
{data._retryIndex}/{data.retry_config?.max_retries}
diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 520c59bf4c..8b0319cabe 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -78,11 +78,24 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe const groupMap = nodeGroupMap.get(iterationNode.node_id)! - if (!groupMap.has(runId)) + if (!groupMap.has(runId)) { groupMap.set(runId, [item]) + } + else { + if (item.status === 'retry') { + const retryNode = groupMap.get(runId)!.find(node => node.node_id === item.node_id) - else - groupMap.get(runId)!.push(item) + if (retryNode) { + if (retryNode?.retryDetail) + retryNode.retryDetail.push(item) + else + retryNode.retryDetail = [item] + } + } + else { + groupMap.get(runId)!.push(item) + } + } if (item.status === 'failed') { iterationNode.status = 'failed' @@ -94,10 +107,24 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe const updateSequentialModeGroup = (index: number, item: NodeTracing, iterationNode: NodeTracing) => { const { details } = iterationNode if (details) { - if (!details[index]) + if (!details[index]) { details[index] = [item] - else - details[index].push(item) + } + else { + if (item.status === 'retry') { + const retryNode = details[index].find(node => node.node_id === item.node_id) + + if (retryNode) { + if (retryNode?.retryDetail) + retryNode.retryDetail.push(item) + else + retryNode.retryDetail = [item] + } + } + else { + details[index].push(item) + } + } } if (item.status === 'failed') { diff --git a/web/app/components/workflow/run/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-result-panel.tsx index b13eadec99..b809e1e669 100644 --- a/web/app/components/workflow/run/iteration-result-panel.tsx +++ b/web/app/components/workflow/run/iteration-result-panel.tsx @@ -11,6 +11,7 @@ import { import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows' import { NodeRunningStatus } from '../types' import TracingPanel from './tracing-panel' +import RetryResultPanel from './retry-result-panel' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' import cn from '@/utils/classnames' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' @@ -41,8 +42,8 @@ const IterationResultPanel: FC = ({ })) }, []) const countIterDuration = (iteration: NodeTracing[], iterDurationMap: IterationDurationMap): string => { - const IterRunIndex = iteration[0].execution_metadata.iteration_index as number - const iterRunId = iteration[0].execution_metadata.parallel_mode_run_id + const IterRunIndex = iteration[0]?.execution_metadata?.iteration_index as number + const iterRunId = iteration[0]?.execution_metadata?.parallel_mode_run_id const iterItem = iterDurationMap[iterRunId || IterRunIndex] const duration = iterItem return `${(duration && duration > 0.01) ? duration.toFixed(2) : 0.01}s` @@ -74,6 +75,10 @@ const IterationResultPanel: FC = ({ ) } + const [retryRunResult, setRetryRunResult] = useState | undefined>() + const handleRetryDetail = (v: number, detail?: NodeTracing[]) => { + setRetryRunResult({ ...retryRunResult, [v]: detail }) + } const main = ( <> @@ -116,15 +121,28 @@ const IterationResultPanel: FC = ({ {expandedIterations[index] &&
} -
- -
+ { + !retryRunResult?.[index] && ( +
+ handleRetryDetail(index, v)} + /> +
+ ) + } + { + retryRunResult?.[index] && ( + handleRetryDetail(index, undefined)} + /> + ) + }
))}
diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index bb07bd1e8c..d2da319a02 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -216,6 +216,11 @@ const NodePanel: FC = ({ {nodeInfo.error} )} + {nodeInfo.status === 'retry' && ( + + {nodeInfo.error} + + )}
{nodeInfo.inputs && (
From c4091c4c6627e257765e15807e94763a63218db0 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 16:28:54 +0800 Subject: [PATCH 120/138] fix: improve error handling for file retrieval in AwsS3Storage (#12002) Signed-off-by: -LAN- --- api/extensions/storage/aws_s3_storage.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/extensions/storage/aws_s3_storage.py b/api/extensions/storage/aws_s3_storage.py index ab2d0fba3b..ce36c2e7de 100644 --- a/api/extensions/storage/aws_s3_storage.py +++ b/api/extensions/storage/aws_s3_storage.py @@ -67,7 +67,9 @@ class AwsS3Storage(BaseStorage): yield from response["Body"].iter_chunks() except ClientError as ex: if ex.response["Error"]["Code"] == "NoSuchKey": - raise FileNotFoundError("File not found") + raise FileNotFoundError("file not found") + elif "reached max retries" in str(ex): + raise ValueError("please do not request the same file too frequently") else: raise From dfc25dbdd036746d93e9cf6cf7ecd09317644abc Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 23 Dec 2024 16:30:04 +0800 Subject: [PATCH 121/138] fix: drop useless and wrong code in Account (#11961) Signed-off-by: yihong0618 --- api/models/account.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/api/models/account.py b/api/models/account.py index 932ba1da57..a8602d10a9 100644 --- a/api/models/account.py +++ b/api/models/account.py @@ -99,11 +99,6 @@ class Account(UserMixin, db.Model): return db.session.query(Account).filter(Account.id == account_integrate.account_id).one_or_none() return None - def get_integrates(self) -> list[db.Model]: - ai = db.Model - return db.session.query(ai).filter(ai.account_id == self.id).all() - - # check current_user.current_tenant.current_role in ['admin', 'owner'] @property def is_admin_or_owner(self): return TenantAccountRole.is_privileged_role(self._current_tenant.current_role) From dc19cd5d9d1bfb354a522c86839e38501d973709 Mon Sep 17 00:00:00 2001 From: Novice <857526207@qq.com> Date: Mon, 23 Dec 2024 16:42:28 +0800 Subject: [PATCH 122/138] fix: add retry feature to code node (#12005) Co-authored-by: Novice Lee --- api/core/workflow/nodes/enums.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/workflow/nodes/enums.py b/api/core/workflow/nodes/enums.py index 32fdc048d1..7970a49aa4 100644 --- a/api/core/workflow/nodes/enums.py +++ b/api/core/workflow/nodes/enums.py @@ -35,4 +35,4 @@ class FailBranchSourceHandle(StrEnum): CONTINUE_ON_ERROR_NODE_TYPE = [NodeType.LLM, NodeType.CODE, NodeType.TOOL, NodeType.HTTP_REQUEST] -RETRY_ON_ERROR_NODE_TYPE = [NodeType.LLM, NodeType.TOOL, NodeType.HTTP_REQUEST] +RETRY_ON_ERROR_NODE_TYPE = CONTINUE_ON_ERROR_NODE_TYPE From 2bf33c4dd2862818e0a0d1e90ca8f85785a9cf28 Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 23 Dec 2024 17:18:09 +0800 Subject: [PATCH 123/138] Fix/workflow retry log (#12013) --- .../workflow/nodes/_base/components/retry/retry-on-node.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx index 9e1d8e1da6..34c3e28d2c 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx @@ -34,6 +34,9 @@ const RetryOnNode = ({ if (!retry_config?.retry_enabled) return null + if (!showDefault && !data._retryIndex) + return null + return (
Date: Mon, 23 Dec 2024 17:53:25 +0800 Subject: [PATCH 124/138] Fix/add retry mechanism to billing service request handling (#12006) Signed-off-by: -LAN- --- api/services/billing_service.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/api/services/billing_service.py b/api/services/billing_service.py index 911d234641..edc5168217 100644 --- a/api/services/billing_service.py +++ b/api/services/billing_service.py @@ -1,6 +1,7 @@ import os -import requests +import httpx +from tenacity import retry, retry_if_not_exception_type, stop_before_delay, wait_fixed from extensions.ext_database import db from models.account import TenantAccountJoin, TenantAccountRole @@ -39,11 +40,17 @@ class BillingService: return cls._send_request("GET", "/invoices", params=params) @classmethod + @retry( + wait=wait_fixed(2), + stop=stop_before_delay(10), + retry=retry_if_not_exception_type(httpx.RequestError), + reraise=True, + ) def _send_request(cls, method, endpoint, json=None, params=None): headers = {"Content-Type": "application/json", "Billing-Api-Secret-Key": cls.secret_key} url = f"{cls.base_url}{endpoint}" - response = requests.request(method, url, json=json, params=params, headers=headers) + response = httpx.request(method, url, json=json, params=params, headers=headers) return response.json() From 75bce2822ed082ba6c3a1e2c78cd6e4b94b2001a Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 17:53:36 +0800 Subject: [PATCH 125/138] fix: add logging for missing edge mapping in StreamProcessor (#12008) Signed-off-by: -LAN- --- api/core/workflow/nodes/answer/base_stream_processor.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/core/workflow/nodes/answer/base_stream_processor.py b/api/core/workflow/nodes/answer/base_stream_processor.py index 36c3fe180a..031347662e 100644 --- a/api/core/workflow/nodes/answer/base_stream_processor.py +++ b/api/core/workflow/nodes/answer/base_stream_processor.py @@ -1,3 +1,4 @@ +import logging from abc import ABC, abstractmethod from collections.abc import Generator @@ -5,6 +6,8 @@ from core.workflow.entities.variable_pool import VariablePool from core.workflow.graph_engine.entities.event import GraphEngineEvent, NodeRunSucceededEvent from core.workflow.graph_engine.entities.graph import Graph +logger = logging.getLogger(__name__) + class StreamProcessor(ABC): def __init__(self, graph: Graph, variable_pool: VariablePool) -> None: @@ -31,6 +34,9 @@ class StreamProcessor(ABC): if run_result.edge_source_handle: reachable_node_ids = [] unreachable_first_node_ids = [] + if finished_node_id not in self.graph.edge_mapping: + logger.warning(f"node {finished_node_id} has no edge mapping") + return for edge in self.graph.edge_mapping[finished_node_id]: if ( edge.run_condition From d0dd8b79554144208d403578e297eb9f49bc139a Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 17:53:42 +0800 Subject: [PATCH 126/138] fix: add UUID validation for tool file ID extraction (#12011) Signed-off-by: -LAN- --- api/core/workflow/nodes/tool/tool_node.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index 3b56f94876..983fa7e623 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -1,5 +1,6 @@ from collections.abc import Mapping, Sequence from typing import Any +from uuid import UUID from sqlalchemy import select from sqlalchemy.orm import Session @@ -231,6 +232,10 @@ class ToolNode(BaseNode[ToolNodeData]): url = str(response.message) transfer_method = FileTransferMethod.TOOL_FILE tool_file_id = url.split("/")[-1].split(".")[0] + try: + UUID(tool_file_id) + except ValueError: + raise ToolFileError(f"cannot extract tool file id from url {url}") with Session(db.engine) as session: stmt = select(ToolFile).where(ToolFile.id == tool_file_id) tool_file = session.scalar(stmt) From af2888d3947654668ff01f9c6606fcfa0d0f6381 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 17:53:57 +0800 Subject: [PATCH 127/138] fix: remove json_schema if response format is disabled. (#12014) Signed-off-by: -LAN- --- api/core/model_runtime/model_providers/openai/llm/llm.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/core/model_runtime/model_providers/openai/llm/llm.py b/api/core/model_runtime/model_providers/openai/llm/llm.py index b73ce8752f..73cd7e3c34 100644 --- a/api/core/model_runtime/model_providers/openai/llm/llm.py +++ b/api/core/model_runtime/model_providers/openai/llm/llm.py @@ -421,7 +421,11 @@ class OpenAILargeLanguageModel(_CommonOpenAI, LargeLanguageModel): # text completion model response = client.completions.create( - prompt=prompt_messages[0].content, model=model, stream=stream, **model_parameters, **extra_model_kwargs + prompt=prompt_messages[0].content, + model=model, + stream=stream, + **model_parameters, + **extra_model_kwargs, ) if stream: @@ -593,6 +597,8 @@ class OpenAILargeLanguageModel(_CommonOpenAI, LargeLanguageModel): model_parameters["response_format"] = {"type": "json_schema", "json_schema": schema} else: model_parameters["response_format"] = {"type": response_format} + elif "json_schema" in model_parameters: + del model_parameters["json_schema"] extra_model_kwargs = {} From c3c85276d194d4de738214fb84d39bfa2c15bd3d Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 17:54:08 +0800 Subject: [PATCH 128/138] Fix/refactor invoke result handling in question classifier node (#12015) Signed-off-by: -LAN- --- .../question_classifier_node.py | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/api/core/workflow/nodes/question_classifier/question_classifier_node.py b/api/core/workflow/nodes/question_classifier/question_classifier_node.py index 5043e25e2b..31f8368d59 100644 --- a/api/core/workflow/nodes/question_classifier/question_classifier_node.py +++ b/api/core/workflow/nodes/question_classifier/question_classifier_node.py @@ -1,10 +1,8 @@ import json -import logging from collections.abc import Mapping, Sequence from typing import TYPE_CHECKING, Any, Optional, cast from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity -from core.llm_generator.output_parser.errors import OutputParserError from core.memory.token_buffer_memory import TokenBufferMemory from core.model_manager import ModelInstance from core.model_runtime.entities import LLMUsage, ModelPropertyKey, PromptMessageRole @@ -96,27 +94,28 @@ class QuestionClassifierNode(LLMNode): jinja2_variables=[], ) - # handle invoke result - generator = self._invoke_llm( - node_data_model=node_data.model, - model_instance=model_instance, - prompt_messages=prompt_messages, - stop=stop, - ) - result_text = "" usage = LLMUsage.empty_usage() finish_reason = None - for event in generator: - if isinstance(event, ModelInvokeCompletedEvent): - result_text = event.text - usage = event.usage - finish_reason = event.finish_reason - break - category_name = node_data.classes[0].name - category_id = node_data.classes[0].id try: + # handle invoke result + generator = self._invoke_llm( + node_data_model=node_data.model, + model_instance=model_instance, + prompt_messages=prompt_messages, + stop=stop, + ) + + for event in generator: + if isinstance(event, ModelInvokeCompletedEvent): + result_text = event.text + usage = event.usage + finish_reason = event.finish_reason + break + + category_name = node_data.classes[0].name + category_id = node_data.classes[0].id result_text_json = parse_and_check_json_markdown(result_text, []) # result_text_json = json.loads(result_text.strip('```JSON\n')) if "category_name" in result_text_json and "category_id" in result_text_json: @@ -127,10 +126,6 @@ class QuestionClassifierNode(LLMNode): if category_id_result in category_ids: category_name = classes_map[category_id_result] category_id = category_id_result - - except OutputParserError: - logging.exception(f"Failed to parse result text: {result_text}") - try: process_data = { "model_mode": model_config.mode, "prompts": PromptMessageUtil.prompt_messages_to_prompt_for_saving( @@ -154,7 +149,7 @@ class QuestionClassifierNode(LLMNode): }, llm_usage=usage, ) - except Exception as e: + except ValueError as e: return NodeRunResult( status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, From e0f1410b48a7330b6a3b85f2103e9e889e9ccde1 Mon Sep 17 00:00:00 2001 From: yihong Date: Mon, 23 Dec 2024 18:56:59 +0800 Subject: [PATCH 129/138] fix: issue Multiple Paths Between IF/ELSE Branches (#11646) Signed-off-by: yihong0618 --- api/core/workflow/nodes/answer/answer_stream_processor.py | 1 - api/core/workflow/nodes/answer/base_stream_processor.py | 8 +++++++- .../core/workflow/graph_engine/test_graph_engine.py | 2 -- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/api/core/workflow/nodes/answer/answer_stream_processor.py b/api/core/workflow/nodes/answer/answer_stream_processor.py index d94f059058..ed033e7f28 100644 --- a/api/core/workflow/nodes/answer/answer_stream_processor.py +++ b/api/core/workflow/nodes/answer/answer_stream_processor.py @@ -60,7 +60,6 @@ class AnswerStreamProcessor(StreamProcessor): del self.current_stream_chunk_generating_node_ids[event.route_node_state.node_id] - # remove unreachable nodes self._remove_unreachable_nodes(event) # generate stream outputs diff --git a/api/core/workflow/nodes/answer/base_stream_processor.py b/api/core/workflow/nodes/answer/base_stream_processor.py index 031347662e..d785397e13 100644 --- a/api/core/workflow/nodes/answer/base_stream_processor.py +++ b/api/core/workflow/nodes/answer/base_stream_processor.py @@ -43,7 +43,13 @@ class StreamProcessor(ABC): and edge.run_condition.branch_identify and run_result.edge_source_handle == edge.run_condition.branch_identify ): - reachable_node_ids.extend(self._fetch_node_ids_in_reachable_branch(edge.target_node_id)) + # remove unreachable nodes + # FIXME: because of the code branch can combine directly, so for answer node + # we remove the node maybe shortcut the answer node, so comment this code for now + # there is not effect on the answer node and the workflow, when we have a better solution + # we can open this code. Issues: #11542 #9560 #10638 #10564 + + # reachable_node_ids.extend(self._fetch_node_ids_in_reachable_branch(edge.target_node_id)) continue else: unreachable_first_node_ids.append(edge.target_node_id) diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_graph_engine.py b/api/tests/unit_tests/core/workflow/graph_engine/test_graph_engine.py index 9f1ba7b6af..b7d8f69e8c 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_graph_engine.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_graph_engine.py @@ -488,14 +488,12 @@ def test_run_branch(mock_close, mock_remove): items = [] generator = graph_engine.run() for item in generator: - # print(type(item), item) items.append(item) assert len(items) == 10 assert items[3].route_node_state.node_id == "if-else-1" assert items[4].route_node_state.node_id == "if-else-1" assert isinstance(items[5], NodeRunStreamChunkEvent) - assert items[5].chunk_content == "1 " assert isinstance(items[6], NodeRunStreamChunkEvent) assert items[6].chunk_content == "takato" assert items[7].route_node_state.node_id == "answer-1" From e88ea71aefd2adca64683ce0cca836df015d0f4f Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 19:15:48 +0800 Subject: [PATCH 130/138] chore/bump version to 0.14.2 (#12017) Signed-off-by: -LAN- --- api/configs/packaging/__init__.py | 2 +- api/services/app_dsl_service.py | 2 +- docker-legacy/docker-compose.yaml | 6 +++--- docker/docker-compose-template.yaml | 6 +++--- docker/docker-compose.yaml | 6 +++--- web/package.json | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/configs/packaging/__init__.py b/api/configs/packaging/__init__.py index 57cd74af1f..4a168a3fb1 100644 --- a/api/configs/packaging/__init__.py +++ b/api/configs/packaging/__init__.py @@ -9,7 +9,7 @@ class PackagingInfo(BaseSettings): CURRENT_VERSION: str = Field( description="Dify version", - default="0.14.1", + default="0.14.2", ) COMMIT_SHA: str = Field( diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index 0478903fa4..7c1a175988 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) IMPORT_INFO_REDIS_KEY_PREFIX = "app_import_info:" IMPORT_INFO_REDIS_EXPIRY = 180 # 3 minutes -CURRENT_DSL_VERSION = "0.1.4" +CURRENT_DSL_VERSION = "0.1.5" class ImportMode(StrEnum): diff --git a/docker-legacy/docker-compose.yaml b/docker-legacy/docker-compose.yaml index 3bf4333ad1..1cff58be7f 100644 --- a/docker-legacy/docker-compose.yaml +++ b/docker-legacy/docker-compose.yaml @@ -2,7 +2,7 @@ version: '3' services: # API service api: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: # Startup mode, 'api' starts the API server. @@ -227,7 +227,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: CONSOLE_WEB_URL: '' @@ -397,7 +397,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.14.1 + image: langgenius/dify-web:0.14.2 restart: always environment: # The base URL of console application api server, refers to the Console base URL of WEB service if console domain is diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index 8370d82daa..d4e0ba49d0 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -2,7 +2,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: # Use the shared environment variables. @@ -25,7 +25,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: # Use the shared environment variables. @@ -47,7 +47,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.14.1 + image: langgenius/dify-web:0.14.2 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 99bc14c717..7122f4a6d0 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -390,7 +390,7 @@ x-shared-env: &shared-api-worker-env services: # API service api: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: # Use the shared environment variables. @@ -413,7 +413,7 @@ services: # worker service # The Celery worker for processing the queue. worker: - image: langgenius/dify-api:0.14.1 + image: langgenius/dify-api:0.14.2 restart: always environment: # Use the shared environment variables. @@ -435,7 +435,7 @@ services: # Frontend web application. web: - image: langgenius/dify-web:0.14.1 + image: langgenius/dify-web:0.14.2 restart: always environment: CONSOLE_API_URL: ${CONSOLE_API_URL:-} diff --git a/web/package.json b/web/package.json index 25cf231a95..d9515645c8 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "dify-web", - "version": "0.14.1", + "version": "0.14.2", "private": true, "engines": { "node": ">=18.17.0" From 1c80941c697c3c869d601c106ff132f917d94a71 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Mon, 23 Dec 2024 22:36:44 +0800 Subject: [PATCH 131/138] fix: add FileNotFoundError to ignored errors in Sentry integration (#12023) Signed-off-by: -LAN- --- api/extensions/ext_sentry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/extensions/ext_sentry.py b/api/extensions/ext_sentry.py index 8016356a3e..3ec8ae6e1d 100644 --- a/api/extensions/ext_sentry.py +++ b/api/extensions/ext_sentry.py @@ -27,6 +27,7 @@ def init_app(app: DifyApp): ignore_errors=[ HTTPException, ValueError, + FileNotFoundError, openai.APIStatusError, InvokeRateLimitError, parse_error.defaultErrorResponse, From e0c24c0e99901e0531ed1dab4a05e9f19a827052 Mon Sep 17 00:00:00 2001 From: Yang Jingtao <83807321+You-keitou@users.noreply.github.com> Date: Tue, 24 Dec 2024 11:30:31 +0900 Subject: [PATCH 132/138] fix: Fix session typo in workflow_trace method (#12031) --- api/core/ops/ops_trace_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index a04fc6ee78..4f41b6ed97 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -360,7 +360,7 @@ class TraceTask: raise ValueError("Workflow run not found") db.session.merge(workflow_run) - db.sessoin.refresh(workflow_run) + db.session.refresh(workflow_run) workflow_id = workflow_run.workflow_id tenant_id = workflow_run.tenant_id From 6a0ff3686cb8dcb5a997545fc2d885167dc966f7 Mon Sep 17 00:00:00 2001 From: yihong Date: Tue, 24 Dec 2024 15:23:27 +0800 Subject: [PATCH 133/138] fix: fix typo (#12034) Signed-off-by: yihong0618 --- .../huggingface_tei/text_embedding/text_embedding.py | 1 - api/core/workflow/graph_engine/graph_engine.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/text_embedding.py index 284429b741..a8a13313db 100644 --- a/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/huggingface_tei/text_embedding/text_embedding.py @@ -157,7 +157,6 @@ class HuggingfaceTeiTextEmbeddingModel(TextEmbeddingModel): headers["Authorization"] = f"Bearer {api_key}" extra_args = TeiHelper.get_tei_extra_parameter(server_url, model, headers) - print(extra_args) if extra_args.model_type != "embedding": raise CredentialsValidateFailedError("Current model is not a embedding model") diff --git a/api/core/workflow/graph_engine/graph_engine.py b/api/core/workflow/graph_engine/graph_engine.py index d7d33c65fc..854036b2c1 100644 --- a/api/core/workflow/graph_engine/graph_engine.py +++ b/api/core/workflow/graph_engine/graph_engine.py @@ -612,8 +612,8 @@ class GraphEngine: max_retries = node_instance.node_data.retry_config.max_retries retry_interval = node_instance.node_data.retry_config.retry_interval_seconds retries = 0 - shoudl_continue_retry = True - while shoudl_continue_retry and retries <= max_retries: + should_continue_retry = True + while should_continue_retry and retries <= max_retries: try: # run node retry_start_at = datetime.now(UTC).replace(tzinfo=None) @@ -692,7 +692,7 @@ class GraphEngine: parent_parallel_id=parent_parallel_id, parent_parallel_start_node_id=parent_parallel_start_node_id, ) - shoudl_continue_retry = False + should_continue_retry = False else: yield NodeRunFailedEvent( error=route_node_state.failed_reason or "Unknown error.", @@ -706,7 +706,7 @@ class GraphEngine: parent_parallel_id=parent_parallel_id, parent_parallel_start_node_id=parent_parallel_start_node_id, ) - shoudl_continue_retry = False + should_continue_retry = False elif run_result.status == WorkflowNodeExecutionStatus.SUCCEEDED: if node_instance.should_continue_on_error and self.graph.edge_mapping.get( node_instance.node_id @@ -758,7 +758,7 @@ class GraphEngine: parent_parallel_id=parent_parallel_id, parent_parallel_start_node_id=parent_parallel_start_node_id, ) - shoudl_continue_retry = False + should_continue_retry = False break elif isinstance(item, RunStreamChunkEvent): From 82134a1d503a444f0de8d922ccabc50b96560a49 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Tue, 24 Dec 2024 15:24:19 +0800 Subject: [PATCH 134/138] =?UTF-8?q?fix:=20Replace=20generic=20exceptions?= =?UTF-8?q?=20with=20specific=20error=20classes=20in=20task=20p=E2=80=A6?= =?UTF-8?q?=20(#12036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: -LAN- --- .../easy_ui_based_generate_task_pipeline.py | 2 +- api/core/app/task_pipeline/exc.py | 17 +++++++++++++++++ .../app/task_pipeline/workflow_cycle_manage.py | 6 ++++-- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 api/core/app/task_pipeline/exc.py diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index 4216cd46cf..e26b60c4d3 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -177,7 +177,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan else: continue - raise Exception("Queue listening stopped unexpectedly.") + raise RuntimeError("queue listening stopped unexpectedly.") def _to_stream_response( self, generator: Generator[StreamResponse, None, None] diff --git a/api/core/app/task_pipeline/exc.py b/api/core/app/task_pipeline/exc.py new file mode 100644 index 0000000000..e4b4168d08 --- /dev/null +++ b/api/core/app/task_pipeline/exc.py @@ -0,0 +1,17 @@ +class TaskPipilineError(ValueError): + pass + + +class RecordNotFoundError(TaskPipilineError): + def __init__(self, record_name: str, record_id: str): + super().__init__(f"{record_name} with id {record_id} not found") + + +class WorkflowRunNotFoundError(RecordNotFoundError): + def __init__(self, workflow_run_id: str): + super().__init__("WorkflowRun", workflow_run_id) + + +class WorkflowNodeExecutionNotFoundError(RecordNotFoundError): + def __init__(self, workflow_node_execution_id: str): + super().__init__("WorkflowNodeExecution", workflow_node_execution_id) diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index 72e4c796c3..df7dbace0e 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -58,6 +58,8 @@ from models.workflow import ( WorkflowRunStatus, ) +from .exc import WorkflowNodeExecutionNotFoundError, WorkflowRunNotFoundError + class WorkflowCycleManage: _application_generate_entity: Union[AdvancedChatAppGenerateEntity, WorkflowAppGenerateEntity] @@ -898,7 +900,7 @@ class WorkflowCycleManage: workflow_run = db.session.query(WorkflowRun).filter(WorkflowRun.id == workflow_run_id).first() if not workflow_run: - raise Exception(f"Workflow run not found: {workflow_run_id}") + raise WorkflowRunNotFoundError(workflow_run_id) return workflow_run @@ -911,6 +913,6 @@ class WorkflowCycleManage: workflow_node_execution = self._wip_workflow_node_executions.get(node_execution_id) if not workflow_node_execution: - raise Exception(f"Workflow node execution not found: {node_execution_id}") + raise WorkflowNodeExecutionNotFoundError(node_execution_id) return workflow_node_execution From 094343739b42668d40f29c208d7c52f9617f76e8 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Tue, 24 Dec 2024 15:56:59 +0800 Subject: [PATCH 135/138] fix/array file cannot use in iteration node (#12035) Signed-off-by: -LAN- --- api/core/variables/variables.py | 2 +- api/tests/unit_tests/core/app/segments/test_variables.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/api/core/variables/variables.py b/api/core/variables/variables.py index f9268b52e6..973e420961 100644 --- a/api/core/variables/variables.py +++ b/api/core/variables/variables.py @@ -90,5 +90,5 @@ class FileVariable(FileSegment, Variable): pass -class ArrayFileVariable(ArrayFileSegment, Variable): +class ArrayFileVariable(ArrayFileSegment, ArrayVariable): pass diff --git a/api/tests/unit_tests/core/app/segments/test_variables.py b/api/tests/unit_tests/core/app/segments/test_variables.py index 0c264c15a0..426557c716 100644 --- a/api/tests/unit_tests/core/app/segments/test_variables.py +++ b/api/tests/unit_tests/core/app/segments/test_variables.py @@ -2,6 +2,8 @@ import pytest from pydantic import ValidationError from core.variables import ( + ArrayFileVariable, + ArrayVariable, FloatVariable, IntegerVariable, ObjectVariable, @@ -81,3 +83,8 @@ def test_variable_to_object(): assert var.to_object() == 3.14 var = SecretVariable(name="secret", value="secret_value") assert var.to_object() == "secret_value" + + +def test_array_file_variable_is_array_variable(): + var = ArrayFileVariable(name="files", value=[]) + assert isinstance(var, ArrayVariable) From c91e8b1737f28c9997ce7bd9c1e8ac4e377ef1b7 Mon Sep 17 00:00:00 2001 From: Yi Xiao <54782454+YIXIAO0@users.noreply.github.com> Date: Tue, 24 Dec 2024 16:26:47 +0800 Subject: [PATCH 136/138] fix: modal bg color (#12042) --- web/app/components/base/modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/base/modal/index.tsx b/web/app/components/base/modal/index.tsx index 3040cdb00b..26cde5fce3 100644 --- a/web/app/components/base/modal/index.tsx +++ b/web/app/components/base/modal/index.tsx @@ -39,7 +39,7 @@ export default function Modal({ leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
Date: Tue, 24 Dec 2024 18:38:51 +0800 Subject: [PATCH 137/138] feat: mypy for all type check (#10921) --- .github/workflows/api-tests.yml | 6 + api/commands.py | 13 +- api/configs/feature/__init__.py | 14 +- api/configs/middleware/__init__.py | 4 - .../remote_settings_sources/apollo/client.py | 5 +- api/constants/model_template.py | 3 +- api/controllers/common/fields.py | 2 +- api/controllers/console/__init__.py | 95 +++++++- api/controllers/console/admin.py | 2 +- api/controllers/console/apikey.py | 23 +- .../console/app/advanced_prompt_template.py | 2 +- api/controllers/console/app/agent.py | 2 +- api/controllers/console/app/annotation.py | 6 +- api/controllers/console/app/app.py | 4 +- api/controllers/console/app/app_import.py | 4 +- api/controllers/console/app/audio.py | 2 +- api/controllers/console/app/completion.py | 4 +- api/controllers/console/app/conversation.py | 15 +- .../console/app/conversation_variables.py | 2 +- api/controllers/console/app/generator.py | 4 +- api/controllers/console/app/message.py | 6 +- api/controllers/console/app/model_config.py | 13 +- api/controllers/console/app/ops_trace.py | 2 +- api/controllers/console/app/site.py | 6 +- api/controllers/console/app/statistic.py | 4 +- api/controllers/console/app/workflow.py | 2 +- .../console/app/workflow_app_log.py | 4 +- api/controllers/console/app/workflow_run.py | 4 +- .../console/app/workflow_statistic.py | 4 +- api/controllers/console/app/wraps.py | 2 +- api/controllers/console/auth/activate.py | 6 +- .../console/auth/data_source_bearer_auth.py | 4 +- .../console/auth/data_source_oauth.py | 8 +- .../console/auth/forgot_password.py | 6 +- api/controllers/console/auth/login.py | 4 +- api/controllers/console/auth/oauth.py | 7 +- api/controllers/console/billing/billing.py | 4 +- .../console/datasets/data_source.py | 4 +- api/controllers/console/datasets/datasets.py | 6 +- .../console/datasets/datasets_document.py | 10 +- .../console/datasets/datasets_segments.py | 4 +- api/controllers/console/datasets/external.py | 4 +- .../console/datasets/hit_testing.py | 2 +- .../console/datasets/hit_testing_base.py | 4 +- api/controllers/console/datasets/website.py | 2 +- api/controllers/console/explore/audio.py | 9 +- api/controllers/console/explore/completion.py | 23 +- .../console/explore/conversation.py | 32 +-- .../console/explore/installed_app.py | 11 +- api/controllers/console/explore/message.py | 25 +- api/controllers/console/explore/parameter.py | 2 +- .../console/explore/recommended_app.py | 4 +- .../console/explore/saved_message.py | 6 +- api/controllers/console/explore/workflow.py | 9 +- api/controllers/console/explore/wraps.py | 4 +- api/controllers/console/extension.py | 4 +- api/controllers/console/feature.py | 4 +- api/controllers/console/files.py | 9 +- api/controllers/console/init_validate.py | 2 +- api/controllers/console/ping.py | 2 +- api/controllers/console/remote_files.py | 4 +- api/controllers/console/setup.py | 2 +- api/controllers/console/tag/tags.py | 6 +- api/controllers/console/version.py | 2 +- api/controllers/console/workspace/account.py | 4 +- .../workspace/load_balancing_config.py | 6 +- api/controllers/console/workspace/members.py | 31 +-- .../console/workspace/model_providers.py | 9 +- api/controllers/console/workspace/models.py | 6 +- .../console/workspace/tool_providers.py | 4 +- .../console/workspace/workspace.py | 14 +- api/controllers/console/wraps.py | 6 +- api/controllers/files/image_preview.py | 2 +- api/controllers/files/tool_files.py | 2 +- .../inner_api/workspace/workspace.py | 2 +- api/controllers/inner_api/wraps.py | 6 +- api/controllers/service_api/app/app.py | 2 +- api/controllers/service_api/app/audio.py | 4 +- api/controllers/service_api/app/completion.py | 2 +- .../service_api/app/conversation.py | 4 +- api/controllers/service_api/app/file.py | 2 +- api/controllers/service_api/app/message.py | 4 +- api/controllers/service_api/app/workflow.py | 4 +- .../service_api/dataset/dataset.py | 2 +- .../service_api/dataset/document.py | 2 +- .../service_api/dataset/segment.py | 4 +- api/controllers/service_api/index.py | 2 +- api/controllers/service_api/wraps.py | 10 +- api/controllers/web/app.py | 2 +- api/controllers/web/audio.py | 4 +- api/controllers/web/completion.py | 2 +- api/controllers/web/conversation.py | 4 +- api/controllers/web/feature.py | 2 +- api/controllers/web/files.py | 4 +- api/controllers/web/message.py | 4 +- api/controllers/web/passport.py | 2 +- api/controllers/web/remote_files.py | 2 +- api/controllers/web/saved_message.py | 4 +- api/controllers/web/site.py | 2 +- api/controllers/web/workflow.py | 2 +- api/controllers/web/wraps.py | 2 +- api/core/agent/base_agent_runner.py | 47 ++-- api/core/agent/cot_agent_runner.py | 82 ++++--- api/core/agent/cot_chat_agent_runner.py | 6 + api/core/agent/cot_completion_agent_runner.py | 21 +- api/core/agent/entities.py | 2 +- api/core/agent/fc_agent_runner.py | 46 ++-- .../agent/output_parser/cot_output_parser.py | 10 +- .../easy_ui_based_app/dataset/manager.py | 2 + .../easy_ui_based_app/model_config/manager.py | 2 +- .../features/opening_statement/manager.py | 4 +- .../app/apps/advanced_chat/app_generator.py | 5 +- .../app_generator_tts_publisher.py | 23 +- api/core/app/apps/advanced_chat/app_runner.py | 10 +- .../advanced_chat/generate_task_pipeline.py | 28 ++- .../app/apps/agent_chat/app_config_manager.py | 2 +- api/core/app/apps/agent_chat/app_generator.py | 13 +- api/core/app/apps/agent_chat/app_runner.py | 19 +- .../agent_chat/generate_response_converter.py | 14 +- api/core/app/apps/base_app_queue_manager.py | 2 +- api/core/app/apps/base_app_runner.py | 45 ++-- api/core/app/apps/chat/app_generator.py | 11 +- .../apps/chat/generate_response_converter.py | 10 +- .../app/apps/completion/app_config_manager.py | 2 +- api/core/app/apps/completion/app_generator.py | 18 +- api/core/app/apps/completion/app_runner.py | 4 +- .../completion/generate_response_converter.py | 10 +- .../app/apps/message_based_app_generator.py | 12 +- api/core/app/apps/workflow/app_generator.py | 2 +- .../workflow/generate_response_converter.py | 12 +- api/core/app/apps/workflow_app_runner.py | 14 +- api/core/app/entities/app_invoke_entities.py | 4 +- api/core/app/entities/queue_entities.py | 2 +- api/core/app/entities/task_entities.py | 14 +- .../annotation_reply/annotation_reply.py | 2 +- .../app/features/rate_limiting/rate_limit.py | 2 +- .../based_generate_task_pipeline.py | 2 + .../easy_ui_based_generate_task_pipeline.py | 51 ++-- .../app/task_pipeline/message_cycle_manage.py | 2 +- .../task_pipeline/workflow_cycle_manage.py | 22 +- .../agent_tool_callback_handler.py | 2 +- .../index_tool_callback_handler.py | 17 +- api/core/entities/model_entities.py | 3 +- api/core/entities/provider_configuration.py | 100 ++++---- .../api_based_extension_requestor.py | 6 +- api/core/extension/extensible.py | 9 +- api/core/extension/extension.py | 10 +- api/core/external_data_tool/api/api.py | 3 + .../external_data_tool/external_data_fetch.py | 24 +- api/core/external_data_tool/factory.py | 10 +- api/core/file/file_manager.py | 5 +- api/core/file/tool_file_parser.py | 4 +- .../helper/code_executor/code_executor.py | 16 +- .../code_executor/jinja2/jinja2_formatter.py | 7 +- .../code_executor/template_transformer.py | 3 +- api/core/helper/lru_cache.py | 2 +- api/core/helper/model_provider_cache.py | 2 +- api/core/helper/moderation.py | 4 +- api/core/helper/module_import_helper.py | 13 +- api/core/helper/tool_parameter_cache.py | 2 +- api/core/helper/tool_provider_cache.py | 2 +- api/core/hosting_configuration.py | 14 +- api/core/indexing_runner.py | 66 ++--- api/core/llm_generator/llm_generator.py | 89 ++++--- api/core/memory/token_buffer_memory.py | 2 +- api/core/model_manager.py | 149 +++++++----- .../callbacks/logging_callback.py | 13 +- .../entities/message_entities.py | 3 +- .../model_providers/__base/ai_model.py | 13 +- .../__base/large_language_model.py | 14 +- .../model_providers/__base/model_provider.py | 3 +- .../__base/text_embedding_model.py | 6 +- .../__base/tokenizers/gpt2_tokenzier.py | 4 +- .../model_providers/__base/tts_model.py | 9 +- .../azure_openai/speech2text/speech2text.py | 4 +- .../model_providers/azure_openai/tts/tts.py | 2 + .../model_providers/bedrock/llm/llm.py | 6 +- .../model_providers/cohere/rerank/rerank.py | 4 +- .../model_providers/fireworks/_common.py | 4 +- .../text_embedding/text_embedding.py | 3 +- .../model_providers/gitee_ai/_common.py | 2 +- .../model_providers/gitee_ai/rerank/rerank.py | 4 +- .../gitee_ai/text_embedding/text_embedding.py | 2 +- .../model_providers/gitee_ai/tts/tts.py | 8 +- .../model_providers/google/llm/llm.py | 2 +- .../huggingface_hub/_common.py | 2 +- .../huggingface_hub/llm/llm.py | 6 +- .../text_embedding/text_embedding.py | 2 +- .../model_providers/hunyuan/llm/llm.py | 12 +- .../hunyuan/text_embedding/text_embedding.py | 10 +- .../jina/text_embedding/jina_tokenizer.py | 4 +- .../minimax/llm/chat_completion.py | 30 +-- .../minimax/llm/chat_completion_pro.py | 26 +- .../model_providers/minimax/llm/types.py | 4 +- .../nomic/text_embedding/text_embedding.py | 4 +- .../model_providers/oci/llm/llm.py | 4 +- .../oci/text_embedding/text_embedding.py | 2 +- .../ollama/text_embedding/text_embedding.py | 1 + .../model_providers/openai/_common.py | 4 +- .../openai/moderation/moderation.py | 6 +- .../model_providers/openai/openai.py | 3 +- .../speech2text/speech2text.py | 1 + .../text_embedding/text_embedding.py | 1 + .../openai_api_compatible/tts/tts.py | 1 + .../openllm/llm/openllm_generate.py | 16 +- .../text_embedding/text_embedding.py | 7 +- .../model_providers/replicate/_common.py | 2 +- .../model_providers/replicate/llm/llm.py | 6 +- .../text_embedding/text_embedding.py | 6 +- .../model_providers/sagemaker/llm/llm.py | 8 +- .../sagemaker/rerank/rerank.py | 3 +- .../sagemaker/speech2text/speech2text.py | 3 +- .../text_embedding/text_embedding.py | 3 +- .../model_providers/sagemaker/tts/tts.py | 2 +- .../model_providers/siliconflow/llm/llm.py | 2 +- .../model_providers/spark/llm/llm.py | 4 +- .../model_providers/togetherai/llm/llm.py | 3 +- .../model_providers/tongyi/_common.py | 2 +- .../model_providers/tongyi/llm/llm.py | 6 +- .../model_providers/tongyi/rerank/rerank.py | 8 +- .../tongyi/text_embedding/text_embedding.py | 2 +- .../model_providers/tongyi/tts/tts.py | 8 +- .../model_providers/upstage/_common.py | 4 +- .../model_providers/upstage/llm/llm.py | 2 +- .../upstage/text_embedding/text_embedding.py | 5 +- .../model_providers/vertex_ai/_common.py | 2 +- .../model_providers/vertex_ai/llm/llm.py | 2 +- .../model_providers/vessl_ai/llm/llm.py | 4 +- .../model_providers/volcengine_maas/client.py | 12 +- .../volcengine_maas/legacy/errors.py | 3 +- .../volcengine_maas/llm/llm.py | 2 +- .../volcengine_maas/llm/models.py | 6 +- .../model_providers/wenxin/llm/ernie_bot.py | 5 +- .../wenxin/text_embedding/text_embedding.py | 9 +- .../model_providers/xinference/llm/llm.py | 2 +- .../xinference/rerank/rerank.py | 2 +- .../xinference/speech2text/speech2text.py | 2 +- .../text_embedding/text_embedding.py | 4 +- .../model_providers/xinference/tts/tts.py | 7 +- .../xinference/xinference_helper.py | 12 +- .../model_providers/yi/llm/llm.py | 2 +- .../model_providers/zhipuai/llm/llm.py | 6 +- .../zhipuai/text_embedding/text_embedding.py | 2 +- .../schema_validators/common_validator.py | 7 +- api/core/model_runtime/utils/encoders.py | 3 +- api/core/model_runtime/utils/helper.py | 3 +- api/core/moderation/api/api.py | 14 +- api/core/moderation/base.py | 4 +- api/core/moderation/factory.py | 3 +- api/core/moderation/input_moderation.py | 8 +- api/core/moderation/keywords/keywords.py | 6 +- .../openai_moderation/openai_moderation.py | 4 + api/core/moderation/output_moderation.py | 2 +- api/core/ops/entities/trace_entity.py | 5 +- api/core/ops/langfuse_trace/langfuse_trace.py | 12 +- .../entities/langsmith_trace_entity.py | 1 - .../ops/langsmith_trace/langsmith_trace.py | 155 ++++++++++-- api/core/ops/ops_trace_manager.py | 52 ++-- api/core/prompt/advanced_prompt_transform.py | 37 +-- .../prompt/agent_history_prompt_transform.py | 2 +- api/core/prompt/prompt_transform.py | 22 +- api/core/prompt/simple_prompt_transform.py | 25 +- api/core/prompt/utils/prompt_message_util.py | 8 +- .../prompt/utils/prompt_template_parser.py | 3 +- api/core/provider_manager.py | 37 ++- .../rag/datasource/keyword/jieba/jieba.py | 37 ++- .../jieba/jieba_keyword_table_handler.py | 4 +- .../rag/datasource/keyword/keyword_base.py | 4 +- api/core/rag/datasource/retrieval_service.py | 23 +- .../vdb/analyticdb/analyticdb_vector.py | 35 +-- .../analyticdb/analyticdb_vector_openapi.py | 37 +-- .../vdb/analyticdb/analyticdb_vector_sql.py | 24 +- .../rag/datasource/vdb/baidu/baidu_vector.py | 28 ++- .../datasource/vdb/chroma/chroma_vector.py | 24 +- .../vdb/couchbase/couchbase_vector.py | 24 +- .../vdb/elasticsearch/elasticsearch_vector.py | 22 +- .../datasource/vdb/lindorm/lindorm_vector.py | 33 ++- .../datasource/vdb/milvus/milvus_vector.py | 22 +- .../datasource/vdb/myscale/myscale_vector.py | 19 +- .../vdb/oceanbase/oceanbase_vector.py | 4 +- .../vdb/opensearch/opensearch_vector.py | 4 +- .../rag/datasource/vdb/oracle/oraclevector.py | 42 ++-- .../datasource/vdb/pgvecto_rs/pgvecto_rs.py | 14 +- .../rag/datasource/vdb/pgvector/pgvector.py | 31 +-- .../datasource/vdb/qdrant/qdrant_vector.py | 21 +- .../rag/datasource/vdb/relyt/relyt_vector.py | 26 +- .../datasource/vdb/tencent/tencent_vector.py | 30 +-- .../tidb_on_qdrant/tidb_on_qdrant_vector.py | 31 ++- .../vdb/tidb_on_qdrant/tidb_service.py | 11 +- .../datasource/vdb/tidb_vector/tidb_vector.py | 12 +- api/core/rag/datasource/vdb/vector_base.py | 11 +- api/core/rag/datasource/vdb/vector_factory.py | 9 +- .../vdb/vikingdb/vikingdb_vector.py | 11 +- .../vdb/weaviate/weaviate_vector.py | 14 +- api/core/rag/docstore/dataset_docstore.py | 9 +- api/core/rag/embedding/cached_embedding.py | 16 +- .../rag/extractor/entity/extract_setting.py | 2 +- api/core/rag/extractor/excel_extractor.py | 8 +- api/core/rag/extractor/extract_processor.py | 20 +- .../rag/extractor/firecrawl/firecrawl_app.py | 21 +- api/core/rag/extractor/html_extractor.py | 3 +- api/core/rag/extractor/notion_extractor.py | 14 +- api/core/rag/extractor/pdf_extractor.py | 6 +- .../unstructured_eml_extractor.py | 2 +- .../unstructured_epub_extractor.py | 3 + .../unstructured_ppt_extractor.py | 4 +- .../unstructured_pptx_extractor.py | 11 +- api/core/rag/extractor/word_extractor.py | 6 + .../index_processor/index_processor_base.py | 1 + .../index_processor_factory.py | 2 +- .../processor/paragraph_index_processor.py | 10 +- .../processor/qa_index_processor.py | 27 +- api/core/rag/rerank/rerank_model.py | 11 +- api/core/rag/rerank/weight_rerank.py | 16 +- api/core/rag/retrieval/dataset_retrieval.py | 111 +++++---- .../multi_dataset_function_call_router.py | 16 +- .../router/multi_dataset_react_route.py | 22 +- api/core/rag/splitter/fixed_text_splitter.py | 4 +- api/core/rag/splitter/text_splitter.py | 4 +- api/core/tools/entities/api_entities.py | 2 +- api/core/tools/entities/tool_bundle.py | 2 +- api/core/tools/entities/tool_entities.py | 12 +- api/core/tools/provider/api_tool_provider.py | 70 +++--- api/core/tools/provider/app_tool_provider.py | 15 +- api/core/tools/provider/builtin/_positions.py | 2 +- .../provider/builtin/aippt/tools/aippt.py | 36 +-- .../builtin/arxiv/tools/arxiv_search.py | 2 +- .../tools/provider/builtin/audio/tools/tts.py | 20 +- .../builtin/aws/tools/apply_guardrail.py | 4 +- .../aws/tools/lambda_translate_utils.py | 2 +- .../builtin/aws/tools/lambda_yaml_to_json.py | 2 +- .../aws/tools/sagemaker_text_rerank.py | 6 +- .../builtin/aws/tools/sagemaker_tts.py | 4 +- .../builtin/cogview/tools/cogvideo.py | 2 +- .../builtin/cogview/tools/cogvideo_job.py | 2 +- .../builtin/cogview/tools/cogview3.py | 2 +- .../feishu_base/tools/search_records.py | 20 +- .../feishu_base/tools/update_records.py | 12 +- .../tools/add_event_attendees.py | 8 +- .../feishu_calendar/tools/delete_event.py | 6 +- .../tools/get_primary_calendar.py | 4 + .../feishu_calendar/tools/list_events.py | 12 +- .../feishu_calendar/tools/update_event.py | 14 +- .../feishu_document/tools/create_document.py | 10 +- .../tools/list_document_blocks.py | 6 +- .../builtin/json_process/tools/delete.py | 2 +- .../builtin/json_process/tools/insert.py | 2 +- .../builtin/json_process/tools/parse.py | 2 +- .../builtin/json_process/tools/replace.py | 2 +- .../builtin/maths/tools/eval_expression.py | 2 +- .../builtin/novitaai/_novita_tool_base.py | 2 +- .../novitaai/tools/novitaai_createtile.py | 2 +- .../novitaai/tools/novitaai_txt2img.py | 2 +- .../tools/podcast_audio_generator.py | 2 +- .../builtin/qrcode/tools/qrcode_generator.py | 8 +- .../builtin/transcript/tools/transcript.py | 2 +- .../builtin/twilio/tools/send_message.py | 2 +- .../tools/provider/builtin/twilio/twilio.py | 4 +- .../provider/builtin/vanna/tools/vanna.py | 5 +- .../wikipedia/tools/wikipedia_search.py | 2 +- .../provider/builtin/yahoo/tools/analytics.py | 2 +- .../provider/builtin/yahoo/tools/news.py | 2 +- .../provider/builtin/yahoo/tools/ticker.py | 2 +- .../provider/builtin/youtube/tools/videos.py | 2 +- .../tools/provider/builtin_tool_provider.py | 70 +++--- api/core/tools/provider/tool_provider.py | 63 ++--- .../tools/provider/workflow_tool_provider.py | 13 +- api/core/tools/tool/api_tool.py | 14 +- api/core/tools/tool/builtin_tool.py | 35 ++- .../dataset_multi_retriever_tool.py | 13 +- .../dataset_retriever_base_tool.py | 2 +- .../dataset_retriever_tool.py | 57 +++-- api/core/tools/tool/dataset_retriever_tool.py | 11 +- api/core/tools/tool/tool.py | 17 +- api/core/tools/tool/workflow_tool.py | 14 +- api/core/tools/tool_engine.py | 24 +- api/core/tools/tool_label_manager.py | 8 +- api/core/tools/tool_manager.py | 126 ++++++---- api/core/tools/utils/configuration.py | 18 +- api/core/tools/utils/feishu_api_utils.py | 179 ++++++++------ api/core/tools/utils/lark_api_utils.py | 193 +++++++++------ api/core/tools/utils/message_transformer.py | 12 +- .../tools/utils/model_invocation_utils.py | 23 +- api/core/tools/utils/parser.py | 17 +- api/core/tools/utils/web_reader_tool.py | 15 +- .../utils/workflow_configuration_sync.py | 4 +- api/core/tools/utils/yaml_utils.py | 2 +- api/core/variables/variables.py | 3 +- .../callbacks/workflow_logging_callback.py | 2 +- api/core/workflow/entities/node_entities.py | 4 +- .../condition_handlers/condition_handler.py | 2 +- .../workflow/graph_engine/entities/graph.py | 16 +- .../workflow/graph_engine/graph_engine.py | 55 +++-- .../nodes/answer/answer_stream_processor.py | 4 +- .../nodes/answer/base_stream_processor.py | 8 +- api/core/workflow/nodes/base/entities.py | 5 +- api/core/workflow/nodes/code/code_node.py | 2 +- api/core/workflow/nodes/code/entities.py | 2 +- .../workflow/nodes/document_extractor/node.py | 7 +- .../nodes/end/end_stream_generate_router.py | 5 +- .../nodes/end/end_stream_processor.py | 2 +- api/core/workflow/nodes/event/event.py | 2 +- .../workflow/nodes/http_request/executor.py | 44 ++-- api/core/workflow/nodes/http_request/node.py | 8 +- .../nodes/iteration/iteration_node.py | 15 +- .../knowledge_retrieval_node.py | 16 +- api/core/workflow/nodes/list_operator/node.py | 34 +-- api/core/workflow/nodes/llm/node.py | 17 +- api/core/workflow/nodes/loop/loop_node.py | 6 +- .../nodes/parameter_extractor/entities.py | 4 +- .../parameter_extractor_node.py | 16 +- .../nodes/parameter_extractor/prompts.py | 4 +- .../question_classifier_node.py | 12 +- api/core/workflow/nodes/tool/tool_node.py | 9 +- .../nodes/variable_assigner/v1/node.py | 2 + .../nodes/variable_assigner/v2/node.py | 6 +- api/core/workflow/workflow_entry.py | 15 +- .../event_handlers/create_document_index.py | 2 +- .../create_site_record_when_app_created.py | 29 +-- .../deduct_quota_when_message_created.py | 2 +- ...rameters_cache_when_sync_draft_workflow.py | 5 +- ...aset_join_when_app_model_config_updated.py | 10 +- ...oin_when_app_published_workflow_updated.py | 10 +- api/extensions/__init__.py | 0 api/extensions/ext_app_metrics.py | 14 +- api/extensions/ext_celery.py | 6 +- api/extensions/ext_compress.py | 2 +- api/extensions/ext_logging.py | 5 +- api/extensions/ext_login.py | 2 +- api/extensions/ext_mail.py | 8 +- api/extensions/ext_migrate.py | 2 +- api/extensions/ext_proxy_fix.py | 2 +- api/extensions/ext_sentry.py | 2 +- api/extensions/ext_storage.py | 8 +- api/extensions/storage/aliyun_oss_storage.py | 12 +- api/extensions/storage/aws_s3_storage.py | 8 +- api/extensions/storage/azure_blob_storage.py | 8 +- api/extensions/storage/baidu_obs_storage.py | 9 +- .../storage/google_cloud_storage.py | 4 +- api/extensions/storage/huawei_obs_storage.py | 4 +- api/extensions/storage/opendal_storage.py | 8 +- api/extensions/storage/oracle_oci_storage.py | 6 +- api/extensions/storage/supabase_storage.py | 2 +- api/extensions/storage/tencent_cos_storage.py | 4 +- .../storage/volcengine_tos_storage.py | 4 +- api/factories/__init__.py | 0 api/factories/file_factory.py | 5 +- api/factories/variable_factory.py | 21 +- api/fields/annotation_fields.py | 2 +- api/fields/api_based_extension_fields.py | 2 +- api/fields/app_fields.py | 2 +- api/fields/conversation_fields.py | 2 +- api/fields/conversation_variable_fields.py | 2 +- api/fields/data_source_fields.py | 2 +- api/fields/dataset_fields.py | 2 +- api/fields/document_fields.py | 2 +- api/fields/end_user_fields.py | 2 +- api/fields/external_dataset_fields.py | 2 +- api/fields/file_fields.py | 2 +- api/fields/hit_testing_fields.py | 2 +- api/fields/installed_app_fields.py | 2 +- api/fields/member_fields.py | 2 +- api/fields/message_fields.py | 2 +- api/fields/raws.py | 2 +- api/fields/segment_fields.py | 2 +- api/fields/tag_fields.py | 2 +- api/fields/workflow_app_log_fields.py | 2 +- api/fields/workflow_fields.py | 2 +- api/fields/workflow_run_fields.py | 2 +- api/libs/external_api.py | 7 +- api/libs/gmpy2_pkcs10aep_cipher.py | 8 +- api/libs/helper.py | 6 +- api/libs/json_in_md_parser.py | 1 + api/libs/login.py | 15 +- api/libs/oauth.py | 6 +- api/libs/oauth_data_source.py | 7 +- api/libs/threadings_utils.py | 4 +- api/models/account.py | 32 +-- api/models/api_based_extension.py | 2 +- api/models/dataset.py | 33 +-- api/models/model.py | 73 +++--- api/models/provider.py | 14 +- api/models/source.py | 4 +- api/models/task.py | 6 +- api/models/tools.py | 22 +- api/models/web.py | 4 +- api/models/workflow.py | 19 +- api/mypy.ini | 10 + api/poetry.lock | 230 ++++++++++++------ api/pyproject.toml | 3 + api/schedule/clean_messages.py | 3 +- api/schedule/clean_unused_datasets_task.py | 6 +- api/schedule/create_tidb_serverless_task.py | 15 +- .../update_tidb_serverless_status_task.py | 13 +- api/services/account_service.py | 31 ++- .../advanced_prompt_template_service.py | 4 + api/services/agent_service.py | 13 +- api/services/annotation_service.py | 14 +- api/services/app_dsl_service.py | 9 +- api/services/app_generate_service.py | 4 +- api/services/app_service.py | 24 +- api/services/audio_service.py | 6 + api/services/auth/firecrawl/firecrawl.py | 4 +- api/services/auth/jina.py | 2 +- api/services/auth/jina/jina.py | 2 +- api/services/billing_service.py | 6 +- api/services/conversation_service.py | 3 +- api/services/dataset_service.py | 42 +++- api/services/enterprise/base.py | 4 +- .../entities/model_provider_entities.py | 8 +- api/services/external_knowledge_service.py | 39 +-- api/services/file_service.py | 6 +- api/services/hit_testing_service.py | 17 +- api/services/knowledge_service.py | 2 +- api/services/message_service.py | 6 +- api/services/model_load_balancing_service.py | 49 ++-- api/services/model_provider_service.py | 25 +- api/services/moderation_service.py | 4 +- api/services/ops_service.py | 26 +- .../buildin/buildin_retrieval.py | 8 +- .../recommend_app/remote/remote_retrieval.py | 6 +- api/services/recommended_app_service.py | 2 +- api/services/saved_message_service.py | 6 + api/services/tag_service.py | 4 +- .../tools/api_tools_manage_service.py | 33 +-- .../tools/builtin_tools_manage_service.py | 8 +- api/services/tools/tools_transform_service.py | 34 ++- .../tools/workflow_tools_manage_service.py | 63 +++-- api/services/web_conversation_service.py | 6 + api/services/website_service.py | 33 ++- api/services/workflow/workflow_converter.py | 18 +- api/services/workflow_run_service.py | 4 +- api/services/workflow_service.py | 6 +- api/services/workspace_service.py | 3 +- api/tasks/__init__.py | 0 api/tasks/add_document_to_index_task.py | 2 +- .../add_annotation_to_index_task.py | 2 +- .../batch_import_annotations_task.py | 2 +- .../delete_annotation_index_task.py | 2 +- .../disable_annotation_reply_task.py | 2 +- .../enable_annotation_reply_task.py | 2 +- .../update_annotation_to_index_task.py | 2 +- .../batch_create_segment_to_index_task.py | 10 +- api/tasks/clean_dataset_task.py | 4 +- api/tasks/clean_document_task.py | 4 +- api/tasks/clean_notion_document_task.py | 2 +- api/tasks/create_segment_to_index_task.py | 2 +- api/tasks/deal_dataset_vector_index_task.py | 2 +- api/tasks/delete_segment_from_index_task.py | 2 +- api/tasks/disable_segment_from_index_task.py | 2 +- api/tasks/document_indexing_sync_task.py | 2 +- api/tasks/document_indexing_task.py | 2 +- api/tasks/document_indexing_update_task.py | 2 +- api/tasks/duplicate_document_indexing_task.py | 4 +- api/tasks/enable_segment_to_index_task.py | 2 +- api/tasks/mail_email_code_login.py | 2 +- api/tasks/mail_invite_member_task.py | 2 +- api/tasks/mail_reset_password_task.py | 2 +- api/tasks/ops_trace_task.py | 2 +- api/tasks/recover_document_indexing_task.py | 2 +- api/tasks/remove_app_and_related_data_task.py | 2 +- api/tasks/remove_document_from_index_task.py | 2 +- api/tasks/retry_document_indexing_task.py | 45 ++-- .../sync_website_document_indexing_task.py | 42 ++-- .../dependencies/test_dependencies_sorted.py | 4 +- .../controllers/test_controllers.py | 2 +- .../model_runtime/__mock/google.py | 4 +- .../model_runtime/__mock/huggingface.py | 2 +- .../model_runtime/__mock/huggingface_chat.py | 6 +- .../model_runtime/__mock/nomic_embeddings.py | 2 +- .../model_runtime/__mock/xinference.py | 4 +- .../model_runtime/tongyi/test_rerank.py | 2 +- .../tools/__mock_server/openapi_todo.py | 2 +- .../vdb/__mock/baiduvectordb.py | 10 +- .../vdb/__mock/tcvectordb.py | 12 +- .../integration_tests/vdb/__mock/vikingdb.py | 2 +- api/tests/unit_tests/oss/__mock/aliyun_oss.py | 4 +- .../unit_tests/oss/__mock/tencent_cos.py | 4 +- .../unit_tests/oss/__mock/volcengine_tos.py | 4 +- .../aliyun_oss/aliyun_oss/test_aliyun_oss.py | 2 +- .../oss/tencent_cos/test_tencent_cos.py | 2 +- .../oss/volcengine_tos/test_volcengine_tos.py | 2 +- .../unit_tests/utils/yaml/test_yaml_utils.py | 2 +- sdks/python-client/dify_client/client.py | 27 +- 584 files changed, 3975 insertions(+), 2826 deletions(-) create mode 100644 api/extensions/__init__.py create mode 100644 api/factories/__init__.py create mode 100644 api/mypy.ini create mode 100644 api/tasks/__init__.py diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index 2cd0b2a7d4..fd98db24b9 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -56,6 +56,12 @@ jobs: - name: Run Tool run: poetry run -C api bash dev/pytest/pytest_tools.sh + - name: Run mypy + run: | + pushd api + poetry run python -m mypy --install-types --non-interactive . + popd + - name: Set up dotenvs run: | cp docker/.env.example docker/.env diff --git a/api/commands.py b/api/commands.py index bf013cc77e..ad7ad972f3 100644 --- a/api/commands.py +++ b/api/commands.py @@ -159,8 +159,7 @@ def migrate_annotation_vector_database(): try: # get apps info apps = ( - db.session.query(App) - .filter(App.status == "normal") + App.query.filter(App.status == "normal") .order_by(App.created_at.desc()) .paginate(page=page, per_page=50) ) @@ -285,8 +284,7 @@ def migrate_knowledge_vector_database(): while True: try: datasets = ( - db.session.query(Dataset) - .filter(Dataset.indexing_technique == "high_quality") + Dataset.query.filter(Dataset.indexing_technique == "high_quality") .order_by(Dataset.created_at.desc()) .paginate(page=page, per_page=50) ) @@ -450,7 +448,8 @@ def convert_to_agent_apps(): if app_id not in proceeded_app_ids: proceeded_app_ids.append(app_id) app = db.session.query(App).filter(App.id == app_id).first() - apps.append(app) + if app is not None: + apps.append(app) if len(apps) == 0: break @@ -621,6 +620,10 @@ where sites.id is null limit 1000""" try: app = db.session.query(App).filter(App.id == app_id).first() + if not app: + print(f"App {app_id} not found") + continue + tenant = app.tenant if tenant: accounts = tenant.get_accounts() diff --git a/api/configs/feature/__init__.py b/api/configs/feature/__init__.py index 73f8a95989..74cdf94486 100644 --- a/api/configs/feature/__init__.py +++ b/api/configs/feature/__init__.py @@ -239,7 +239,6 @@ class HttpConfig(BaseSettings): ) @computed_field - @property def CONSOLE_CORS_ALLOW_ORIGINS(self) -> list[str]: return self.inner_CONSOLE_CORS_ALLOW_ORIGINS.split(",") @@ -250,7 +249,6 @@ class HttpConfig(BaseSettings): ) @computed_field - @property def WEB_API_CORS_ALLOW_ORIGINS(self) -> list[str]: return self.inner_WEB_API_CORS_ALLOW_ORIGINS.split(",") @@ -715,27 +713,27 @@ class PositionConfig(BaseSettings): default="", ) - @computed_field + @property def POSITION_PROVIDER_PINS_LIST(self) -> list[str]: return [item.strip() for item in self.POSITION_PROVIDER_PINS.split(",") if item.strip() != ""] - @computed_field + @property def POSITION_PROVIDER_INCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_PROVIDER_INCLUDES.split(",") if item.strip() != ""} - @computed_field + @property def POSITION_PROVIDER_EXCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_PROVIDER_EXCLUDES.split(",") if item.strip() != ""} - @computed_field + @property def POSITION_TOOL_PINS_LIST(self) -> list[str]: return [item.strip() for item in self.POSITION_TOOL_PINS.split(",") if item.strip() != ""] - @computed_field + @property def POSITION_TOOL_INCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_TOOL_INCLUDES.split(",") if item.strip() != ""} - @computed_field + @property def POSITION_TOOL_EXCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_TOOL_EXCLUDES.split(",") if item.strip() != ""} diff --git a/api/configs/middleware/__init__.py b/api/configs/middleware/__init__.py index 9265a48d9b..f6a44eaa47 100644 --- a/api/configs/middleware/__init__.py +++ b/api/configs/middleware/__init__.py @@ -130,7 +130,6 @@ class DatabaseConfig(BaseSettings): ) @computed_field - @property def SQLALCHEMY_DATABASE_URI(self) -> str: db_extras = ( f"{self.DB_EXTRAS}&client_encoding={self.DB_CHARSET}" if self.DB_CHARSET else self.DB_EXTRAS @@ -168,7 +167,6 @@ class DatabaseConfig(BaseSettings): ) @computed_field - @property def SQLALCHEMY_ENGINE_OPTIONS(self) -> dict[str, Any]: return { "pool_size": self.SQLALCHEMY_POOL_SIZE, @@ -206,7 +204,6 @@ class CeleryConfig(DatabaseConfig): ) @computed_field - @property def CELERY_RESULT_BACKEND(self) -> str | None: return ( "db+{}".format(self.SQLALCHEMY_DATABASE_URI) @@ -214,7 +211,6 @@ class CeleryConfig(DatabaseConfig): else self.CELERY_BROKER_URL ) - @computed_field @property def BROKER_USE_SSL(self) -> bool: return self.CELERY_BROKER_URL.startswith("rediss://") if self.CELERY_BROKER_URL else False diff --git a/api/configs/remote_settings_sources/apollo/client.py b/api/configs/remote_settings_sources/apollo/client.py index d1f6781ed3..03c64ea00f 100644 --- a/api/configs/remote_settings_sources/apollo/client.py +++ b/api/configs/remote_settings_sources/apollo/client.py @@ -4,6 +4,7 @@ import logging import os import threading import time +from collections.abc import Mapping from pathlib import Path from .python_3x import http_request, makedirs_wrapper @@ -255,8 +256,8 @@ class ApolloClient: logger.info("stopped, long_poll") # add the need for endorsement to the header - def _sign_headers(self, url): - headers = {} + def _sign_headers(self, url: str) -> Mapping[str, str]: + headers: dict[str, str] = {} if self.secret == "": return headers uri = url[len(self.config_url) : len(url)] diff --git a/api/constants/model_template.py b/api/constants/model_template.py index 7e1a196356..c26d8c0186 100644 --- a/api/constants/model_template.py +++ b/api/constants/model_template.py @@ -1,8 +1,9 @@ import json +from collections.abc import Mapping from models.model import AppMode -default_app_templates = { +default_app_templates: Mapping[AppMode, Mapping] = { # workflow default mode AppMode.WORKFLOW: { "app": { diff --git a/api/controllers/common/fields.py b/api/controllers/common/fields.py index 79869916ed..b1ebc444a5 100644 --- a/api/controllers/common/fields.py +++ b/api/controllers/common/fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore parameters__system_parameters = { "image_file_size_limit": fields.Integer, diff --git a/api/controllers/console/__init__.py b/api/controllers/console/__init__.py index f46d5b6b13..cb6b0d097b 100644 --- a/api/controllers/console/__init__.py +++ b/api/controllers/console/__init__.py @@ -3,6 +3,25 @@ from flask import Blueprint from libs.external_api import ExternalApi from .app.app_import import AppImportApi, AppImportConfirmApi +from .explore.audio import ChatAudioApi, ChatTextApi +from .explore.completion import ChatApi, ChatStopApi, CompletionApi, CompletionStopApi +from .explore.conversation import ( + ConversationApi, + ConversationListApi, + ConversationPinApi, + ConversationRenameApi, + ConversationUnPinApi, +) +from .explore.message import ( + MessageFeedbackApi, + MessageListApi, + MessageMoreLikeThisApi, + MessageSuggestedQuestionApi, +) +from .explore.workflow import ( + InstalledAppWorkflowRunApi, + InstalledAppWorkflowTaskStopApi, +) from .files import FileApi, FilePreviewApi, FileSupportTypeApi from .remote_files import RemoteFileInfoApi, RemoteFileUploadApi @@ -66,15 +85,81 @@ from .datasets import ( # Import explore controllers from .explore import ( - audio, - completion, - conversation, installed_app, - message, parameter, recommended_app, saved_message, - workflow, +) + +# Explore Audio +api.add_resource(ChatAudioApi, "/installed-apps//audio-to-text", endpoint="installed_app_audio") +api.add_resource(ChatTextApi, "/installed-apps//text-to-audio", endpoint="installed_app_text") + +# Explore Completion +api.add_resource( + CompletionApi, "/installed-apps//completion-messages", endpoint="installed_app_completion" +) +api.add_resource( + CompletionStopApi, + "/installed-apps//completion-messages//stop", + endpoint="installed_app_stop_completion", +) +api.add_resource( + ChatApi, "/installed-apps//chat-messages", endpoint="installed_app_chat_completion" +) +api.add_resource( + ChatStopApi, + "/installed-apps//chat-messages//stop", + endpoint="installed_app_stop_chat_completion", +) + +# Explore Conversation +api.add_resource( + ConversationRenameApi, + "/installed-apps//conversations//name", + endpoint="installed_app_conversation_rename", +) +api.add_resource( + ConversationListApi, "/installed-apps//conversations", endpoint="installed_app_conversations" +) +api.add_resource( + ConversationApi, + "/installed-apps//conversations/", + endpoint="installed_app_conversation", +) +api.add_resource( + ConversationPinApi, + "/installed-apps//conversations//pin", + endpoint="installed_app_conversation_pin", +) +api.add_resource( + ConversationUnPinApi, + "/installed-apps//conversations//unpin", + endpoint="installed_app_conversation_unpin", +) + + +# Explore Message +api.add_resource(MessageListApi, "/installed-apps//messages", endpoint="installed_app_messages") +api.add_resource( + MessageFeedbackApi, + "/installed-apps//messages//feedbacks", + endpoint="installed_app_message_feedback", +) +api.add_resource( + MessageMoreLikeThisApi, + "/installed-apps//messages//more-like-this", + endpoint="installed_app_more_like_this", +) +api.add_resource( + MessageSuggestedQuestionApi, + "/installed-apps//messages//suggested-questions", + endpoint="installed_app_suggested_question", +) +# Explore Workflow +api.add_resource(InstalledAppWorkflowRunApi, "/installed-apps//workflows/run") +api.add_resource( + InstalledAppWorkflowTaskStopApi, "/installed-apps//workflows/tasks//stop" ) # Import tag controllers diff --git a/api/controllers/console/admin.py b/api/controllers/console/admin.py index 8c0bf8710d..52e0bb6c56 100644 --- a/api/controllers/console/admin.py +++ b/api/controllers/console/admin.py @@ -1,7 +1,7 @@ from functools import wraps from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import NotFound, Unauthorized from configs import dify_config diff --git a/api/controllers/console/apikey.py b/api/controllers/console/apikey.py index 9537708689..ca8ddc3209 100644 --- a/api/controllers/console/apikey.py +++ b/api/controllers/console/apikey.py @@ -1,5 +1,7 @@ -import flask_restful -from flask_login import current_user +from typing import Any + +import flask_restful # type: ignore +from flask_login import current_user # type: ignore from flask_restful import Resource, fields, marshal_with from werkzeug.exceptions import Forbidden @@ -35,14 +37,15 @@ def _get_resource(resource_id, tenant_id, resource_model): class BaseApiKeyListResource(Resource): method_decorators = [account_initialization_required, login_required, setup_required] - resource_type = None - resource_model = None - resource_id_field = None - token_prefix = None + resource_type: str | None = None + resource_model: Any = None + resource_id_field: str | None = None + token_prefix: str | None = None max_keys = 10 @marshal_with(api_key_list) def get(self, resource_id): + assert self.resource_id_field is not None, "resource_id_field must be set" resource_id = str(resource_id) _get_resource(resource_id, current_user.current_tenant_id, self.resource_model) keys = ( @@ -54,6 +57,7 @@ class BaseApiKeyListResource(Resource): @marshal_with(api_key_fields) def post(self, resource_id): + assert self.resource_id_field is not None, "resource_id_field must be set" resource_id = str(resource_id) _get_resource(resource_id, current_user.current_tenant_id, self.resource_model) if not current_user.is_editor: @@ -86,11 +90,12 @@ class BaseApiKeyListResource(Resource): class BaseApiKeyResource(Resource): method_decorators = [account_initialization_required, login_required, setup_required] - resource_type = None - resource_model = None - resource_id_field = None + resource_type: str | None = None + resource_model: Any = None + resource_id_field: str | None = None def delete(self, resource_id, api_key_id): + assert self.resource_id_field is not None, "resource_id_field must be set" resource_id = str(resource_id) api_key_id = str(api_key_id) _get_resource(resource_id, current_user.current_tenant_id, self.resource_model) diff --git a/api/controllers/console/app/advanced_prompt_template.py b/api/controllers/console/app/advanced_prompt_template.py index c228743fa5..8d0c5b84af 100644 --- a/api/controllers/console/app/advanced_prompt_template.py +++ b/api/controllers/console/app/advanced_prompt_template.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.wraps import account_initialization_required, setup_required diff --git a/api/controllers/console/app/agent.py b/api/controllers/console/app/agent.py index d433415894..920cae0d85 100644 --- a/api/controllers/console/app/agent.py +++ b/api/controllers/console/app/agent.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model diff --git a/api/controllers/console/app/annotation.py b/api/controllers/console/app/annotation.py index fd05cbc19b..24f1020c18 100644 --- a/api/controllers/console/app/annotation.py +++ b/api/controllers/console/app/annotation.py @@ -1,6 +1,6 @@ from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api @@ -110,7 +110,7 @@ class AnnotationListApi(Resource): page = request.args.get("page", default=1, type=int) limit = request.args.get("limit", default=20, type=int) - keyword = request.args.get("keyword", default=None, type=str) + keyword = request.args.get("keyword", default="", type=str) app_id = str(app_id) annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(app_id, page, limit, keyword) diff --git a/api/controllers/console/app/app.py b/api/controllers/console/app/app.py index da72b704c7..9cd56cef0b 100644 --- a/api/controllers/console/app/app.py +++ b/api/controllers/console/app/app.py @@ -1,8 +1,8 @@ import uuid from typing import cast -from flask_login import current_user -from flask_restful import Resource, inputs, marshal, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, inputs, marshal, marshal_with, reqparse # type: ignore from sqlalchemy import select from sqlalchemy.orm import Session from werkzeug.exceptions import BadRequest, Forbidden, abort diff --git a/api/controllers/console/app/app_import.py b/api/controllers/console/app/app_import.py index 244dcd75de..7e2888d71c 100644 --- a/api/controllers/console/app/app_import.py +++ b/api/controllers/console/app/app_import.py @@ -1,7 +1,7 @@ from typing import cast -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden diff --git a/api/controllers/console/app/audio.py b/api/controllers/console/app/audio.py index 695b8890e3..9d26af276d 100644 --- a/api/controllers/console/app/audio.py +++ b/api/controllers/console/app/audio.py @@ -1,7 +1,7 @@ import logging from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import InternalServerError import services diff --git a/api/controllers/console/app/completion.py b/api/controllers/console/app/completion.py index 9896fcaab8..dba41e5c47 100644 --- a/api/controllers/console/app/completion.py +++ b/api/controllers/console/app/completion.py @@ -1,7 +1,7 @@ import logging -import flask_login -from flask_restful import Resource, reqparse +import flask_login # type: ignore +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services diff --git a/api/controllers/console/app/conversation.py b/api/controllers/console/app/conversation.py index a25004be4d..8827f129d9 100644 --- a/api/controllers/console/app/conversation.py +++ b/api/controllers/console/app/conversation.py @@ -1,9 +1,9 @@ from datetime import UTC, datetime -import pytz -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse -from flask_restful.inputs import int_range +import pytz # pip install pytz +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from sqlalchemy import func, or_ from sqlalchemy.orm import joinedload from werkzeug.exceptions import Forbidden, NotFound @@ -77,8 +77,9 @@ class CompletionConversationApi(Resource): query = query.where(Conversation.created_at < end_datetime_utc) + # FIXME, the type ignore in this file if args["annotation_status"] == "annotated": - query = query.options(joinedload(Conversation.message_annotations)).join( + query = query.options(joinedload(Conversation.message_annotations)).join( # type: ignore MessageAnnotation, MessageAnnotation.conversation_id == Conversation.id ) elif args["annotation_status"] == "not_annotated": @@ -222,7 +223,7 @@ class ChatConversationApi(Resource): query = query.where(Conversation.created_at <= end_datetime_utc) if args["annotation_status"] == "annotated": - query = query.options(joinedload(Conversation.message_annotations)).join( + query = query.options(joinedload(Conversation.message_annotations)).join( # type: ignore MessageAnnotation, MessageAnnotation.conversation_id == Conversation.id ) elif args["annotation_status"] == "not_annotated": @@ -234,7 +235,7 @@ class ChatConversationApi(Resource): if args["message_count_gte"] and args["message_count_gte"] >= 1: query = ( - query.options(joinedload(Conversation.messages)) + query.options(joinedload(Conversation.messages)) # type: ignore .join(Message, Message.conversation_id == Conversation.id) .group_by(Conversation.id) .having(func.count(Message.id) >= args["message_count_gte"]) diff --git a/api/controllers/console/app/conversation_variables.py b/api/controllers/console/app/conversation_variables.py index d49f433ba1..c0a20b7160 100644 --- a/api/controllers/console/app/conversation_variables.py +++ b/api/controllers/console/app/conversation_variables.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, marshal_with, reqparse +from flask_restful import Resource, marshal_with, reqparse # type: ignore from sqlalchemy import select from sqlalchemy.orm import Session diff --git a/api/controllers/console/app/generator.py b/api/controllers/console/app/generator.py index 9c3cbe4e3e..8518d34a8e 100644 --- a/api/controllers/console/app/generator.py +++ b/api/controllers/console/app/generator.py @@ -1,7 +1,7 @@ import os -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.app.error import ( diff --git a/api/controllers/console/app/message.py b/api/controllers/console/app/message.py index b7a4c31a15..b5828b6b4b 100644 --- a/api/controllers/console/app/message.py +++ b/api/controllers/console/app/message.py @@ -1,8 +1,8 @@ import logging -from flask_login import current_user -from flask_restful import Resource, fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_login import current_user # type: ignore +from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import Forbidden, InternalServerError, NotFound from controllers.console import api diff --git a/api/controllers/console/app/model_config.py b/api/controllers/console/app/model_config.py index a46bc6a8a9..8ecc8a9db5 100644 --- a/api/controllers/console/app/model_config.py +++ b/api/controllers/console/app/model_config.py @@ -1,8 +1,9 @@ import json +from typing import cast from flask import request -from flask_login import current_user -from flask_restful import Resource +from flask_login import current_user # type: ignore +from flask_restful import Resource # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model @@ -26,7 +27,9 @@ class ModelConfigResource(Resource): """Modify app model config""" # validate config model_configuration = AppModelConfigService.validate_configuration( - tenant_id=current_user.current_tenant_id, config=request.json, app_mode=AppMode.value_of(app_model.mode) + tenant_id=current_user.current_tenant_id, + config=cast(dict, request.json), + app_mode=AppMode.value_of(app_model.mode), ) new_app_model_config = AppModelConfig( @@ -38,9 +41,11 @@ class ModelConfigResource(Resource): if app_model.mode == AppMode.AGENT_CHAT.value or app_model.is_agent: # get original app model config - original_app_model_config: AppModelConfig = ( + original_app_model_config = ( db.session.query(AppModelConfig).filter(AppModelConfig.id == app_model.app_model_config_id).first() ) + if original_app_model_config is None: + raise ValueError("Original app model config not found") agent_mode = original_app_model_config.agent_mode_dict # decrypt agent tool parameters if it's secret-input parameter_map = {} diff --git a/api/controllers/console/app/ops_trace.py b/api/controllers/console/app/ops_trace.py index 3f10215e70..dd25af8ebf 100644 --- a/api/controllers/console/app/ops_trace.py +++ b/api/controllers/console/app/ops_trace.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import BadRequest from controllers.console import api diff --git a/api/controllers/console/app/site.py b/api/controllers/console/app/site.py index 407f689819..db29b95c41 100644 --- a/api/controllers/console/app/site.py +++ b/api/controllers/console/app/site.py @@ -1,7 +1,7 @@ from datetime import UTC, datetime -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Forbidden, NotFound from constants.languages import supported_language @@ -50,7 +50,7 @@ class AppSite(Resource): if not current_user.is_editor: raise Forbidden() - site = db.session.query(Site).filter(Site.app_id == app_model.id).one_or_404() + site = Site.query.filter(Site.app_id == app_model.id).one_or_404() for attr_name in [ "title", diff --git a/api/controllers/console/app/statistic.py b/api/controllers/console/app/statistic.py index db5e282409..3b21108cea 100644 --- a/api/controllers/console/app/statistic.py +++ b/api/controllers/console/app/statistic.py @@ -3,8 +3,8 @@ from decimal import Decimal import pytz from flask import jsonify -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index f228c3ec4a..26a3a022d4 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -2,7 +2,7 @@ import json import logging from flask import abort, request -from flask_restful import Resource, marshal_with, reqparse +from flask_restful import Resource, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services diff --git a/api/controllers/console/app/workflow_app_log.py b/api/controllers/console/app/workflow_app_log.py index 2940556f84..882c53e4fb 100644 --- a/api/controllers/console/app/workflow_app_log.py +++ b/api/controllers/console/app/workflow_app_log.py @@ -1,5 +1,5 @@ -from flask_restful import Resource, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import Resource, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model diff --git a/api/controllers/console/app/workflow_run.py b/api/controllers/console/app/workflow_run.py index 08ab61bbb9..25a99c1e15 100644 --- a/api/controllers/console/app/workflow_run.py +++ b/api/controllers/console/app/workflow_run.py @@ -1,5 +1,5 @@ -from flask_restful import Resource, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import Resource, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model diff --git a/api/controllers/console/app/workflow_statistic.py b/api/controllers/console/app/workflow_statistic.py index 6c7c73707b..097bf7d188 100644 --- a/api/controllers/console/app/workflow_statistic.py +++ b/api/controllers/console/app/workflow_statistic.py @@ -3,8 +3,8 @@ from decimal import Decimal import pytz from flask import jsonify -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.app.wraps import get_app_model diff --git a/api/controllers/console/app/wraps.py b/api/controllers/console/app/wraps.py index 63edb83079..9ad8c15847 100644 --- a/api/controllers/console/app/wraps.py +++ b/api/controllers/console/app/wraps.py @@ -8,7 +8,7 @@ from libs.login import current_user from models import App, AppMode -def get_app_model(view: Optional[Callable] = None, *, mode: Union[AppMode, list[AppMode]] = None): +def get_app_model(view: Optional[Callable] = None, *, mode: Union[AppMode, list[AppMode], None] = None): def decorator(view_func): @wraps(view_func) def decorated_view(*args, **kwargs): diff --git a/api/controllers/console/auth/activate.py b/api/controllers/console/auth/activate.py index d2aa7c903b..c56f551d49 100644 --- a/api/controllers/console/auth/activate.py +++ b/api/controllers/console/auth/activate.py @@ -1,14 +1,14 @@ import datetime from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from constants.languages import supported_language from controllers.console import api from controllers.console.error import AlreadyActivateError from extensions.ext_database import db from libs.helper import StrLen, email, extract_remote_ip, timezone -from models.account import AccountStatus, Tenant +from models.account import AccountStatus from services.account_service import AccountService, RegisterService @@ -27,7 +27,7 @@ class ActivateCheckApi(Resource): invitation = RegisterService.get_invitation_if_token_valid(workspaceId, reg_email, token) if invitation: data = invitation.get("data", {}) - tenant: Tenant = invitation.get("tenant", None) + tenant = invitation.get("tenant", None) workspace_name = tenant.name if tenant else None workspace_id = tenant.id if tenant else None invitee_email = data.get("email") if data else None diff --git a/api/controllers/console/auth/data_source_bearer_auth.py b/api/controllers/console/auth/data_source_bearer_auth.py index 465c44e9b6..ea00c2b8c2 100644 --- a/api/controllers/console/auth/data_source_bearer_auth.py +++ b/api/controllers/console/auth/data_source_bearer_auth.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api diff --git a/api/controllers/console/auth/data_source_oauth.py b/api/controllers/console/auth/data_source_oauth.py index faca67bb17..e911c9a5e5 100644 --- a/api/controllers/console/auth/data_source_oauth.py +++ b/api/controllers/console/auth/data_source_oauth.py @@ -2,8 +2,8 @@ import logging import requests from flask import current_app, redirect, request -from flask_login import current_user -from flask_restful import Resource +from flask_login import current_user # type: ignore +from flask_restful import Resource # type: ignore from werkzeug.exceptions import Forbidden from configs import dify_config @@ -17,8 +17,8 @@ from ..wraps import account_initialization_required, setup_required def get_oauth_providers(): with current_app.app_context(): notion_oauth = NotionOAuth( - client_id=dify_config.NOTION_CLIENT_ID, - client_secret=dify_config.NOTION_CLIENT_SECRET, + client_id=dify_config.NOTION_CLIENT_ID or "", + client_secret=dify_config.NOTION_CLIENT_SECRET or "", redirect_uri=dify_config.CONSOLE_API_URL + "/console/api/oauth/data-source/callback/notion", ) diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py index fb32bb2b60..140b9e145f 100644 --- a/api/controllers/console/auth/forgot_password.py +++ b/api/controllers/console/auth/forgot_password.py @@ -2,7 +2,7 @@ import base64 import secrets from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from constants.languages import languages from controllers.console import api @@ -122,8 +122,8 @@ class ForgotPasswordResetApi(Resource): else: try: account = AccountService.create_account_and_tenant( - email=reset_data.get("email"), - name=reset_data.get("email"), + email=reset_data.get("email", ""), + name=reset_data.get("email", ""), password=password_confirm, interface_language=languages[0], ) diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index f4463ce9cb..78a80fc8d7 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -1,8 +1,8 @@ from typing import cast -import flask_login +import flask_login # type: ignore from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore import services from constants.languages import languages diff --git a/api/controllers/console/auth/oauth.py b/api/controllers/console/auth/oauth.py index b9188aa079..333b241427 100644 --- a/api/controllers/console/auth/oauth.py +++ b/api/controllers/console/auth/oauth.py @@ -4,7 +4,7 @@ from typing import Optional import requests from flask import current_app, redirect, request -from flask_restful import Resource +from flask_restful import Resource # type: ignore from werkzeug.exceptions import Unauthorized from configs import dify_config @@ -77,7 +77,8 @@ class OAuthCallback(Resource): token = oauth_provider.get_access_token(code) user_info = oauth_provider.get_user_info(token) except requests.exceptions.RequestException as e: - logging.exception(f"An error occurred during the OAuth process with {provider}: {e.response.text}") + error_text = e.response.text if e.response else str(e) + logging.exception(f"An error occurred during the OAuth process with {provider}: {error_text}") return {"error": "OAuth process failed"}, 400 if invite_token and RegisterService.is_valid_invite_token(invite_token): @@ -129,7 +130,7 @@ class OAuthCallback(Resource): def _get_account_by_openid_or_email(provider: str, user_info: OAuthUserInfo) -> Optional[Account]: - account = Account.get_by_openid(provider, user_info.id) + account: Optional[Account] = Account.get_by_openid(provider, user_info.id) if not account: account = Account.query.filter_by(email=user_info.email).first() diff --git a/api/controllers/console/billing/billing.py b/api/controllers/console/billing/billing.py index 4b0c82ae6c..fd7b7bd8cb 100644 --- a/api/controllers/console/billing/billing.py +++ b/api/controllers/console/billing/billing.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.wraps import account_initialization_required, only_edition_cloud, setup_required diff --git a/api/controllers/console/datasets/data_source.py b/api/controllers/console/datasets/data_source.py index 278295ca39..d7c431b950 100644 --- a/api/controllers/console/datasets/data_source.py +++ b/api/controllers/console/datasets/data_source.py @@ -2,8 +2,8 @@ import datetime import json from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore from werkzeug.exceptions import NotFound from controllers.console import api diff --git a/api/controllers/console/datasets/datasets.py b/api/controllers/console/datasets/datasets.py index 95d4013e3a..f3c3736b25 100644 --- a/api/controllers/console/datasets/datasets.py +++ b/api/controllers/console/datasets/datasets.py @@ -1,7 +1,7 @@ -import flask_restful +import flask_restful # type: ignore from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal, marshal_with, reqparse +from flask_login import current_user # type: ignore # type: ignore +from flask_restful import Resource, marshal, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Forbidden, NotFound import services diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index ad4768f519..ca41e504be 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -1,12 +1,13 @@ import logging from argparse import ArgumentTypeError from datetime import UTC, datetime +from typing import cast from flask import request -from flask_login import current_user -from flask_restful import Resource, fields, marshal, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, fields, marshal, marshal_with, reqparse # type: ignore from sqlalchemy import asc, desc -from transformers.hf_argparser import string_to_bool +from transformers.hf_argparser import string_to_bool # type: ignore from werkzeug.exceptions import Forbidden, NotFound import services @@ -733,8 +734,7 @@ class DocumentMetadataApi(DocumentResource): if not isinstance(doc_metadata, dict): raise ValueError("doc_metadata must be a dictionary.") - - metadata_schema = DocumentService.DOCUMENT_METADATA_SCHEMA[doc_type] + metadata_schema: dict = cast(dict, DocumentService.DOCUMENT_METADATA_SCHEMA[doc_type]) document.doc_metadata = {} if doc_type == "others": diff --git a/api/controllers/console/datasets/datasets_segments.py b/api/controllers/console/datasets/datasets_segments.py index 6f7ef86d2c..2d5933ca23 100644 --- a/api/controllers/console/datasets/datasets_segments.py +++ b/api/controllers/console/datasets/datasets_segments.py @@ -3,8 +3,8 @@ from datetime import UTC, datetime import pandas as pd from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal, reqparse # type: ignore from werkzeug.exceptions import Forbidden, NotFound import services diff --git a/api/controllers/console/datasets/external.py b/api/controllers/console/datasets/external.py index bc6e3687c1..48f360dcd1 100644 --- a/api/controllers/console/datasets/external.py +++ b/api/controllers/console/datasets/external.py @@ -1,6 +1,6 @@ from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal, reqparse # type: ignore from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services diff --git a/api/controllers/console/datasets/hit_testing.py b/api/controllers/console/datasets/hit_testing.py index 495f511275..18b746f547 100644 --- a/api/controllers/console/datasets/hit_testing.py +++ b/api/controllers/console/datasets/hit_testing.py @@ -1,4 +1,4 @@ -from flask_restful import Resource +from flask_restful import Resource # type: ignore from controllers.console import api from controllers.console.datasets.hit_testing_base import DatasetsHitTestingBase diff --git a/api/controllers/console/datasets/hit_testing_base.py b/api/controllers/console/datasets/hit_testing_base.py index 3b4c076863..bd944602c1 100644 --- a/api/controllers/console/datasets/hit_testing_base.py +++ b/api/controllers/console/datasets/hit_testing_base.py @@ -1,7 +1,7 @@ import logging -from flask_login import current_user -from flask_restful import marshal, reqparse +from flask_login import current_user # type: ignore +from flask_restful import marshal, reqparse # type: ignore from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services.dataset_service diff --git a/api/controllers/console/datasets/website.py b/api/controllers/console/datasets/website.py index 9127c8af45..da995537e7 100644 --- a/api/controllers/console/datasets/website.py +++ b/api/controllers/console/datasets/website.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from controllers.console import api from controllers.console.datasets.error import WebsiteCrawlError diff --git a/api/controllers/console/explore/audio.py b/api/controllers/console/explore/audio.py index 9690677f61..c7f9fec326 100644 --- a/api/controllers/console/explore/audio.py +++ b/api/controllers/console/explore/audio.py @@ -4,7 +4,6 @@ from flask import request from werkzeug.exceptions import InternalServerError import services -from controllers.console import api from controllers.console.app.error import ( AppUnavailableError, AudioTooLargeError, @@ -67,7 +66,7 @@ class ChatAudioApi(InstalledAppResource): class ChatTextApi(InstalledAppResource): def post(self, installed_app): - from flask_restful import reqparse + from flask_restful import reqparse # type: ignore app_model = installed_app.app try: @@ -118,9 +117,3 @@ class ChatTextApi(InstalledAppResource): except Exception as e: logging.exception("internal server error.") raise InternalServerError() - - -api.add_resource(ChatAudioApi, "/installed-apps//audio-to-text", endpoint="installed_app_audio") -api.add_resource(ChatTextApi, "/installed-apps//text-to-audio", endpoint="installed_app_text") -# api.add_resource(ChatTextApiWithMessageId, '/installed-apps//text-to-audio/message-id', -# endpoint='installed_app_text_with_message_id') diff --git a/api/controllers/console/explore/completion.py b/api/controllers/console/explore/completion.py index 85c43f8101..3331ded70f 100644 --- a/api/controllers/console/explore/completion.py +++ b/api/controllers/console/explore/completion.py @@ -1,12 +1,11 @@ import logging from datetime import UTC, datetime -from flask_login import current_user -from flask_restful import reqparse +from flask_login import current_user # type: ignore +from flask_restful import reqparse # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services -from controllers.console import api from controllers.console.app.error import ( AppUnavailableError, CompletionRequestError, @@ -147,21 +146,3 @@ class ChatStopApi(InstalledAppResource): AppQueueManager.set_stop_flag(task_id, InvokeFrom.EXPLORE, current_user.id) return {"result": "success"}, 200 - - -api.add_resource( - CompletionApi, "/installed-apps//completion-messages", endpoint="installed_app_completion" -) -api.add_resource( - CompletionStopApi, - "/installed-apps//completion-messages//stop", - endpoint="installed_app_stop_completion", -) -api.add_resource( - ChatApi, "/installed-apps//chat-messages", endpoint="installed_app_chat_completion" -) -api.add_resource( - ChatStopApi, - "/installed-apps//chat-messages//stop", - endpoint="installed_app_stop_chat_completion", -) diff --git a/api/controllers/console/explore/conversation.py b/api/controllers/console/explore/conversation.py index 5e7a3da017..91916cbc1e 100644 --- a/api/controllers/console/explore/conversation.py +++ b/api/controllers/console/explore/conversation.py @@ -1,10 +1,9 @@ -from flask_login import current_user -from flask_restful import marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_login import current_user # type: ignore +from flask_restful import marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound -from controllers.console import api from controllers.console.explore.error import NotChatAppError from controllers.console.explore.wraps import InstalledAppResource from core.app.entities.app_invoke_entities import InvokeFrom @@ -118,28 +117,3 @@ class ConversationUnPinApi(InstalledAppResource): WebConversationService.unpin(app_model, conversation_id, current_user) return {"result": "success"} - - -api.add_resource( - ConversationRenameApi, - "/installed-apps//conversations//name", - endpoint="installed_app_conversation_rename", -) -api.add_resource( - ConversationListApi, "/installed-apps//conversations", endpoint="installed_app_conversations" -) -api.add_resource( - ConversationApi, - "/installed-apps//conversations/", - endpoint="installed_app_conversation", -) -api.add_resource( - ConversationPinApi, - "/installed-apps//conversations//pin", - endpoint="installed_app_conversation_pin", -) -api.add_resource( - ConversationUnPinApi, - "/installed-apps//conversations//unpin", - endpoint="installed_app_conversation_unpin", -) diff --git a/api/controllers/console/explore/installed_app.py b/api/controllers/console/explore/installed_app.py index 3de179164d..86550b2bdf 100644 --- a/api/controllers/console/explore/installed_app.py +++ b/api/controllers/console/explore/installed_app.py @@ -1,8 +1,9 @@ from datetime import UTC, datetime +from typing import Any from flask import request -from flask_login import current_user -from flask_restful import Resource, inputs, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, inputs, marshal_with, reqparse # type: ignore from sqlalchemy import and_ from werkzeug.exceptions import BadRequest, Forbidden, NotFound @@ -34,7 +35,7 @@ class InstalledAppsListApi(Resource): installed_apps = db.session.query(InstalledApp).filter(InstalledApp.tenant_id == current_tenant_id).all() current_user.role = TenantService.get_user_role(current_user, current_user.current_tenant) - installed_apps = [ + installed_app_list: list[dict[str, Any]] = [ { "id": installed_app.id, "app": installed_app.app, @@ -47,7 +48,7 @@ class InstalledAppsListApi(Resource): for installed_app in installed_apps if installed_app.app is not None ] - installed_apps.sort( + installed_app_list.sort( key=lambda app: ( -app["is_pinned"], app["last_used_at"] is None, @@ -55,7 +56,7 @@ class InstalledAppsListApi(Resource): ) ) - return {"installed_apps": installed_apps} + return {"installed_apps": installed_app_list} @login_required @account_initialization_required diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index 4e11d8005f..c3488de299 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -1,12 +1,11 @@ import logging -from flask_login import current_user -from flask_restful import marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_login import current_user # type: ignore +from flask_restful import marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services -from controllers.console import api from controllers.console.app.error import ( AppMoreLikeThisDisabledError, CompletionRequestError, @@ -153,21 +152,3 @@ class MessageSuggestedQuestionApi(InstalledAppResource): raise InternalServerError() return {"data": questions} - - -api.add_resource(MessageListApi, "/installed-apps//messages", endpoint="installed_app_messages") -api.add_resource( - MessageFeedbackApi, - "/installed-apps//messages//feedbacks", - endpoint="installed_app_message_feedback", -) -api.add_resource( - MessageMoreLikeThisApi, - "/installed-apps//messages//more-like-this", - endpoint="installed_app_more_like_this", -) -api.add_resource( - MessageSuggestedQuestionApi, - "/installed-apps//messages//suggested-questions", - endpoint="installed_app_suggested_question", -) diff --git a/api/controllers/console/explore/parameter.py b/api/controllers/console/explore/parameter.py index fee52248a6..5bc74d16e7 100644 --- a/api/controllers/console/explore/parameter.py +++ b/api/controllers/console/explore/parameter.py @@ -1,4 +1,4 @@ -from flask_restful import marshal_with +from flask_restful import marshal_with # type: ignore from controllers.common import fields from controllers.common import helpers as controller_helpers diff --git a/api/controllers/console/explore/recommended_app.py b/api/controllers/console/explore/recommended_app.py index ce85f495aa..be6b1f5d21 100644 --- a/api/controllers/console/explore/recommended_app.py +++ b/api/controllers/console/explore/recommended_app.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import Resource, fields, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore from constants.languages import languages from controllers.console import api diff --git a/api/controllers/console/explore/saved_message.py b/api/controllers/console/explore/saved_message.py index 0fc9637479..9f0c496645 100644 --- a/api/controllers/console/explore/saved_message.py +++ b/api/controllers/console/explore/saved_message.py @@ -1,6 +1,6 @@ -from flask_login import current_user -from flask_restful import fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_login import current_user # type: ignore +from flask_restful import fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import NotFound from controllers.console import api diff --git a/api/controllers/console/explore/workflow.py b/api/controllers/console/explore/workflow.py index 45f99b1db9..76d30299cd 100644 --- a/api/controllers/console/explore/workflow.py +++ b/api/controllers/console/explore/workflow.py @@ -1,9 +1,8 @@ import logging -from flask_restful import reqparse +from flask_restful import reqparse # type: ignore from werkzeug.exceptions import InternalServerError -from controllers.console import api from controllers.console.app.error import ( CompletionRequestError, ProviderModelCurrentlyNotSupportError, @@ -73,9 +72,3 @@ class InstalledAppWorkflowTaskStopApi(InstalledAppResource): AppQueueManager.set_stop_flag(task_id, InvokeFrom.EXPLORE, current_user.id) return {"result": "success"} - - -api.add_resource(InstalledAppWorkflowRunApi, "/installed-apps//workflows/run") -api.add_resource( - InstalledAppWorkflowTaskStopApi, "/installed-apps//workflows/tasks//stop" -) diff --git a/api/controllers/console/explore/wraps.py b/api/controllers/console/explore/wraps.py index 49ea81a8a0..b7ba81fba2 100644 --- a/api/controllers/console/explore/wraps.py +++ b/api/controllers/console/explore/wraps.py @@ -1,7 +1,7 @@ from functools import wraps -from flask_login import current_user -from flask_restful import Resource +from flask_login import current_user # type: ignore +from flask_restful import Resource # type: ignore from werkzeug.exceptions import NotFound from controllers.console.wraps import account_initialization_required diff --git a/api/controllers/console/extension.py b/api/controllers/console/extension.py index 4ac0aa497e..ed6cedb220 100644 --- a/api/controllers/console/extension.py +++ b/api/controllers/console/extension.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore from constants import HIDDEN_VALUE from controllers.console import api diff --git a/api/controllers/console/feature.py b/api/controllers/console/feature.py index 70ab4ff865..da1171412f 100644 --- a/api/controllers/console/feature.py +++ b/api/controllers/console/feature.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import Resource +from flask_login import current_user # type: ignore +from flask_restful import Resource # type: ignore from libs.login import login_required from services.feature_service import FeatureService diff --git a/api/controllers/console/files.py b/api/controllers/console/files.py index ca32d29efa..8cf754bbd6 100644 --- a/api/controllers/console/files.py +++ b/api/controllers/console/files.py @@ -1,6 +1,8 @@ +from typing import Literal + from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal_with +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with # type: ignore from werkzeug.exceptions import Forbidden import services @@ -48,7 +50,8 @@ class FileApi(Resource): @cloud_edition_billing_resource_check("documents") def post(self): file = request.files["file"] - source = request.form.get("source") + source_str = request.form.get("source") + source: Literal["datasets"] | None = "datasets" if source_str == "datasets" else None if "file" not in request.files: raise NoFileUploadedError() diff --git a/api/controllers/console/init_validate.py b/api/controllers/console/init_validate.py index ae759bb752..d9ae5cf29f 100644 --- a/api/controllers/console/init_validate.py +++ b/api/controllers/console/init_validate.py @@ -1,7 +1,7 @@ import os from flask import session -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from configs import dify_config from libs.helper import StrLen diff --git a/api/controllers/console/ping.py b/api/controllers/console/ping.py index cd28cc946e..2a116112a3 100644 --- a/api/controllers/console/ping.py +++ b/api/controllers/console/ping.py @@ -1,4 +1,4 @@ -from flask_restful import Resource +from flask_restful import Resource # type: ignore from controllers.console import api diff --git a/api/controllers/console/remote_files.py b/api/controllers/console/remote_files.py index b8cf019e4f..30afc930a8 100644 --- a/api/controllers/console/remote_files.py +++ b/api/controllers/console/remote_files.py @@ -2,8 +2,8 @@ import urllib.parse from typing import cast import httpx -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore import services from controllers.common import helpers diff --git a/api/controllers/console/setup.py b/api/controllers/console/setup.py index e0b728d977..aba6f0aad9 100644 --- a/api/controllers/console/setup.py +++ b/api/controllers/console/setup.py @@ -1,5 +1,5 @@ from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from configs import dify_config from libs.helper import StrLen, email, extract_remote_ip diff --git a/api/controllers/console/tag/tags.py b/api/controllers/console/tag/tags.py index ccd3293a62..da83f64019 100644 --- a/api/controllers/console/tag/tags.py +++ b/api/controllers/console/tag/tags.py @@ -1,6 +1,6 @@ from flask import request -from flask_login import current_user -from flask_restful import Resource, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api @@ -23,7 +23,7 @@ class TagListApi(Resource): @account_initialization_required @marshal_with(tag_fields) def get(self): - tag_type = request.args.get("type", type=str) + tag_type = request.args.get("type", type=str, default="") keyword = request.args.get("keyword", default=None, type=str) tags = TagService.get_tags(tag_type, current_user.current_tenant_id, keyword) diff --git a/api/controllers/console/version.py b/api/controllers/console/version.py index 7dea8e554e..7773c99944 100644 --- a/api/controllers/console/version.py +++ b/api/controllers/console/version.py @@ -2,7 +2,7 @@ import json import logging import requests -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from packaging import version from configs import dify_config diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py index f704783cff..96ed4b7a57 100644 --- a/api/controllers/console/workspace/account.py +++ b/api/controllers/console/workspace/account.py @@ -2,8 +2,8 @@ import datetime import pytz from flask import request -from flask_login import current_user -from flask_restful import Resource, fields, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore from configs import dify_config from constants.languages import supported_language diff --git a/api/controllers/console/workspace/load_balancing_config.py b/api/controllers/console/workspace/load_balancing_config.py index d2b2092b75..7009343d99 100644 --- a/api/controllers/console/workspace/load_balancing_config.py +++ b/api/controllers/console/workspace/load_balancing_config.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api @@ -37,7 +37,7 @@ class LoadBalancingCredentialsValidateApi(Resource): model_load_balancing_service = ModelLoadBalancingService() result = True - error = None + error = "" try: model_load_balancing_service.validate_load_balancing_credentials( @@ -86,7 +86,7 @@ class LoadBalancingConfigCredentialsValidateApi(Resource): model_load_balancing_service = ModelLoadBalancingService() result = True - error = None + error = "" try: model_load_balancing_service.validate_load_balancing_credentials( diff --git a/api/controllers/console/workspace/members.py b/api/controllers/console/workspace/members.py index 38ed2316a5..1afb41ea87 100644 --- a/api/controllers/console/workspace/members.py +++ b/api/controllers/console/workspace/members.py @@ -1,7 +1,7 @@ from urllib import parse -from flask_login import current_user -from flask_restful import Resource, abort, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, abort, marshal_with, reqparse # type: ignore import services from configs import dify_config @@ -89,19 +89,19 @@ class MemberCancelInviteApi(Resource): @account_initialization_required def delete(self, member_id): member = db.session.query(Account).filter(Account.id == str(member_id)).first() - if not member: + if member is None: abort(404) - - try: - TenantService.remove_member_from_tenant(current_user.current_tenant, member, current_user) - except services.errors.account.CannotOperateSelfError as e: - return {"code": "cannot-operate-self", "message": str(e)}, 400 - except services.errors.account.NoPermissionError as e: - return {"code": "forbidden", "message": str(e)}, 403 - except services.errors.account.MemberNotInTenantError as e: - return {"code": "member-not-found", "message": str(e)}, 404 - except Exception as e: - raise ValueError(str(e)) + else: + try: + TenantService.remove_member_from_tenant(current_user.current_tenant, member, current_user) + except services.errors.account.CannotOperateSelfError as e: + return {"code": "cannot-operate-self", "message": str(e)}, 400 + except services.errors.account.NoPermissionError as e: + return {"code": "forbidden", "message": str(e)}, 403 + except services.errors.account.MemberNotInTenantError as e: + return {"code": "member-not-found", "message": str(e)}, 404 + except Exception as e: + raise ValueError(str(e)) return {"result": "success"}, 204 @@ -122,10 +122,11 @@ class MemberUpdateRoleApi(Resource): return {"code": "invalid-role", "message": "Invalid role"}, 400 member = db.session.get(Account, str(member_id)) - if not member: + if member: abort(404) try: + assert member is not None, "Member not found" TenantService.update_member_role(current_user.current_tenant, member, new_role, current_user) except Exception as e: raise ValueError(str(e)) diff --git a/api/controllers/console/workspace/model_providers.py b/api/controllers/console/workspace/model_providers.py index 0e54126063..2d11295b0f 100644 --- a/api/controllers/console/workspace/model_providers.py +++ b/api/controllers/console/workspace/model_providers.py @@ -1,8 +1,8 @@ import io from flask import send_file -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api @@ -66,7 +66,7 @@ class ModelProviderValidateApi(Resource): model_provider_service = ModelProviderService() result = True - error = None + error = "" try: model_provider_service.provider_credentials_validate( @@ -132,7 +132,8 @@ class ModelProviderIconApi(Resource): icon_type=icon_type, lang=lang, ) - + if icon is None: + raise ValueError(f"icon not found for provider {provider}, icon_type {icon_type}, lang {lang}") return send_file(io.BytesIO(icon), mimetype=mimetype) diff --git a/api/controllers/console/workspace/models.py b/api/controllers/console/workspace/models.py index f804285f00..618262e502 100644 --- a/api/controllers/console/workspace/models.py +++ b/api/controllers/console/workspace/models.py @@ -1,7 +1,7 @@ import logging -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import Forbidden from controllers.console import api @@ -308,7 +308,7 @@ class ModelProviderModelValidateApi(Resource): model_provider_service = ModelProviderService() result = True - error = None + error = "" try: model_provider_service.model_credentials_validate( diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 9e62a54699..964f386229 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -1,8 +1,8 @@ import io from flask import send_file -from flask_login import current_user -from flask_restful import Resource, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, reqparse # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden diff --git a/api/controllers/console/workspace/workspace.py b/api/controllers/console/workspace/workspace.py index 76d76f6b58..0f99bf62e3 100644 --- a/api/controllers/console/workspace/workspace.py +++ b/api/controllers/console/workspace/workspace.py @@ -1,8 +1,8 @@ import logging from flask import request -from flask_login import current_user -from flask_restful import Resource, fields, inputs, marshal, marshal_with, reqparse +from flask_login import current_user # type: ignore +from flask_restful import Resource, fields, inputs, marshal, marshal_with, reqparse # type: ignore from werkzeug.exceptions import Unauthorized import services @@ -82,11 +82,7 @@ class WorkspaceListApi(Resource): parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args") args = parser.parse_args() - tenants = ( - db.session.query(Tenant) - .order_by(Tenant.created_at.desc()) - .paginate(page=args["page"], per_page=args["limit"]) - ) + tenants = Tenant.query.order_by(Tenant.created_at.desc()).paginate(page=args["page"], per_page=args["limit"]) has_more = False if len(tenants.items) == args["limit"]: @@ -151,6 +147,8 @@ class SwitchWorkspaceApi(Resource): raise AccountNotLinkTenantError("Account not link tenant") new_tenant = db.session.query(Tenant).get(args["tenant_id"]) # Get new tenant + if new_tenant is None: + raise ValueError("Tenant not found") return {"result": "success", "new_tenant": marshal(WorkspaceService.get_tenant_info(new_tenant), tenant_fields)} @@ -166,7 +164,7 @@ class CustomConfigWorkspaceApi(Resource): parser.add_argument("replace_webapp_logo", type=str, location="json") args = parser.parse_args() - tenant = db.session.query(Tenant).filter(Tenant.id == current_user.current_tenant_id).one_or_404() + tenant = Tenant.query.filter(Tenant.id == current_user.current_tenant_id).one_or_404() custom_config_dict = { "remove_webapp_brand": args["remove_webapp_brand"], diff --git a/api/controllers/console/wraps.py b/api/controllers/console/wraps.py index d0df296c24..111db7ccf2 100644 --- a/api/controllers/console/wraps.py +++ b/api/controllers/console/wraps.py @@ -3,7 +3,7 @@ import os from functools import wraps from flask import abort, request -from flask_login import current_user +from flask_login import current_user # type: ignore from configs import dify_config from controllers.console.workspace.error import AccountNotInitializedError @@ -121,8 +121,8 @@ def cloud_utm_record(view): utm_info = request.cookies.get("utm_info") if utm_info: - utm_info = json.loads(utm_info) - OperationService.record_utm(current_user.current_tenant_id, utm_info) + utm_info_dict: dict = json.loads(utm_info) + OperationService.record_utm(current_user.current_tenant_id, utm_info_dict) except Exception as e: pass return view(*args, **kwargs) diff --git a/api/controllers/files/image_preview.py b/api/controllers/files/image_preview.py index 6b3ac93cdf..2357288a50 100644 --- a/api/controllers/files/image_preview.py +++ b/api/controllers/files/image_preview.py @@ -1,5 +1,5 @@ from flask import Response, request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import NotFound import services diff --git a/api/controllers/files/tool_files.py b/api/controllers/files/tool_files.py index a298701a2f..cfcce81247 100644 --- a/api/controllers/files/tool_files.py +++ b/api/controllers/files/tool_files.py @@ -1,5 +1,5 @@ from flask import Response -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import Forbidden, NotFound from controllers.files import api diff --git a/api/controllers/inner_api/workspace/workspace.py b/api/controllers/inner_api/workspace/workspace.py index 99d32af593..d7346b13b1 100644 --- a/api/controllers/inner_api/workspace/workspace.py +++ b/api/controllers/inner_api/workspace/workspace.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from controllers.console.wraps import setup_required from controllers.inner_api import api diff --git a/api/controllers/inner_api/wraps.py b/api/controllers/inner_api/wraps.py index 51ffe683ff..d4587235f6 100644 --- a/api/controllers/inner_api/wraps.py +++ b/api/controllers/inner_api/wraps.py @@ -45,14 +45,14 @@ def inner_api_user_auth(view): if " " in user_id: user_id = user_id.split(" ")[1] - inner_api_key = request.headers.get("X-Inner-Api-Key") + inner_api_key = request.headers.get("X-Inner-Api-Key", "") data_to_sign = f"DIFY {user_id}" signature = hmac_new(inner_api_key.encode("utf-8"), data_to_sign.encode("utf-8"), sha1) - signature = b64encode(signature.digest()).decode("utf-8") + signature_base64 = b64encode(signature.digest()).decode("utf-8") - if signature != token: + if signature_base64 != token: return view(*args, **kwargs) kwargs["user"] = db.session.query(EndUser).filter(EndUser.id == user_id).first() diff --git a/api/controllers/service_api/app/app.py b/api/controllers/service_api/app/app.py index ecff7d07e9..8388e2045d 100644 --- a/api/controllers/service_api/app/app.py +++ b/api/controllers/service_api/app/app.py @@ -1,4 +1,4 @@ -from flask_restful import Resource, marshal_with +from flask_restful import Resource, marshal_with # type: ignore from controllers.common import fields from controllers.common import helpers as controller_helpers diff --git a/api/controllers/service_api/app/audio.py b/api/controllers/service_api/app/audio.py index 5db4163647..e6bcc0bfd2 100644 --- a/api/controllers/service_api/app/audio.py +++ b/api/controllers/service_api/app/audio.py @@ -1,7 +1,7 @@ import logging from flask import request -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import InternalServerError import services @@ -83,7 +83,7 @@ class TextApi(Resource): and app_model.workflow and app_model.workflow.features_dict ): - text_to_speech = app_model.workflow.features_dict.get("text_to_speech") + text_to_speech = app_model.workflow.features_dict.get("text_to_speech", {}) voice = args.get("voice") or text_to_speech.get("voice") else: try: diff --git a/api/controllers/service_api/app/completion.py b/api/controllers/service_api/app/completion.py index 8d8e356c4c..1be54b386b 100644 --- a/api/controllers/service_api/app/completion.py +++ b/api/controllers/service_api/app/completion.py @@ -1,6 +1,6 @@ import logging -from flask_restful import Resource, reqparse +from flask_restful import Resource, reqparse # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index 32940cbc29..334f2c5620 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -1,5 +1,5 @@ -from flask_restful import Resource, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import Resource, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound diff --git a/api/controllers/service_api/app/file.py b/api/controllers/service_api/app/file.py index b0fd8e65ef..27b21b9f50 100644 --- a/api/controllers/service_api/app/file.py +++ b/api/controllers/service_api/app/file.py @@ -1,5 +1,5 @@ from flask import request -from flask_restful import Resource, marshal_with +from flask_restful import Resource, marshal_with # type: ignore import services from controllers.common.errors import FilenameNotExistsError diff --git a/api/controllers/service_api/app/message.py b/api/controllers/service_api/app/message.py index 599401bc6f..522c7509b9 100644 --- a/api/controllers/service_api/app/message.py +++ b/api/controllers/service_api/app/message.py @@ -1,7 +1,7 @@ import logging -from flask_restful import Resource, fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import BadRequest, InternalServerError, NotFound import services diff --git a/api/controllers/service_api/app/workflow.py b/api/controllers/service_api/app/workflow.py index 96d1337632..c7dd4de345 100644 --- a/api/controllers/service_api/app/workflow.py +++ b/api/controllers/service_api/app/workflow.py @@ -1,7 +1,7 @@ import logging -from flask_restful import Resource, fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import Resource, fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import InternalServerError from controllers.service_api import api diff --git a/api/controllers/service_api/dataset/dataset.py b/api/controllers/service_api/dataset/dataset.py index 799fccc228..d6a3beb6b8 100644 --- a/api/controllers/service_api/dataset/dataset.py +++ b/api/controllers/service_api/dataset/dataset.py @@ -1,5 +1,5 @@ from flask import request -from flask_restful import marshal, reqparse +from flask_restful import marshal, reqparse # type: ignore from werkzeug.exceptions import NotFound import services.dataset_service diff --git a/api/controllers/service_api/dataset/document.py b/api/controllers/service_api/dataset/document.py index 5c3fc7b241..34afe2837f 100644 --- a/api/controllers/service_api/dataset/document.py +++ b/api/controllers/service_api/dataset/document.py @@ -1,7 +1,7 @@ import json from flask import request -from flask_restful import marshal, reqparse +from flask_restful import marshal, reqparse # type: ignore from sqlalchemy import desc from werkzeug.exceptions import NotFound diff --git a/api/controllers/service_api/dataset/segment.py b/api/controllers/service_api/dataset/segment.py index e68f6b4dc4..34904574a8 100644 --- a/api/controllers/service_api/dataset/segment.py +++ b/api/controllers/service_api/dataset/segment.py @@ -1,5 +1,5 @@ -from flask_login import current_user -from flask_restful import marshal, reqparse +from flask_login import current_user # type: ignore +from flask_restful import marshal, reqparse # type: ignore from werkzeug.exceptions import NotFound from controllers.service_api import api diff --git a/api/controllers/service_api/index.py b/api/controllers/service_api/index.py index d24c4597e2..75d9141a6d 100644 --- a/api/controllers/service_api/index.py +++ b/api/controllers/service_api/index.py @@ -1,4 +1,4 @@ -from flask_restful import Resource +from flask_restful import Resource # type: ignore from configs import dify_config from controllers.service_api import api diff --git a/api/controllers/service_api/wraps.py b/api/controllers/service_api/wraps.py index 2128c4c53f..740b92ef8e 100644 --- a/api/controllers/service_api/wraps.py +++ b/api/controllers/service_api/wraps.py @@ -5,8 +5,8 @@ from functools import wraps from typing import Optional from flask import current_app, request -from flask_login import user_logged_in -from flask_restful import Resource +from flask_login import user_logged_in # type: ignore +from flask_restful import Resource # type: ignore from pydantic import BaseModel from werkzeug.exceptions import Forbidden, Unauthorized @@ -49,6 +49,8 @@ def validate_app_token(view: Optional[Callable] = None, *, fetch_user_arg: Optio raise Forbidden("The app's API service has been disabled.") tenant = db.session.query(Tenant).filter(Tenant.id == app_model.tenant_id).first() + if tenant is None: + raise ValueError("Tenant does not exist.") if tenant.status == TenantStatus.ARCHIVE: raise Forbidden("The workspace's status is archived.") @@ -154,8 +156,8 @@ def validate_dataset_token(view=None): # Login admin if account: account.current_tenant = tenant - current_app.login_manager._update_request_context_with_user(account) - user_logged_in.send(current_app._get_current_object(), user=_get_user()) + current_app.login_manager._update_request_context_with_user(account) # type: ignore + user_logged_in.send(current_app._get_current_object(), user=_get_user()) # type: ignore else: raise Unauthorized("Tenant owner account does not exist.") else: diff --git a/api/controllers/web/app.py b/api/controllers/web/app.py index cc8255ccf4..20e071c834 100644 --- a/api/controllers/web/app.py +++ b/api/controllers/web/app.py @@ -1,4 +1,4 @@ -from flask_restful import marshal_with +from flask_restful import marshal_with # type: ignore from controllers.common import fields from controllers.common import helpers as controller_helpers diff --git a/api/controllers/web/audio.py b/api/controllers/web/audio.py index e8521307ad..97d980d07c 100644 --- a/api/controllers/web/audio.py +++ b/api/controllers/web/audio.py @@ -65,7 +65,7 @@ class AudioApi(WebApiResource): class TextApi(WebApiResource): def post(self, app_model: App, end_user): - from flask_restful import reqparse + from flask_restful import reqparse # type: ignore try: parser = reqparse.RequestParser() @@ -82,7 +82,7 @@ class TextApi(WebApiResource): and app_model.workflow and app_model.workflow.features_dict ): - text_to_speech = app_model.workflow.features_dict.get("text_to_speech") + text_to_speech = app_model.workflow.features_dict.get("text_to_speech", {}) voice = args.get("voice") or text_to_speech.get("voice") else: try: diff --git a/api/controllers/web/completion.py b/api/controllers/web/completion.py index 45b890dfc4..761771a81a 100644 --- a/api/controllers/web/completion.py +++ b/api/controllers/web/completion.py @@ -1,6 +1,6 @@ import logging -from flask_restful import reqparse +from flask_restful import reqparse # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services diff --git a/api/controllers/web/conversation.py b/api/controllers/web/conversation.py index fe0d7c74f3..28feb1ca47 100644 --- a/api/controllers/web/conversation.py +++ b/api/controllers/web/conversation.py @@ -1,5 +1,5 @@ -from flask_restful import marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound diff --git a/api/controllers/web/feature.py b/api/controllers/web/feature.py index 0563ed2238..ce841a8814 100644 --- a/api/controllers/web/feature.py +++ b/api/controllers/web/feature.py @@ -1,4 +1,4 @@ -from flask_restful import Resource +from flask_restful import Resource # type: ignore from controllers.web import api from services.feature_service import FeatureService diff --git a/api/controllers/web/files.py b/api/controllers/web/files.py index a282fc63a8..1d4474015a 100644 --- a/api/controllers/web/files.py +++ b/api/controllers/web/files.py @@ -1,5 +1,5 @@ from flask import request -from flask_restful import marshal_with +from flask_restful import marshal_with # type: ignore import services from controllers.common.errors import FilenameNotExistsError @@ -33,7 +33,7 @@ class FileApi(WebApiResource): content=file.read(), mimetype=file.mimetype, user=end_user, - source=source, + source="datasets" if source == "datasets" else None, ) except services.errors.file.FileTooLargeError as file_too_large_error: raise FileTooLargeError(file_too_large_error.description) diff --git a/api/controllers/web/message.py b/api/controllers/web/message.py index febaab5328..0f47e64370 100644 --- a/api/controllers/web/message.py +++ b/api/controllers/web/message.py @@ -1,7 +1,7 @@ import logging -from flask_restful import fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import InternalServerError, NotFound import services diff --git a/api/controllers/web/passport.py b/api/controllers/web/passport.py index a01ffd8612..4625c1f43d 100644 --- a/api/controllers/web/passport.py +++ b/api/controllers/web/passport.py @@ -1,7 +1,7 @@ import uuid from flask import request -from flask_restful import Resource +from flask_restful import Resource # type: ignore from werkzeug.exceptions import NotFound, Unauthorized from controllers.web import api diff --git a/api/controllers/web/remote_files.py b/api/controllers/web/remote_files.py index ae68df6bdc..d559ab8e07 100644 --- a/api/controllers/web/remote_files.py +++ b/api/controllers/web/remote_files.py @@ -1,7 +1,7 @@ import urllib.parse import httpx -from flask_restful import marshal_with, reqparse +from flask_restful import marshal_with, reqparse # type: ignore import services from controllers.common import helpers diff --git a/api/controllers/web/saved_message.py b/api/controllers/web/saved_message.py index b0492e6b6f..6a9b818907 100644 --- a/api/controllers/web/saved_message.py +++ b/api/controllers/web/saved_message.py @@ -1,5 +1,5 @@ -from flask_restful import fields, marshal_with, reqparse -from flask_restful.inputs import int_range +from flask_restful import fields, marshal_with, reqparse # type: ignore +from flask_restful.inputs import int_range # type: ignore from werkzeug.exceptions import NotFound from controllers.web import api diff --git a/api/controllers/web/site.py b/api/controllers/web/site.py index 0564b15ea3..e68dc7aa4a 100644 --- a/api/controllers/web/site.py +++ b/api/controllers/web/site.py @@ -1,4 +1,4 @@ -from flask_restful import fields, marshal_with +from flask_restful import fields, marshal_with # type: ignore from werkzeug.exceptions import Forbidden from configs import dify_config diff --git a/api/controllers/web/workflow.py b/api/controllers/web/workflow.py index 55b0c3e2ab..48d25e720c 100644 --- a/api/controllers/web/workflow.py +++ b/api/controllers/web/workflow.py @@ -1,6 +1,6 @@ import logging -from flask_restful import reqparse +from flask_restful import reqparse # type: ignore from werkzeug.exceptions import InternalServerError from controllers.web import api diff --git a/api/controllers/web/wraps.py b/api/controllers/web/wraps.py index c327c3df18..1b4d263bee 100644 --- a/api/controllers/web/wraps.py +++ b/api/controllers/web/wraps.py @@ -1,7 +1,7 @@ from functools import wraps from flask import request -from flask_restful import Resource +from flask_restful import Resource # type: ignore from werkzeug.exceptions import BadRequest, NotFound, Unauthorized from controllers.web.error import WebSSOAuthRequiredError diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index ead293200e..8d69bdcec2 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -1,7 +1,6 @@ import json import logging import uuid -from collections.abc import Mapping, Sequence from datetime import UTC, datetime from typing import Optional, Union, cast @@ -53,6 +52,7 @@ logger = logging.getLogger(__name__) class BaseAgentRunner(AppRunner): def __init__( self, + *, tenant_id: str, application_generate_entity: AgentChatAppGenerateEntity, conversation: Conversation, @@ -66,7 +66,7 @@ class BaseAgentRunner(AppRunner): prompt_messages: Optional[list[PromptMessage]] = None, variables_pool: Optional[ToolRuntimeVariablePool] = None, db_variables: Optional[ToolConversationVariables] = None, - model_instance: ModelInstance | None = None, + model_instance: ModelInstance, ) -> None: self.tenant_id = tenant_id self.application_generate_entity = application_generate_entity @@ -117,7 +117,7 @@ class BaseAgentRunner(AppRunner): features = model_schema.features if model_schema and model_schema.features else [] self.stream_tool_call = ModelFeature.STREAM_TOOL_CALL in features self.files = application_generate_entity.files if ModelFeature.VISION in features else [] - self.query = None + self.query: Optional[str] = "" self._current_thoughts: list[PromptMessage] = [] def _repack_app_generate_entity( @@ -145,7 +145,7 @@ class BaseAgentRunner(AppRunner): message_tool = PromptMessageTool( name=tool.tool_name, - description=tool_entity.description.llm, + description=tool_entity.description.llm if tool_entity.description else "", parameters={ "type": "object", "properties": {}, @@ -167,7 +167,7 @@ class BaseAgentRunner(AppRunner): continue enum = [] if parameter.type == ToolParameter.ToolParameterType.SELECT: - enum = [option.value for option in parameter.options] + enum = [option.value for option in parameter.options] if parameter.options else [] message_tool.parameters["properties"][parameter.name] = { "type": parameter_type, @@ -187,8 +187,8 @@ class BaseAgentRunner(AppRunner): convert dataset retriever tool to prompt message tool """ prompt_tool = PromptMessageTool( - name=tool.identity.name, - description=tool.description.llm, + name=tool.identity.name if tool.identity else "unknown", + description=tool.description.llm if tool.description else "", parameters={ "type": "object", "properties": {}, @@ -210,14 +210,14 @@ class BaseAgentRunner(AppRunner): return prompt_tool - def _init_prompt_tools(self) -> tuple[Mapping[str, Tool], Sequence[PromptMessageTool]]: + def _init_prompt_tools(self) -> tuple[dict[str, Tool], list[PromptMessageTool]]: """ Init tools """ tool_instances = {} prompt_messages_tools = [] - for tool in self.app_config.agent.tools if self.app_config.agent else []: + for tool in self.app_config.agent.tools or [] if self.app_config.agent else []: try: prompt_tool, tool_entity = self._convert_tool_to_prompt_message_tool(tool) except Exception: @@ -234,7 +234,8 @@ class BaseAgentRunner(AppRunner): # save prompt tool prompt_messages_tools.append(prompt_tool) # save tool entity - tool_instances[dataset_tool.identity.name] = dataset_tool + if dataset_tool.identity is not None: + tool_instances[dataset_tool.identity.name] = dataset_tool return tool_instances, prompt_messages_tools @@ -258,7 +259,7 @@ class BaseAgentRunner(AppRunner): continue enum = [] if parameter.type == ToolParameter.ToolParameterType.SELECT: - enum = [option.value for option in parameter.options] + enum = [option.value for option in parameter.options] if parameter.options else [] prompt_tool.parameters["properties"][parameter.name] = { "type": parameter_type, @@ -322,16 +323,21 @@ class BaseAgentRunner(AppRunner): tool_name: str, tool_input: Union[str, dict], thought: str, - observation: Union[str, dict], - tool_invoke_meta: Union[str, dict], + observation: Union[str, dict, None], + tool_invoke_meta: Union[str, dict, None], answer: str, messages_ids: list[str], - llm_usage: LLMUsage = None, - ) -> MessageAgentThought: + llm_usage: LLMUsage | None = None, + ): """ Save agent thought """ - agent_thought = db.session.query(MessageAgentThought).filter(MessageAgentThought.id == agent_thought.id).first() + queried_thought = ( + db.session.query(MessageAgentThought).filter(MessageAgentThought.id == agent_thought.id).first() + ) + if not queried_thought: + raise ValueError(f"Agent thought {agent_thought.id} not found") + agent_thought = queried_thought if thought is not None: agent_thought.thought = thought @@ -404,7 +410,7 @@ class BaseAgentRunner(AppRunner): """ convert tool variables to db variables """ - db_variables = ( + queried_variables = ( db.session.query(ToolConversationVariables) .filter( ToolConversationVariables.conversation_id == self.message.conversation_id, @@ -412,6 +418,11 @@ class BaseAgentRunner(AppRunner): .first() ) + if not queried_variables: + return + + db_variables = queried_variables + db_variables.updated_at = datetime.now(UTC).replace(tzinfo=None) db_variables.variables_str = json.dumps(jsonable_encoder(tool_variables.pool)) db.session.commit() @@ -421,7 +432,7 @@ class BaseAgentRunner(AppRunner): """ Organize agent history """ - result = [] + result: list[PromptMessage] = [] # check if there is a system message in the beginning of the conversation for prompt_message in prompt_messages: if isinstance(prompt_message, SystemPromptMessage): diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index d98ba5a3fa..e936acb605 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -1,7 +1,7 @@ import json from abc import ABC, abstractmethod -from collections.abc import Generator -from typing import Optional, Union +from collections.abc import Generator, Mapping +from typing import Any, Optional from core.agent.base_agent_runner import BaseAgentRunner from core.agent.entities import AgentScratchpadUnit @@ -12,6 +12,7 @@ from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, PromptMessage, + PromptMessageTool, ToolPromptMessage, UserPromptMessage, ) @@ -26,18 +27,18 @@ from models.model import Message class CotAgentRunner(BaseAgentRunner, ABC): _is_first_iteration = True _ignore_observation_providers = ["wenxin"] - _historic_prompt_messages: list[PromptMessage] = None - _agent_scratchpad: list[AgentScratchpadUnit] = None - _instruction: str = None - _query: str = None - _prompt_messages_tools: list[PromptMessage] = None + _historic_prompt_messages: list[PromptMessage] | None = None + _agent_scratchpad: list[AgentScratchpadUnit] | None = None + _instruction: str = "" # FIXME this must be str for now + _query: str | None = None + _prompt_messages_tools: list[PromptMessageTool] = [] def run( self, message: Message, query: str, - inputs: dict[str, str], - ) -> Union[Generator, LLMResult]: + inputs: Mapping[str, str], + ) -> Generator: """ Run Cot agent application """ @@ -57,19 +58,19 @@ class CotAgentRunner(BaseAgentRunner, ABC): # init instruction inputs = inputs or {} instruction = app_config.prompt_template.simple_prompt_template - self._instruction = self._fill_in_inputs_from_external_data_tools(instruction, inputs) + self._instruction = self._fill_in_inputs_from_external_data_tools(instruction=instruction or "", inputs=inputs) iteration_step = 1 - max_iteration_steps = min(app_config.agent.max_iteration, 5) + 1 + max_iteration_steps = min(app_config.agent.max_iteration if app_config.agent else 5, 5) + 1 # convert tools into ModelRuntime Tool format tool_instances, self._prompt_messages_tools = self._init_prompt_tools() function_call_state = True - llm_usage = {"usage": None} + llm_usage: dict[str, Optional[LLMUsage]] = {"usage": None} final_answer = "" - def increase_usage(final_llm_usage_dict: dict[str, LLMUsage], usage: LLMUsage): + def increase_usage(final_llm_usage_dict: dict[str, Optional[LLMUsage]], usage: LLMUsage): if not final_llm_usage_dict["usage"]: final_llm_usage_dict["usage"] = usage else: @@ -90,7 +91,7 @@ class CotAgentRunner(BaseAgentRunner, ABC): # the last iteration, remove all tools self._prompt_messages_tools = [] - message_file_ids = [] + message_file_ids: list[str] = [] agent_thought = self.create_agent_thought( message_id=message.id, message="", tool_name="", tool_input="", messages_ids=message_file_ids @@ -105,7 +106,7 @@ class CotAgentRunner(BaseAgentRunner, ABC): prompt_messages = self._organize_prompt_messages() self.recalc_llm_max_tokens(self.model_config, prompt_messages) # invoke model - chunks: Generator[LLMResultChunk, None, None] = model_instance.invoke_llm( + chunks = model_instance.invoke_llm( prompt_messages=prompt_messages, model_parameters=app_generate_entity.model_conf.parameters, tools=[], @@ -115,11 +116,14 @@ class CotAgentRunner(BaseAgentRunner, ABC): callbacks=[], ) + if not isinstance(chunks, Generator): + raise ValueError("Expected streaming response from LLM") + # check llm result if not chunks: raise ValueError("failed to invoke llm") - usage_dict = {} + usage_dict: dict[str, Optional[LLMUsage]] = {"usage": None} react_chunks = CotAgentOutputParser.handle_react_stream_output(chunks, usage_dict) scratchpad = AgentScratchpadUnit( agent_response="", @@ -139,25 +143,30 @@ class CotAgentRunner(BaseAgentRunner, ABC): if isinstance(chunk, AgentScratchpadUnit.Action): action = chunk # detect action - scratchpad.agent_response += json.dumps(chunk.model_dump()) + if scratchpad.agent_response is not None: + scratchpad.agent_response += json.dumps(chunk.model_dump()) scratchpad.action_str = json.dumps(chunk.model_dump()) scratchpad.action = action else: - scratchpad.agent_response += chunk - scratchpad.thought += chunk + if scratchpad.agent_response is not None: + scratchpad.agent_response += chunk + if scratchpad.thought is not None: + scratchpad.thought += chunk yield LLMResultChunk( model=self.model_config.model, prompt_messages=prompt_messages, system_fingerprint="", delta=LLMResultChunkDelta(index=0, message=AssistantPromptMessage(content=chunk), usage=None), ) - - scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you" - self._agent_scratchpad.append(scratchpad) + if scratchpad.thought is not None: + scratchpad.thought = scratchpad.thought.strip() or "I am thinking about how to help you" + if self._agent_scratchpad is not None: + self._agent_scratchpad.append(scratchpad) # get llm usage if "usage" in usage_dict: - increase_usage(llm_usage, usage_dict["usage"]) + if usage_dict["usage"] is not None: + increase_usage(llm_usage, usage_dict["usage"]) else: usage_dict["usage"] = LLMUsage.empty_usage() @@ -166,9 +175,9 @@ class CotAgentRunner(BaseAgentRunner, ABC): tool_name=scratchpad.action.action_name if scratchpad.action else "", tool_input={scratchpad.action.action_name: scratchpad.action.action_input} if scratchpad.action else {}, tool_invoke_meta={}, - thought=scratchpad.thought, + thought=scratchpad.thought or "", observation="", - answer=scratchpad.agent_response, + answer=scratchpad.agent_response or "", messages_ids=[], llm_usage=usage_dict["usage"], ) @@ -209,7 +218,7 @@ class CotAgentRunner(BaseAgentRunner, ABC): agent_thought=agent_thought, tool_name=scratchpad.action.action_name, tool_input={scratchpad.action.action_name: scratchpad.action.action_input}, - thought=scratchpad.thought, + thought=scratchpad.thought or "", observation={scratchpad.action.action_name: tool_invoke_response}, tool_invoke_meta={scratchpad.action.action_name: tool_invoke_meta.to_dict()}, answer=scratchpad.agent_response, @@ -247,8 +256,8 @@ class CotAgentRunner(BaseAgentRunner, ABC): answer=final_answer, messages_ids=[], ) - - self.update_db_variables(self.variables_pool, self.db_variables_pool) + if self.variables_pool is not None and self.db_variables_pool is not None: + self.update_db_variables(self.variables_pool, self.db_variables_pool) # publish end event self.queue_manager.publish( QueueMessageEndEvent( @@ -307,8 +316,9 @@ class CotAgentRunner(BaseAgentRunner, ABC): # publish files for message_file_id, save_as in message_files: - if save_as: - self.variables_pool.set_file(tool_name=tool_call_name, value=message_file_id, name=save_as) + if save_as is not None and self.variables_pool: + # FIXME the save_as type is confusing, it should be a string or not + self.variables_pool.set_file(tool_name=tool_call_name, value=message_file_id, name=str(save_as)) # publish message file self.queue_manager.publish( @@ -325,7 +335,7 @@ class CotAgentRunner(BaseAgentRunner, ABC): """ return AgentScratchpadUnit.Action(action_name=action["action"], action_input=action["action_input"]) - def _fill_in_inputs_from_external_data_tools(self, instruction: str, inputs: dict) -> str: + def _fill_in_inputs_from_external_data_tools(self, instruction: str, inputs: Mapping[str, Any]) -> str: """ fill in inputs from external data tools """ @@ -376,11 +386,13 @@ class CotAgentRunner(BaseAgentRunner, ABC): """ result: list[PromptMessage] = [] scratchpads: list[AgentScratchpadUnit] = [] - current_scratchpad: AgentScratchpadUnit = None + current_scratchpad: AgentScratchpadUnit | None = None for message in self.history_prompt_messages: if isinstance(message, AssistantPromptMessage): if not current_scratchpad: + if not isinstance(message.content, str | None): + raise NotImplementedError("expected str type") current_scratchpad = AgentScratchpadUnit( agent_response=message.content, thought=message.content or "I am thinking about how to help you", @@ -399,8 +411,12 @@ class CotAgentRunner(BaseAgentRunner, ABC): except: pass elif isinstance(message, ToolPromptMessage): - if current_scratchpad: + if not current_scratchpad: + continue + if isinstance(message.content, str): current_scratchpad.observation = message.content + else: + raise NotImplementedError("expected str type") elif isinstance(message, UserPromptMessage): if scratchpads: result.append(AssistantPromptMessage(content=self._format_assistant_message(scratchpads))) diff --git a/api/core/agent/cot_chat_agent_runner.py b/api/core/agent/cot_chat_agent_runner.py index d8d047fe91..6a96c349b2 100644 --- a/api/core/agent/cot_chat_agent_runner.py +++ b/api/core/agent/cot_chat_agent_runner.py @@ -19,7 +19,12 @@ class CotChatAgentRunner(CotAgentRunner): """ Organize system prompt """ + if not self.app_config.agent: + raise ValueError("Agent configuration is not set") + prompt_entity = self.app_config.agent.prompt + if not prompt_entity: + raise ValueError("Agent prompt configuration is not set") first_prompt = prompt_entity.first_prompt system_prompt = ( @@ -75,6 +80,7 @@ class CotChatAgentRunner(CotAgentRunner): assistant_messages = [] else: assistant_message = AssistantPromptMessage(content="") + assistant_message.content = "" # FIXME: type check tell mypy that assistant_message.content is str for unit in agent_scratchpad: if unit.is_final(): assistant_message.content += f"Final Answer: {unit.agent_response}" diff --git a/api/core/agent/cot_completion_agent_runner.py b/api/core/agent/cot_completion_agent_runner.py index 0563090537..3a4d31e047 100644 --- a/api/core/agent/cot_completion_agent_runner.py +++ b/api/core/agent/cot_completion_agent_runner.py @@ -2,7 +2,12 @@ import json from typing import Optional from core.agent.cot_agent_runner import CotAgentRunner -from core.model_runtime.entities.message_entities import AssistantPromptMessage, PromptMessage, UserPromptMessage +from core.model_runtime.entities.message_entities import ( + AssistantPromptMessage, + PromptMessage, + TextPromptMessageContent, + UserPromptMessage, +) from core.model_runtime.utils.encoders import jsonable_encoder @@ -11,7 +16,11 @@ class CotCompletionAgentRunner(CotAgentRunner): """ Organize instruction prompt """ + if self.app_config.agent is None: + raise ValueError("Agent configuration is not set") prompt_entity = self.app_config.agent.prompt + if prompt_entity is None: + raise ValueError("prompt entity is not set") first_prompt = prompt_entity.first_prompt system_prompt = ( @@ -33,7 +42,13 @@ class CotCompletionAgentRunner(CotAgentRunner): if isinstance(message, UserPromptMessage): historic_prompt += f"Question: {message.content}\n\n" elif isinstance(message, AssistantPromptMessage): - historic_prompt += message.content + "\n\n" + if isinstance(message.content, str): + historic_prompt += message.content + "\n\n" + elif isinstance(message.content, list): + for content in message.content: + if not isinstance(content, TextPromptMessageContent): + continue + historic_prompt += content.data return historic_prompt @@ -50,7 +65,7 @@ class CotCompletionAgentRunner(CotAgentRunner): # organize current assistant messages agent_scratchpad = self._agent_scratchpad assistant_prompt = "" - for unit in agent_scratchpad: + for unit in agent_scratchpad or []: if unit.is_final(): assistant_prompt += f"Final Answer: {unit.agent_response}" else: diff --git a/api/core/agent/entities.py b/api/core/agent/entities.py index 119a88fc7b..2ae87dca3f 100644 --- a/api/core/agent/entities.py +++ b/api/core/agent/entities.py @@ -78,5 +78,5 @@ class AgentEntity(BaseModel): model: str strategy: Strategy prompt: Optional[AgentPromptEntity] = None - tools: list[AgentToolEntity] = None + tools: list[AgentToolEntity] | None = None max_iteration: int = 5 diff --git a/api/core/agent/fc_agent_runner.py b/api/core/agent/fc_agent_runner.py index cd546dee12..b862c96072 100644 --- a/api/core/agent/fc_agent_runner.py +++ b/api/core/agent/fc_agent_runner.py @@ -40,6 +40,8 @@ class FunctionCallAgentRunner(BaseAgentRunner): app_generate_entity = self.application_generate_entity app_config = self.app_config + assert app_config is not None, "app_config is required" + assert app_config.agent is not None, "app_config.agent is required" # convert tools into ModelRuntime Tool format tool_instances, prompt_messages_tools = self._init_prompt_tools() @@ -49,7 +51,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): # continue to run until there is not any tool call function_call_state = True - llm_usage = {"usage": None} + llm_usage: dict[str, LLMUsage] = {"usage": LLMUsage.empty_usage()} final_answer = "" # get tracing instance @@ -75,7 +77,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): # the last iteration, remove all tools prompt_messages_tools = [] - message_file_ids = [] + message_file_ids: list[str] = [] agent_thought = self.create_agent_thought( message_id=message.id, message="", tool_name="", tool_input="", messages_ids=message_file_ids ) @@ -105,7 +107,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): current_llm_usage = None - if self.stream_tool_call: + if self.stream_tool_call and isinstance(chunks, Generator): is_first_chunk = True for chunk in chunks: if is_first_chunk: @@ -116,7 +118,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): # check if there is any tool call if self.check_tool_calls(chunk): function_call_state = True - tool_calls.extend(self.extract_tool_calls(chunk)) + tool_calls.extend(self.extract_tool_calls(chunk) or []) tool_call_names = ";".join([tool_call[1] for tool_call in tool_calls]) try: tool_call_inputs = json.dumps( @@ -131,19 +133,19 @@ class FunctionCallAgentRunner(BaseAgentRunner): for content in chunk.delta.message.content: response += content.data else: - response += chunk.delta.message.content + response += str(chunk.delta.message.content) if chunk.delta.usage: increase_usage(llm_usage, chunk.delta.usage) current_llm_usage = chunk.delta.usage yield chunk - else: - result: LLMResult = chunks + elif not self.stream_tool_call and isinstance(chunks, LLMResult): + result = chunks # check if there is any tool call if self.check_blocking_tool_calls(result): function_call_state = True - tool_calls.extend(self.extract_blocking_tool_calls(result)) + tool_calls.extend(self.extract_blocking_tool_calls(result) or []) tool_call_names = ";".join([tool_call[1] for tool_call in tool_calls]) try: tool_call_inputs = json.dumps( @@ -162,7 +164,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): for content in result.message.content: response += content.data else: - response += result.message.content + response += str(result.message.content) if not result.message.content: result.message.content = "" @@ -181,6 +183,8 @@ class FunctionCallAgentRunner(BaseAgentRunner): usage=result.usage, ), ) + else: + raise RuntimeError(f"invalid chunks type: {type(chunks)}") assistant_message = AssistantPromptMessage(content="", tool_calls=[]) if tool_calls: @@ -243,7 +247,10 @@ class FunctionCallAgentRunner(BaseAgentRunner): # publish files for message_file_id, save_as in message_files: if save_as: - self.variables_pool.set_file(tool_name=tool_call_name, value=message_file_id, name=save_as) + if self.variables_pool: + self.variables_pool.set_file( + tool_name=tool_call_name, value=message_file_id, name=save_as + ) # publish message file self.queue_manager.publish( @@ -263,7 +270,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): if tool_response["tool_response"] is not None: self._current_thoughts.append( ToolPromptMessage( - content=tool_response["tool_response"], + content=str(tool_response["tool_response"]), tool_call_id=tool_call_id, name=tool_call_name, ) @@ -273,9 +280,9 @@ class FunctionCallAgentRunner(BaseAgentRunner): # save agent thought self.save_agent_thought( agent_thought=agent_thought, - tool_name=None, - tool_input=None, - thought=None, + tool_name="", + tool_input="", + thought="", tool_invoke_meta={ tool_response["tool_call_name"]: tool_response["meta"] for tool_response in tool_responses }, @@ -283,7 +290,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): tool_response["tool_call_name"]: tool_response["tool_response"] for tool_response in tool_responses }, - answer=None, + answer="", messages_ids=message_file_ids, ) self.queue_manager.publish( @@ -296,7 +303,8 @@ class FunctionCallAgentRunner(BaseAgentRunner): iteration_step += 1 - self.update_db_variables(self.variables_pool, self.db_variables_pool) + if self.variables_pool and self.db_variables_pool: + self.update_db_variables(self.variables_pool, self.db_variables_pool) # publish end event self.queue_manager.publish( QueueMessageEndEvent( @@ -389,9 +397,9 @@ class FunctionCallAgentRunner(BaseAgentRunner): if prompt_messages and not isinstance(prompt_messages[0], SystemPromptMessage) and prompt_template: prompt_messages.insert(0, SystemPromptMessage(content=prompt_template)) - return prompt_messages + return prompt_messages or [] - def _organize_user_query(self, query, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: + def _organize_user_query(self, query: str, prompt_messages: list[PromptMessage]) -> list[PromptMessage]: """ Organize user query """ @@ -449,7 +457,7 @@ class FunctionCallAgentRunner(BaseAgentRunner): def _organize_prompt_messages(self): prompt_template = self.app_config.prompt_template.simple_prompt_template or "" self.history_prompt_messages = self._init_system_message(prompt_template, self.history_prompt_messages) - query_prompt_messages = self._organize_user_query(self.query, []) + query_prompt_messages = self._organize_user_query(self.query or "", []) self.history_prompt_messages = AgentHistoryPromptTransform( model_config=self.model_config, diff --git a/api/core/agent/output_parser/cot_output_parser.py b/api/core/agent/output_parser/cot_output_parser.py index 085bac8601..61fa774ea5 100644 --- a/api/core/agent/output_parser/cot_output_parser.py +++ b/api/core/agent/output_parser/cot_output_parser.py @@ -38,7 +38,7 @@ class CotAgentOutputParser: except: return json_str or "" - def extra_json_from_code_block(code_block) -> Generator[Union[dict, str], None, None]: + def extra_json_from_code_block(code_block) -> Generator[Union[str, AgentScratchpadUnit.Action], None, None]: code_blocks = re.findall(r"```(.*?)```", code_block, re.DOTALL) if not code_blocks: return @@ -67,15 +67,15 @@ class CotAgentOutputParser: for response in llm_response: if response.delta.usage: usage_dict["usage"] = response.delta.usage - response = response.delta.message.content - if not isinstance(response, str): + response_content = response.delta.message.content + if not isinstance(response_content, str): continue # stream index = 0 - while index < len(response): + while index < len(response_content): steps = 1 - delta = response[index : index + steps] + delta = response_content[index : index + steps] yield_delta = False if delta == "`": diff --git a/api/core/app/app_config/easy_ui_based_app/dataset/manager.py b/api/core/app/app_config/easy_ui_based_app/dataset/manager.py index b9aae7904f..646c4badb9 100644 --- a/api/core/app/app_config/easy_ui_based_app/dataset/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/dataset/manager.py @@ -66,6 +66,8 @@ class DatasetConfigManager: dataset_configs = config.get("dataset_configs") else: dataset_configs = {"retrieval_model": "multiple"} + if dataset_configs is None: + return None query_variable = config.get("dataset_query_variable") if dataset_configs["retrieval_model"] == "single": diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py index 5adcf26f14..6426865115 100644 --- a/api/core/app/app_config/easy_ui_based_app/model_config/manager.py +++ b/api/core/app/app_config/easy_ui_based_app/model_config/manager.py @@ -94,7 +94,7 @@ class ModelConfigManager: config["model"]["completion_params"] ) - return config, ["model"] + return dict(config), ["model"] @classmethod def validate_model_completion_params(cls, cp: dict) -> dict: diff --git a/api/core/app/app_config/features/opening_statement/manager.py b/api/core/app/app_config/features/opening_statement/manager.py index b4dacbc409..92b4185abf 100644 --- a/api/core/app/app_config/features/opening_statement/manager.py +++ b/api/core/app/app_config/features/opening_statement/manager.py @@ -7,10 +7,10 @@ class OpeningStatementConfigManager: :param config: model config args """ # opening statement - opening_statement = config.get("opening_statement") + opening_statement = config.get("opening_statement", "") # suggested questions - suggested_questions_list = config.get("suggested_questions") + suggested_questions_list = config.get("suggested_questions", []) return opening_statement, suggested_questions_list diff --git a/api/core/app/apps/advanced_chat/app_generator.py b/api/core/app/apps/advanced_chat/app_generator.py index 6200299d21..a18b40712b 100644 --- a/api/core/app/apps/advanced_chat/app_generator.py +++ b/api/core/app/apps/advanced_chat/app_generator.py @@ -29,6 +29,7 @@ from factories import file_factory from models.account import Account from models.model import App, Conversation, EndUser, Message from models.workflow import Workflow +from services.errors.message import MessageNotExistsError logger = logging.getLogger(__name__) @@ -145,7 +146,7 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id ), query=query, - files=file_objs, + files=list(file_objs), parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, user_id=user.id, stream=streaming, @@ -313,6 +314,8 @@ class AdvancedChatAppGenerator(MessageBasedAppGenerator): # get conversation and message conversation = self._get_conversation(conversation_id) message = self._get_message(message_id) + if message is None: + raise MessageNotExistsError("Message not exists") # chatbot app runner = AdvancedChatAppRunner( diff --git a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py index 29709914b7..a506447671 100644 --- a/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py +++ b/api/core/app/apps/advanced_chat/app_generator_tts_publisher.py @@ -5,6 +5,7 @@ import queue import re import threading from collections.abc import Iterable +from typing import Optional from core.app.entities.queue_entities import ( MessageQueueMessage, @@ -15,6 +16,7 @@ from core.app.entities.queue_entities import ( WorkflowQueueMessage, ) from core.model_manager import ModelInstance, ModelManager +from core.model_runtime.entities.message_entities import TextPromptMessageContent from core.model_runtime.entities.model_entities import ModelType @@ -71,8 +73,9 @@ class AppGeneratorTTSPublisher: if not voice or voice not in values: self.voice = self.voices[0].get("value") self.MAX_SENTENCE = 2 - self._last_audio_event = None - self._runtime_thread = threading.Thread(target=self._runtime).start() + self._last_audio_event: Optional[AudioTrunk] = None + # FIXME better way to handle this threading.start + threading.Thread(target=self._runtime).start() self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=3) def publish(self, message: WorkflowQueueMessage | MessageQueueMessage | None, /): @@ -92,10 +95,21 @@ class AppGeneratorTTSPublisher: future_queue.put(futures_result) break elif isinstance(message.event, QueueAgentMessageEvent | QueueLLMChunkEvent): - self.msg_text += message.event.chunk.delta.message.content + message_content = message.event.chunk.delta.message.content + if not message_content: + continue + if isinstance(message_content, str): + self.msg_text += message_content + elif isinstance(message_content, list): + for content in message_content: + if not isinstance(content, TextPromptMessageContent): + continue + self.msg_text += content.data elif isinstance(message.event, QueueTextChunkEvent): self.msg_text += message.event.text elif isinstance(message.event, QueueNodeSucceededEvent): + if message.event.outputs is None: + continue self.msg_text += message.event.outputs.get("output", "") self.last_message = message sentence_arr, text_tmp = self._extract_sentence(self.msg_text) @@ -121,11 +135,10 @@ class AppGeneratorTTSPublisher: if self._last_audio_event and self._last_audio_event.status == "finish": if self.executor: self.executor.shutdown(wait=False) - return self.last_message + return self._last_audio_event audio = self._audio_queue.get_nowait() if audio and audio.status == "finish": self.executor.shutdown(wait=False) - self._runtime_thread = None if audio: self._last_audio_event = audio return audio diff --git a/api/core/app/apps/advanced_chat/app_runner.py b/api/core/app/apps/advanced_chat/app_runner.py index cf0c9d7593..6339d79898 100644 --- a/api/core/app/apps/advanced_chat/app_runner.py +++ b/api/core/app/apps/advanced_chat/app_runner.py @@ -109,18 +109,18 @@ class AdvancedChatAppRunner(WorkflowBasedAppRunner): ConversationVariable.conversation_id == self.conversation.id, ) with Session(db.engine) as session: - conversation_variables = session.scalars(stmt).all() - if not conversation_variables: + db_conversation_variables = session.scalars(stmt).all() + if not db_conversation_variables: # Create conversation variables if they don't exist. - conversation_variables = [ + db_conversation_variables = [ ConversationVariable.from_variable( app_id=self.conversation.app_id, conversation_id=self.conversation.id, variable=variable ) for variable in workflow.conversation_variables ] - session.add_all(conversation_variables) + session.add_all(db_conversation_variables) # Convert database entities to variables. - conversation_variables = [item.to_variable() for item in conversation_variables] + conversation_variables = [item.to_variable() for item in db_conversation_variables] session.commit() diff --git a/api/core/app/apps/advanced_chat/generate_task_pipeline.py b/api/core/app/apps/advanced_chat/generate_task_pipeline.py index 635e482ad9..1073a0f2e4 100644 --- a/api/core/app/apps/advanced_chat/generate_task_pipeline.py +++ b/api/core/app/apps/advanced_chat/generate_task_pipeline.py @@ -2,6 +2,7 @@ import json import logging import time from collections.abc import Generator, Mapping +from threading import Thread from typing import Any, Optional, Union from constants.tts_auto_play_timeout import TTS_AUTO_PLAY_TIMEOUT, TTS_AUTO_PLAY_YIELD_CPU_TIME @@ -64,6 +65,7 @@ from models.enums import CreatedByRole from models.workflow import ( Workflow, WorkflowNodeExecution, + WorkflowRun, WorkflowRunStatus, ) @@ -81,6 +83,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc _user: Union[Account, EndUser] _workflow_system_variables: dict[SystemVariableKey, Any] _wip_workflow_node_executions: dict[str, WorkflowNodeExecution] + _conversation_name_generate_thread: Optional[Thread] = None def __init__( self, @@ -131,7 +134,7 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc self._conversation_name_generate_thread = None self._recorded_files: list[Mapping[str, Any]] = [] - def process(self): + def process(self) -> Union[ChatbotAppBlockingResponse, Generator[ChatbotAppStreamResponse, None, None]]: """ Process generate task pipeline. :return: @@ -262,8 +265,8 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc :return: """ # init fake graph runtime state - graph_runtime_state = None - workflow_run = None + graph_runtime_state: Optional[GraphRuntimeState] = None + workflow_run: Optional[WorkflowRun] = None for queue_message in self._queue_manager.listen(): event = queue_message.event @@ -315,14 +318,14 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc workflow_node_execution = self._handle_node_execution_start(workflow_run=workflow_run, event=event) - response = self._workflow_node_start_to_stream_response( + response_start = self._workflow_node_start_to_stream_response( event=event, task_id=self._application_generate_entity.task_id, workflow_node_execution=workflow_node_execution, ) - if response: - yield response + if response_start: + yield response_start elif isinstance(event, QueueNodeSucceededEvent): workflow_node_execution = self._handle_workflow_node_execution_success(event) @@ -330,18 +333,18 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc if event.node_type in [NodeType.ANSWER, NodeType.END]: self._recorded_files.extend(self._fetch_files_from_node_outputs(event.outputs or {})) - response = self._workflow_node_finish_to_stream_response( + response_finish = self._workflow_node_finish_to_stream_response( event=event, task_id=self._application_generate_entity.task_id, workflow_node_execution=workflow_node_execution, ) - if response: - yield response + if response_finish: + yield response_finish elif isinstance(event, QueueNodeFailedEvent | QueueNodeInIterationFailedEvent | QueueNodeExceptionEvent): workflow_node_execution = self._handle_workflow_node_execution_failed(event) - response = self._workflow_node_finish_to_stream_response( + response_finish = self._workflow_node_finish_to_stream_response( event=event, task_id=self._application_generate_entity.task_id, workflow_node_execution=workflow_node_execution, @@ -609,7 +612,10 @@ class AdvancedChatAppGenerateTaskPipeline(BasedGenerateTaskPipeline, WorkflowCyc del extras["metadata"]["annotation_reply"] return MessageEndStreamResponse( - task_id=self._application_generate_entity.task_id, id=self._message.id, files=self._recorded_files, **extras + task_id=self._application_generate_entity.task_id, + id=self._message.id, + files=self._recorded_files, + metadata=extras.get("metadata", {}), ) def _handle_output_moderation_chunk(self, text: str) -> bool: diff --git a/api/core/app/apps/agent_chat/app_config_manager.py b/api/core/app/apps/agent_chat/app_config_manager.py index 417d23eccf..55b6ee510f 100644 --- a/api/core/app/apps/agent_chat/app_config_manager.py +++ b/api/core/app/apps/agent_chat/app_config_manager.py @@ -61,7 +61,7 @@ class AgentChatAppConfigManager(BaseAppConfigManager): app_model_config_dict = app_model_config.to_dict() config_dict = app_model_config_dict.copy() else: - config_dict = override_config_dict + config_dict = override_config_dict or {} app_mode = AppMode.value_of(app_model.mode) app_config = AgentChatAppConfig( diff --git a/api/core/app/apps/agent_chat/app_generator.py b/api/core/app/apps/agent_chat/app_generator.py index b391169e3d..63e11bdaa2 100644 --- a/api/core/app/apps/agent_chat/app_generator.py +++ b/api/core/app/apps/agent_chat/app_generator.py @@ -23,6 +23,7 @@ from core.ops.ops_trace_manager import TraceQueueManager from extensions.ext_database import db from factories import file_factory from models import Account, App, EndUser +from services.errors.message import MessageNotExistsError logger = logging.getLogger(__name__) @@ -97,7 +98,7 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): # get conversation conversation = None if args.get("conversation_id"): - conversation = self._get_conversation_by_user(app_model, args.get("conversation_id"), user) + conversation = self._get_conversation_by_user(app_model, args.get("conversation_id", ""), user) # get app model config app_model_config = self._get_app_model_config(app_model=app_model, conversation=conversation) @@ -153,7 +154,7 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id ), query=query, - files=file_objs, + files=list(file_objs), parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, user_id=user.id, stream=streaming, @@ -180,7 +181,7 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): worker_thread = threading.Thread( target=self._generate_worker, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "application_generate_entity": application_generate_entity, "queue_manager": queue_manager, "conversation_id": conversation.id, @@ -199,8 +200,8 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): user=user, stream=streaming, ) - - return AgentChatAppGenerateResponseConverter.convert(response=response, invoke_from=invoke_from) + # FIXME: Type hinting issue here, ignore it for now, will fix it later + return AgentChatAppGenerateResponseConverter.convert(response=response, invoke_from=invoke_from) # type: ignore def _generate_worker( self, @@ -224,6 +225,8 @@ class AgentChatAppGenerator(MessageBasedAppGenerator): # get conversation and message conversation = self._get_conversation(conversation_id) message = self._get_message(message_id) + if message is None: + raise MessageNotExistsError("Message not exists") # chatbot app runner = AgentChatAppRunner() diff --git a/api/core/app/apps/agent_chat/app_runner.py b/api/core/app/apps/agent_chat/app_runner.py index 45b1bf0093..ac71f02b6d 100644 --- a/api/core/app/apps/agent_chat/app_runner.py +++ b/api/core/app/apps/agent_chat/app_runner.py @@ -173,6 +173,8 @@ class AgentChatAppRunner(AppRunner): return agent_entity = app_config.agent + if not agent_entity: + raise ValueError("Agent entity not found") # load tool variables tool_conversation_variables = self._load_tool_variables( @@ -200,14 +202,21 @@ class AgentChatAppRunner(AppRunner): # change function call strategy based on LLM model llm_model = cast(LargeLanguageModel, model_instance.model_type_instance) model_schema = llm_model.get_model_schema(model_instance.model, model_instance.credentials) + if not model_schema or not model_schema.features: + raise ValueError("Model schema not found") if {ModelFeature.MULTI_TOOL_CALL, ModelFeature.TOOL_CALL}.intersection(model_schema.features or []): agent_entity.strategy = AgentEntity.Strategy.FUNCTION_CALLING - conversation = db.session.query(Conversation).filter(Conversation.id == conversation.id).first() - message = db.session.query(Message).filter(Message.id == message.id).first() + conversation_result = db.session.query(Conversation).filter(Conversation.id == conversation.id).first() + if conversation_result is None: + raise ValueError("Conversation not found") + message_result = db.session.query(Message).filter(Message.id == message.id).first() + if message_result is None: + raise ValueError("Message not found") db.session.close() + runner_cls: type[FunctionCallAgentRunner] | type[CotChatAgentRunner] | type[CotCompletionAgentRunner] # start agent runner if agent_entity.strategy == AgentEntity.Strategy.CHAIN_OF_THOUGHT: # check LLM mode @@ -225,12 +234,12 @@ class AgentChatAppRunner(AppRunner): runner = runner_cls( tenant_id=app_config.tenant_id, application_generate_entity=application_generate_entity, - conversation=conversation, + conversation=conversation_result, app_config=app_config, model_config=application_generate_entity.model_conf, config=agent_entity, queue_manager=queue_manager, - message=message, + message=message_result, user_id=application_generate_entity.user_id, memory=memory, prompt_messages=prompt_message, @@ -257,7 +266,7 @@ class AgentChatAppRunner(AppRunner): """ load tool variables from database """ - tool_variables: ToolConversationVariables = ( + tool_variables: ToolConversationVariables | None = ( db.session.query(ToolConversationVariables) .filter( ToolConversationVariables.conversation_id == conversation_id, diff --git a/api/core/app/apps/agent_chat/generate_response_converter.py b/api/core/app/apps/agent_chat/generate_response_converter.py index 629c309c06..ce331d904c 100644 --- a/api/core/app/apps/agent_chat/generate_response_converter.py +++ b/api/core/app/apps/agent_chat/generate_response_converter.py @@ -16,7 +16,7 @@ class AgentChatAppGenerateResponseConverter(AppGenerateResponseConverter): _blocking_response_type = ChatbotAppBlockingResponse @classmethod - def convert_blocking_full_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: + def convert_blocking_full_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking full response. :param blocking_response: blocking response @@ -37,7 +37,7 @@ class AgentChatAppGenerateResponseConverter(AppGenerateResponseConverter): return response @classmethod - def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: + def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking simple response. :param blocking_response: blocking response @@ -51,8 +51,9 @@ class AgentChatAppGenerateResponseConverter(AppGenerateResponseConverter): return response @classmethod - def convert_stream_full_response( - cls, stream_response: Generator[ChatbotAppStreamResponse, None, None] + def convert_stream_full_response( # type: ignore[override] + cls, + stream_response: Generator[ChatbotAppStreamResponse, None, None], ) -> Generator[str, None, None]: """ Convert stream full response. @@ -82,8 +83,9 @@ class AgentChatAppGenerateResponseConverter(AppGenerateResponseConverter): yield json.dumps(response_chunk) @classmethod - def convert_stream_simple_response( - cls, stream_response: Generator[ChatbotAppStreamResponse, None, None] + def convert_stream_simple_response( # type: ignore[override] + cls, + stream_response: Generator[ChatbotAppStreamResponse, None, None], ) -> Generator[str, None, None]: """ Convert stream simple response. diff --git a/api/core/app/apps/base_app_queue_manager.py b/api/core/app/apps/base_app_queue_manager.py index 3725c6e6dd..1842fc4303 100644 --- a/api/core/app/apps/base_app_queue_manager.py +++ b/api/core/app/apps/base_app_queue_manager.py @@ -50,7 +50,7 @@ class AppQueueManager: # wait for APP_MAX_EXECUTION_TIME seconds to stop listen listen_timeout = dify_config.APP_MAX_EXECUTION_TIME start_time = time.time() - last_ping_time = 0 + last_ping_time: int | float = 0 while True: try: message = self._q.get(timeout=1) diff --git a/api/core/app/apps/base_app_runner.py b/api/core/app/apps/base_app_runner.py index 609fd03f22..07a248d77a 100644 --- a/api/core/app/apps/base_app_runner.py +++ b/api/core/app/apps/base_app_runner.py @@ -1,5 +1,5 @@ import time -from collections.abc import Generator, Mapping +from collections.abc import Generator, Mapping, Sequence from typing import TYPE_CHECKING, Any, Optional, Union from core.app.app_config.entities import ExternalDataVariableEntity, PromptTemplateEntity @@ -36,8 +36,8 @@ class AppRunner: app_record: App, model_config: ModelConfigWithCredentialsEntity, prompt_template_entity: PromptTemplateEntity, - inputs: dict[str, str], - files: list["File"], + inputs: Mapping[str, str], + files: Sequence["File"], query: Optional[str] = None, ) -> int: """ @@ -64,7 +64,7 @@ class AppRunner: ): max_tokens = ( model_config.parameters.get(parameter_rule.name) - or model_config.parameters.get(parameter_rule.use_template) + or model_config.parameters.get(parameter_rule.use_template or "") ) or 0 if model_context_tokens is None: @@ -85,7 +85,7 @@ class AppRunner: prompt_tokens = model_instance.get_llm_num_tokens(prompt_messages) - rest_tokens = model_context_tokens - max_tokens - prompt_tokens + rest_tokens: int = model_context_tokens - max_tokens - prompt_tokens if rest_tokens < 0: raise InvokeBadRequestError( "Query or prefix prompt is too long, you can reduce the prefix prompt, " @@ -111,7 +111,7 @@ class AppRunner: ): max_tokens = ( model_config.parameters.get(parameter_rule.name) - or model_config.parameters.get(parameter_rule.use_template) + or model_config.parameters.get(parameter_rule.use_template or "") ) or 0 if model_context_tokens is None: @@ -136,8 +136,8 @@ class AppRunner: app_record: App, model_config: ModelConfigWithCredentialsEntity, prompt_template_entity: PromptTemplateEntity, - inputs: dict[str, str], - files: list["File"], + inputs: Mapping[str, str], + files: Sequence["File"], query: Optional[str] = None, context: Optional[str] = None, memory: Optional[TokenBufferMemory] = None, @@ -156,6 +156,7 @@ class AppRunner: """ # get prompt without memory and context if prompt_template_entity.prompt_type == PromptTemplateEntity.PromptType.SIMPLE: + prompt_transform: Union[SimplePromptTransform, AdvancedPromptTransform] prompt_transform = SimplePromptTransform() prompt_messages, stop = prompt_transform.get_prompt( app_mode=AppMode.value_of(app_record.mode), @@ -171,8 +172,11 @@ class AppRunner: memory_config = MemoryConfig(window=MemoryConfig.WindowConfig(enabled=False)) model_mode = ModelMode.value_of(model_config.mode) + prompt_template: Union[CompletionModelPromptTemplate, list[ChatModelMessage]] if model_mode == ModelMode.COMPLETION: advanced_completion_prompt_template = prompt_template_entity.advanced_completion_prompt_template + if not advanced_completion_prompt_template: + raise InvokeBadRequestError("Advanced completion prompt template is required.") prompt_template = CompletionModelPromptTemplate(text=advanced_completion_prompt_template.prompt) if advanced_completion_prompt_template.role_prefix: @@ -181,6 +185,8 @@ class AppRunner: assistant=advanced_completion_prompt_template.role_prefix.assistant, ) else: + if not prompt_template_entity.advanced_chat_prompt_template: + raise InvokeBadRequestError("Advanced chat prompt template is required.") prompt_template = [] for message in prompt_template_entity.advanced_chat_prompt_template.messages: prompt_template.append(ChatModelMessage(text=message.text, role=message.role)) @@ -246,7 +252,7 @@ class AppRunner: def _handle_invoke_result( self, - invoke_result: Union[LLMResult, Generator], + invoke_result: Union[LLMResult, Generator[Any, None, None]], queue_manager: AppQueueManager, stream: bool, agent: bool = False, @@ -259,10 +265,12 @@ class AppRunner: :param agent: agent :return: """ - if not stream: + if not stream and isinstance(invoke_result, LLMResult): self._handle_invoke_result_direct(invoke_result=invoke_result, queue_manager=queue_manager, agent=agent) - else: + elif stream and isinstance(invoke_result, Generator): self._handle_invoke_result_stream(invoke_result=invoke_result, queue_manager=queue_manager, agent=agent) + else: + raise NotImplementedError(f"unsupported invoke result type: {type(invoke_result)}") def _handle_invoke_result_direct( self, invoke_result: LLMResult, queue_manager: AppQueueManager, agent: bool @@ -291,8 +299,8 @@ class AppRunner: :param agent: agent :return: """ - model = None - prompt_messages = [] + model: str = "" + prompt_messages: list[PromptMessage] = [] text = "" usage = None for result in invoke_result: @@ -328,13 +336,14 @@ class AppRunner: def moderation_for_inputs( self, + *, app_id: str, tenant_id: str, app_generate_entity: AppGenerateEntity, inputs: Mapping[str, Any], - query: str, + query: str | None = None, message_id: str, - ) -> tuple[bool, dict, str]: + ) -> tuple[bool, Mapping[str, Any], str]: """ Process sensitive_word_avoidance. :param app_id: app id @@ -350,7 +359,7 @@ class AppRunner: app_id=app_id, tenant_id=tenant_id, app_config=app_generate_entity.app_config, - inputs=inputs, + inputs=dict(inputs), query=query or "", message_id=message_id, trace_manager=app_generate_entity.trace_manager, @@ -390,9 +399,9 @@ class AppRunner: tenant_id: str, app_id: str, external_data_tools: list[ExternalDataVariableEntity], - inputs: dict, + inputs: Mapping[str, Any], query: str, - ) -> dict: + ) -> Mapping[str, Any]: """ Fill in variable inputs from external data tools if exists. diff --git a/api/core/app/apps/chat/app_generator.py b/api/core/app/apps/chat/app_generator.py index 5b8debaaae..6ed71fcd84 100644 --- a/api/core/app/apps/chat/app_generator.py +++ b/api/core/app/apps/chat/app_generator.py @@ -24,6 +24,7 @@ from extensions.ext_database import db from factories import file_factory from models.account import Account from models.model import App, EndUser +from services.errors.message import MessageNotExistsError logger = logging.getLogger(__name__) @@ -91,7 +92,7 @@ class ChatAppGenerator(MessageBasedAppGenerator): # get conversation conversation = None if args.get("conversation_id"): - conversation = self._get_conversation_by_user(app_model, args.get("conversation_id"), user) + conversation = self._get_conversation_by_user(app_model, args.get("conversation_id", ""), user) # get app model config app_model_config = self._get_app_model_config(app_model=app_model, conversation=conversation) @@ -104,7 +105,7 @@ class ChatAppGenerator(MessageBasedAppGenerator): # validate config override_model_config_dict = ChatAppConfigManager.config_validate( - tenant_id=app_model.tenant_id, config=args.get("model_config") + tenant_id=app_model.tenant_id, config=args.get("model_config", {}) ) # always enable retriever resource in debugger mode @@ -146,7 +147,7 @@ class ChatAppGenerator(MessageBasedAppGenerator): user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id ), query=query, - files=file_objs, + files=list(file_objs), parent_message_id=args.get("parent_message_id") if invoke_from != InvokeFrom.SERVICE_API else UUID_NIL, user_id=user.id, invoke_from=invoke_from, @@ -172,7 +173,7 @@ class ChatAppGenerator(MessageBasedAppGenerator): worker_thread = threading.Thread( target=self._generate_worker, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "application_generate_entity": application_generate_entity, "queue_manager": queue_manager, "conversation_id": conversation.id, @@ -216,6 +217,8 @@ class ChatAppGenerator(MessageBasedAppGenerator): # get conversation and message conversation = self._get_conversation(conversation_id) message = self._get_message(message_id) + if message is None: + raise MessageNotExistsError("Message not exists") # chatbot app runner = ChatAppRunner() diff --git a/api/core/app/apps/chat/generate_response_converter.py b/api/core/app/apps/chat/generate_response_converter.py index 0fa7af0a7f..9024c3a982 100644 --- a/api/core/app/apps/chat/generate_response_converter.py +++ b/api/core/app/apps/chat/generate_response_converter.py @@ -16,7 +16,7 @@ class ChatAppGenerateResponseConverter(AppGenerateResponseConverter): _blocking_response_type = ChatbotAppBlockingResponse @classmethod - def convert_blocking_full_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: + def convert_blocking_full_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking full response. :param blocking_response: blocking response @@ -37,7 +37,7 @@ class ChatAppGenerateResponseConverter(AppGenerateResponseConverter): return response @classmethod - def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: + def convert_blocking_simple_response(cls, blocking_response: ChatbotAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking simple response. :param blocking_response: blocking response @@ -52,7 +52,8 @@ class ChatAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_full_response( - cls, stream_response: Generator[ChatbotAppStreamResponse, None, None] + cls, + stream_response: Generator[ChatbotAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream full response. @@ -83,7 +84,8 @@ class ChatAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_simple_response( - cls, stream_response: Generator[ChatbotAppStreamResponse, None, None] + cls, + stream_response: Generator[ChatbotAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream simple response. diff --git a/api/core/app/apps/completion/app_config_manager.py b/api/core/app/apps/completion/app_config_manager.py index 1193c4b7a4..02e5d47568 100644 --- a/api/core/app/apps/completion/app_config_manager.py +++ b/api/core/app/apps/completion/app_config_manager.py @@ -42,7 +42,7 @@ class CompletionAppConfigManager(BaseAppConfigManager): app_model_config_dict = app_model_config.to_dict() config_dict = app_model_config_dict.copy() else: - config_dict = override_config_dict + config_dict = override_config_dict or {} app_mode = AppMode.value_of(app_model.mode) app_config = CompletionAppConfig( diff --git a/api/core/app/apps/completion/app_generator.py b/api/core/app/apps/completion/app_generator.py index 14fd33dd39..17d0d52497 100644 --- a/api/core/app/apps/completion/app_generator.py +++ b/api/core/app/apps/completion/app_generator.py @@ -83,8 +83,6 @@ class CompletionAppGenerator(MessageBasedAppGenerator): query = query.replace("\x00", "") inputs = args["inputs"] - extras = {} - # get conversation conversation = None @@ -99,7 +97,7 @@ class CompletionAppGenerator(MessageBasedAppGenerator): # validate config override_model_config_dict = CompletionAppConfigManager.config_validate( - tenant_id=app_model.tenant_id, config=args.get("model_config") + tenant_id=app_model.tenant_id, config=args.get("model_config", {}) ) # parse files @@ -132,11 +130,11 @@ class CompletionAppGenerator(MessageBasedAppGenerator): user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id ), query=query, - files=file_objs, + files=list(file_objs), user_id=user.id, stream=streaming, invoke_from=invoke_from, - extras=extras, + extras={}, trace_manager=trace_manager, ) @@ -157,7 +155,7 @@ class CompletionAppGenerator(MessageBasedAppGenerator): worker_thread = threading.Thread( target=self._generate_worker, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "application_generate_entity": application_generate_entity, "queue_manager": queue_manager, "message_id": message.id, @@ -197,6 +195,8 @@ class CompletionAppGenerator(MessageBasedAppGenerator): try: # get message message = self._get_message(message_id) + if message is None: + raise MessageNotExistsError() # chatbot app runner = CompletionAppRunner() @@ -231,7 +231,7 @@ class CompletionAppGenerator(MessageBasedAppGenerator): user: Union[Account, EndUser], invoke_from: InvokeFrom, stream: bool = True, - ) -> Union[dict, Generator[str, None, None]]: + ) -> Union[Mapping[str, Any], Generator[str, None, None]]: """ Generate App response. @@ -293,7 +293,7 @@ class CompletionAppGenerator(MessageBasedAppGenerator): model_conf=ModelConfigConverter.convert(app_config), inputs=message.inputs, query=message.query, - files=file_objs, + files=list(file_objs), user_id=user.id, stream=stream, invoke_from=invoke_from, @@ -317,7 +317,7 @@ class CompletionAppGenerator(MessageBasedAppGenerator): worker_thread = threading.Thread( target=self._generate_worker, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "application_generate_entity": application_generate_entity, "queue_manager": queue_manager, "message_id": message.id, diff --git a/api/core/app/apps/completion/app_runner.py b/api/core/app/apps/completion/app_runner.py index 908d74ff53..41278b75b4 100644 --- a/api/core/app/apps/completion/app_runner.py +++ b/api/core/app/apps/completion/app_runner.py @@ -76,7 +76,7 @@ class CompletionAppRunner(AppRunner): tenant_id=app_config.tenant_id, app_generate_entity=application_generate_entity, inputs=inputs, - query=query, + query=query or "", message_id=message.id, ) except ModerationError as e: @@ -122,7 +122,7 @@ class CompletionAppRunner(AppRunner): tenant_id=app_record.tenant_id, model_config=application_generate_entity.model_conf, config=dataset_config, - query=query, + query=query or "", invoke_from=application_generate_entity.invoke_from, show_retrieve_source=app_config.additional_features.show_retrieve_source, hit_callback=hit_callback, diff --git a/api/core/app/apps/completion/generate_response_converter.py b/api/core/app/apps/completion/generate_response_converter.py index 697f0273a5..73f38c3d0b 100644 --- a/api/core/app/apps/completion/generate_response_converter.py +++ b/api/core/app/apps/completion/generate_response_converter.py @@ -16,7 +16,7 @@ class CompletionAppGenerateResponseConverter(AppGenerateResponseConverter): _blocking_response_type = CompletionAppBlockingResponse @classmethod - def convert_blocking_full_response(cls, blocking_response: CompletionAppBlockingResponse) -> dict: + def convert_blocking_full_response(cls, blocking_response: CompletionAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking full response. :param blocking_response: blocking response @@ -36,7 +36,7 @@ class CompletionAppGenerateResponseConverter(AppGenerateResponseConverter): return response @classmethod - def convert_blocking_simple_response(cls, blocking_response: CompletionAppBlockingResponse) -> dict: + def convert_blocking_simple_response(cls, blocking_response: CompletionAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking simple response. :param blocking_response: blocking response @@ -51,7 +51,8 @@ class CompletionAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_full_response( - cls, stream_response: Generator[CompletionAppStreamResponse, None, None] + cls, + stream_response: Generator[CompletionAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream full response. @@ -81,7 +82,8 @@ class CompletionAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_simple_response( - cls, stream_response: Generator[CompletionAppStreamResponse, None, None] + cls, + stream_response: Generator[CompletionAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream simple response. diff --git a/api/core/app/apps/message_based_app_generator.py b/api/core/app/apps/message_based_app_generator.py index 95ae798ec1..c2e35faf89 100644 --- a/api/core/app/apps/message_based_app_generator.py +++ b/api/core/app/apps/message_based_app_generator.py @@ -2,11 +2,11 @@ import json import logging from collections.abc import Generator from datetime import UTC, datetime -from typing import Optional, Union +from typing import Optional, Union, cast from sqlalchemy import and_ -from core.app.app_config.entities import EasyUIBasedAppModelConfigFrom +from core.app.app_config.entities import EasyUIBasedAppConfig, EasyUIBasedAppModelConfigFrom from core.app.apps.base_app_generator import BaseAppGenerator from core.app.apps.base_app_queue_manager import AppQueueManager, GenerateTaskStoppedError from core.app.entities.app_invoke_entities import ( @@ -42,7 +42,7 @@ class MessageBasedAppGenerator(BaseAppGenerator): ChatAppGenerateEntity, CompletionAppGenerateEntity, AgentChatAppGenerateEntity, - AdvancedChatAppGenerateEntity, + AgentChatAppGenerateEntity, ], queue_manager: AppQueueManager, conversation: Conversation, @@ -144,7 +144,7 @@ class MessageBasedAppGenerator(BaseAppGenerator): :conversation conversation :return: """ - app_config = application_generate_entity.app_config + app_config: EasyUIBasedAppConfig = cast(EasyUIBasedAppConfig, application_generate_entity.app_config) # get from source end_user_id = None @@ -267,7 +267,7 @@ class MessageBasedAppGenerator(BaseAppGenerator): except KeyError: pass - return introduction + return introduction or "" def _get_conversation(self, conversation_id: str): """ @@ -282,7 +282,7 @@ class MessageBasedAppGenerator(BaseAppGenerator): return conversation - def _get_message(self, message_id: str) -> Message: + def _get_message(self, message_id: str) -> Optional[Message]: """ Get message by message id :param message_id: message id diff --git a/api/core/app/apps/workflow/app_generator.py b/api/core/app/apps/workflow/app_generator.py index dc4ee9e566..1d5f21b9e0 100644 --- a/api/core/app/apps/workflow/app_generator.py +++ b/api/core/app/apps/workflow/app_generator.py @@ -116,7 +116,7 @@ class WorkflowAppGenerator(BaseAppGenerator): inputs=self._prepare_user_inputs( user_inputs=inputs, variables=app_config.variables, tenant_id=app_model.tenant_id ), - files=system_files, + files=list(system_files), user_id=user.id, stream=streaming, invoke_from=invoke_from, diff --git a/api/core/app/apps/workflow/generate_response_converter.py b/api/core/app/apps/workflow/generate_response_converter.py index 08d00ee180..5cdac6ad28 100644 --- a/api/core/app/apps/workflow/generate_response_converter.py +++ b/api/core/app/apps/workflow/generate_response_converter.py @@ -17,16 +17,16 @@ class WorkflowAppGenerateResponseConverter(AppGenerateResponseConverter): _blocking_response_type = WorkflowAppBlockingResponse @classmethod - def convert_blocking_full_response(cls, blocking_response: WorkflowAppBlockingResponse) -> dict: + def convert_blocking_full_response(cls, blocking_response: WorkflowAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking full response. :param blocking_response: blocking response :return: """ - return blocking_response.to_dict() + return dict(blocking_response.to_dict()) @classmethod - def convert_blocking_simple_response(cls, blocking_response: WorkflowAppBlockingResponse) -> dict: + def convert_blocking_simple_response(cls, blocking_response: WorkflowAppBlockingResponse) -> dict: # type: ignore[override] """ Convert blocking simple response. :param blocking_response: blocking response @@ -36,7 +36,8 @@ class WorkflowAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_full_response( - cls, stream_response: Generator[WorkflowAppStreamResponse, None, None] + cls, + stream_response: Generator[WorkflowAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream full response. @@ -65,7 +66,8 @@ class WorkflowAppGenerateResponseConverter(AppGenerateResponseConverter): @classmethod def convert_stream_simple_response( - cls, stream_response: Generator[WorkflowAppStreamResponse, None, None] + cls, + stream_response: Generator[WorkflowAppStreamResponse, None, None], # type: ignore[override] ) -> Generator[str, None, None]: """ Convert stream simple response. diff --git a/api/core/app/apps/workflow_app_runner.py b/api/core/app/apps/workflow_app_runner.py index 885283504b..63f516bcc6 100644 --- a/api/core/app/apps/workflow_app_runner.py +++ b/api/core/app/apps/workflow_app_runner.py @@ -24,6 +24,7 @@ from core.app.entities.queue_entities import ( QueueWorkflowStartedEvent, QueueWorkflowSucceededEvent, ) +from core.workflow.entities.node_entities import NodeRunMetadataKey from core.workflow.entities.variable_pool import VariablePool from core.workflow.graph_engine.entities.event import ( GraphEngineEvent, @@ -190,16 +191,15 @@ class WorkflowBasedAppRunner(AppRunner): self._publish_event(QueueWorkflowFailedEvent(error=event.error, exceptions_count=event.exceptions_count)) elif isinstance(event, NodeRunRetryEvent): node_run_result = event.route_node_state.node_run_result + inputs: Mapping[str, Any] | None = {} + process_data: Mapping[str, Any] | None = {} + outputs: Mapping[str, Any] | None = {} + execution_metadata: Mapping[NodeRunMetadataKey, Any] | None = {} if node_run_result: inputs = node_run_result.inputs process_data = node_run_result.process_data outputs = node_run_result.outputs execution_metadata = node_run_result.metadata - else: - inputs = {} - process_data = {} - outputs = {} - execution_metadata = {} self._publish_event( QueueNodeRetryEvent( node_execution_id=event.id, @@ -289,7 +289,7 @@ class WorkflowBasedAppRunner(AppRunner): process_data=event.route_node_state.node_run_result.process_data if event.route_node_state.node_run_result else {}, - outputs=event.route_node_state.node_run_result.outputs + outputs=event.route_node_state.node_run_result.outputs or {} if event.route_node_state.node_run_result else {}, error=event.route_node_state.node_run_result.error @@ -349,7 +349,7 @@ class WorkflowBasedAppRunner(AppRunner): process_data=event.route_node_state.node_run_result.process_data if event.route_node_state.node_run_result else {}, - outputs=event.route_node_state.node_run_result.outputs + outputs=event.route_node_state.node_run_result.outputs or {} if event.route_node_state.node_run_result else {}, execution_metadata=event.route_node_state.node_run_result.metadata diff --git a/api/core/app/entities/app_invoke_entities.py b/api/core/app/entities/app_invoke_entities.py index 31c3a996e1..16dc91bb77 100644 --- a/api/core/app/entities/app_invoke_entities.py +++ b/api/core/app/entities/app_invoke_entities.py @@ -5,7 +5,7 @@ from typing import Any, Optional from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator from constants import UUID_NIL -from core.app.app_config.entities import AppConfig, EasyUIBasedAppConfig, WorkflowUIBasedAppConfig +from core.app.app_config.entities import EasyUIBasedAppConfig, WorkflowUIBasedAppConfig from core.entities.provider_configuration import ProviderModelBundle from core.file import File, FileUploadConfig from core.model_runtime.entities.model_entities import AIModelEntity @@ -79,7 +79,7 @@ class AppGenerateEntity(BaseModel): task_id: str # app config - app_config: AppConfig + app_config: Any file_upload_config: Optional[FileUploadConfig] = None inputs: Mapping[str, Any] diff --git a/api/core/app/entities/queue_entities.py b/api/core/app/entities/queue_entities.py index d73c2eb53b..a93e533ff4 100644 --- a/api/core/app/entities/queue_entities.py +++ b/api/core/app/entities/queue_entities.py @@ -308,7 +308,7 @@ class QueueNodeSucceededEvent(AppQueueEvent): inputs: Optional[Mapping[str, Any]] = None process_data: Optional[Mapping[str, Any]] = None outputs: Optional[Mapping[str, Any]] = None - execution_metadata: Optional[dict[NodeRunMetadataKey, Any]] = None + execution_metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None error: Optional[str] = None """single iteration duration map""" diff --git a/api/core/app/entities/task_entities.py b/api/core/app/entities/task_entities.py index dd088a8978..5e845eba2d 100644 --- a/api/core/app/entities/task_entities.py +++ b/api/core/app/entities/task_entities.py @@ -70,7 +70,7 @@ class StreamResponse(BaseModel): event: StreamEvent task_id: str - def to_dict(self) -> dict: + def to_dict(self): return jsonable_encoder(self) @@ -474,8 +474,8 @@ class IterationNodeStartStreamResponse(StreamResponse): title: str created_at: int extras: dict = {} - metadata: dict = {} - inputs: dict = {} + metadata: Mapping = {} + inputs: Mapping = {} parallel_id: Optional[str] = None parallel_start_node_id: Optional[str] = None @@ -526,15 +526,15 @@ class IterationNodeCompletedStreamResponse(StreamResponse): node_id: str node_type: str title: str - outputs: Optional[dict] = None + outputs: Optional[Mapping] = None created_at: int extras: Optional[dict] = None - inputs: Optional[dict] = None + inputs: Optional[Mapping] = None status: WorkflowNodeExecutionStatus error: Optional[str] = None elapsed_time: float total_tokens: int - execution_metadata: Optional[dict] = None + execution_metadata: Optional[Mapping] = None finished_at: int steps: int parallel_id: Optional[str] = None @@ -628,7 +628,7 @@ class AppBlockingResponse(BaseModel): task_id: str - def to_dict(self) -> dict: + def to_dict(self): return jsonable_encoder(self) diff --git a/api/core/app/features/annotation_reply/annotation_reply.py b/api/core/app/features/annotation_reply/annotation_reply.py index 77b6bb554c..83fd3debad 100644 --- a/api/core/app/features/annotation_reply/annotation_reply.py +++ b/api/core/app/features/annotation_reply/annotation_reply.py @@ -58,7 +58,7 @@ class AnnotationReplyFeature: query=query, top_k=1, score_threshold=score_threshold, filter={"group_id": [dataset.id]} ) - if documents: + if documents and documents[0].metadata: annotation_id = documents[0].metadata["annotation_id"] score = documents[0].metadata["score"] annotation = AppAnnotationService.get_annotation_by_id(annotation_id) diff --git a/api/core/app/features/rate_limiting/rate_limit.py b/api/core/app/features/rate_limiting/rate_limit.py index 8fe1d96b37..dcc2b4e55f 100644 --- a/api/core/app/features/rate_limiting/rate_limit.py +++ b/api/core/app/features/rate_limiting/rate_limit.py @@ -17,7 +17,7 @@ class RateLimit: _UNLIMITED_REQUEST_ID = "unlimited_request_id" _REQUEST_MAX_ALIVE_TIME = 10 * 60 # 10 minutes _ACTIVE_REQUESTS_COUNT_FLUSH_INTERVAL = 5 * 60 # recalculate request_count from request_detail every 5 minutes - _instance_dict = {} + _instance_dict: dict[str, "RateLimit"] = {} def __new__(cls: type["RateLimit"], client_id: str, max_active_requests: int): if client_id not in cls._instance_dict: diff --git a/api/core/app/task_pipeline/based_generate_task_pipeline.py b/api/core/app/task_pipeline/based_generate_task_pipeline.py index 51d610e2cb..03a81353d0 100644 --- a/api/core/app/task_pipeline/based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/based_generate_task_pipeline.py @@ -62,6 +62,7 @@ class BasedGenerateTaskPipeline: """ logger.debug("error: %s", event.error) e = event.error + err: Exception if isinstance(e, InvokeAuthorizationError): err = InvokeAuthorizationError("Incorrect API key provided") @@ -130,6 +131,7 @@ class BasedGenerateTaskPipeline: rule=ModerationRule(type=sensitive_word_avoidance.type, config=sensitive_word_avoidance.config), queue_manager=self._queue_manager, ) + return None def _handle_output_moderation_when_task_finished(self, completion: str) -> Optional[str]: """ diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index e26b60c4d3..b9f8e7ca56 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -2,6 +2,7 @@ import json import logging import time from collections.abc import Generator +from threading import Thread from typing import Optional, Union, cast from constants.tts_auto_play_timeout import TTS_AUTO_PLAY_TIMEOUT, TTS_AUTO_PLAY_YIELD_CPU_TIME @@ -103,7 +104,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan ) ) - self._conversation_name_generate_thread = None + self._conversation_name_generate_thread: Optional[Thread] = None def process( self, @@ -123,7 +124,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan if self._application_generate_entity.app_config.app_mode != AppMode.COMPLETION: # start generate conversation name thread self._conversation_name_generate_thread = self._generate_conversation_name( - self._conversation, self._application_generate_entity.query + self._conversation, self._application_generate_entity.query or "" ) generator = self._wrapper_process_stream_response(trace_manager=self._application_generate_entity.trace_manager) @@ -146,7 +147,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan extras = {"usage": jsonable_encoder(self._task_state.llm_result.usage)} if self._task_state.metadata: extras["metadata"] = self._task_state.metadata - + response: Union[ChatbotAppBlockingResponse, CompletionAppBlockingResponse] if self._conversation.mode == AppMode.COMPLETION.value: response = CompletionAppBlockingResponse( task_id=self._application_generate_entity.task_id, @@ -154,7 +155,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan id=self._message.id, mode=self._conversation.mode, message_id=self._message.id, - answer=self._task_state.llm_result.message.content, + answer=cast(str, self._task_state.llm_result.message.content), created_at=int(self._message.created_at.timestamp()), **extras, ), @@ -167,7 +168,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan mode=self._conversation.mode, conversation_id=self._conversation.id, message_id=self._message.id, - answer=self._task_state.llm_result.message.content, + answer=cast(str, self._task_state.llm_result.message.content), created_at=int(self._message.created_at.timestamp()), **extras, ), @@ -252,7 +253,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan yield MessageAudioEndStreamResponse(audio="", task_id=task_id) def _process_stream_response( - self, publisher: AppGeneratorTTSPublisher, trace_manager: Optional[TraceQueueManager] = None + self, publisher: Optional[AppGeneratorTTSPublisher], trace_manager: Optional[TraceQueueManager] = None ) -> Generator[StreamResponse, None, None]: """ Process stream response. @@ -269,13 +270,14 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan break elif isinstance(event, QueueStopEvent | QueueMessageEndEvent): if isinstance(event, QueueMessageEndEvent): - self._task_state.llm_result = event.llm_result + if event.llm_result: + self._task_state.llm_result = event.llm_result else: self._handle_stop(event) # handle output moderation output_moderation_answer = self._handle_output_moderation_when_task_finished( - self._task_state.llm_result.message.content + cast(str, self._task_state.llm_result.message.content) ) if output_moderation_answer: self._task_state.llm_result.message.content = output_moderation_answer @@ -292,7 +294,9 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan if annotation: self._task_state.llm_result.message.content = annotation.content elif isinstance(event, QueueAgentThoughtEvent): - yield self._agent_thought_to_stream_response(event) + agent_thought_response = self._agent_thought_to_stream_response(event) + if agent_thought_response is not None: + yield agent_thought_response elif isinstance(event, QueueMessageFileEvent): response = self._message_file_to_stream_response(event) if response: @@ -307,16 +311,18 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan self._task_state.llm_result.prompt_messages = chunk.prompt_messages # handle output moderation chunk - should_direct_answer = self._handle_output_moderation_chunk(delta_text) + should_direct_answer = self._handle_output_moderation_chunk(cast(str, delta_text)) if should_direct_answer: continue - self._task_state.llm_result.message.content += delta_text + current_content = cast(str, self._task_state.llm_result.message.content) + current_content += cast(str, delta_text) + self._task_state.llm_result.message.content = current_content if isinstance(event, QueueLLMChunkEvent): - yield self._message_to_stream_response(delta_text, self._message.id) + yield self._message_to_stream_response(cast(str, delta_text), self._message.id) else: - yield self._agent_message_to_stream_response(delta_text, self._message.id) + yield self._agent_message_to_stream_response(cast(str, delta_text), self._message.id) elif isinstance(event, QueueMessageReplaceEvent): yield self._message_replace_to_stream_response(answer=event.text) elif isinstance(event, QueuePingEvent): @@ -336,8 +342,14 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan llm_result = self._task_state.llm_result usage = llm_result.usage - self._message = db.session.query(Message).filter(Message.id == self._message.id).first() - self._conversation = db.session.query(Conversation).filter(Conversation.id == self._conversation.id).first() + message = db.session.query(Message).filter(Message.id == self._message.id).first() + if not message: + raise Exception(f"Message {self._message.id} not found") + self._message = message + conversation = db.session.query(Conversation).filter(Conversation.id == self._conversation.id).first() + if not conversation: + raise Exception(f"Conversation {self._conversation.id} not found") + self._conversation = conversation self._message.message = PromptMessageUtil.prompt_messages_to_prompt_for_saving( self._model_config.mode, self._task_state.llm_result.prompt_messages @@ -346,7 +358,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan self._message.message_unit_price = usage.prompt_unit_price self._message.message_price_unit = usage.prompt_price_unit self._message.answer = ( - PromptTemplateParser.remove_template_variables(llm_result.message.content.strip()) + PromptTemplateParser.remove_template_variables(cast(str, llm_result.message.content).strip()) if llm_result.message.content else "" ) @@ -374,6 +386,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan application_generate_entity=self._application_generate_entity, conversation=self._conversation, is_first_message=self._application_generate_entity.app_config.app_mode in {AppMode.AGENT_CHAT, AppMode.CHAT} + and hasattr(self._application_generate_entity, "conversation_id") and self._application_generate_entity.conversation_id is None, extras=self._application_generate_entity.extras, ) @@ -420,7 +433,9 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan extras["metadata"] = self._task_state.metadata return MessageEndStreamResponse( - task_id=self._application_generate_entity.task_id, id=self._message.id, **extras + task_id=self._application_generate_entity.task_id, + id=self._message.id, + metadata=extras.get("metadata", {}), ) def _agent_message_to_stream_response(self, answer: str, message_id: str) -> AgentMessageStreamResponse: @@ -440,7 +455,7 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline, MessageCycleMan :param event: agent thought event :return: """ - agent_thought: MessageAgentThought = ( + agent_thought: Optional[MessageAgentThought] = ( db.session.query(MessageAgentThought).filter(MessageAgentThought.id == event.agent_thought_id).first() ) db.session.refresh(agent_thought) diff --git a/api/core/app/task_pipeline/message_cycle_manage.py b/api/core/app/task_pipeline/message_cycle_manage.py index e818a090ed..007543f6d0 100644 --- a/api/core/app/task_pipeline/message_cycle_manage.py +++ b/api/core/app/task_pipeline/message_cycle_manage.py @@ -128,7 +128,7 @@ class MessageCycleManage: """ message_file = db.session.query(MessageFile).filter(MessageFile.id == event.message_file_id).first() - if message_file: + if message_file and message_file.url is not None: # get tool file id tool_file_id = message_file.url.split("/")[-1] # trim extension diff --git a/api/core/app/task_pipeline/workflow_cycle_manage.py b/api/core/app/task_pipeline/workflow_cycle_manage.py index df7dbace0e..f581e564f2 100644 --- a/api/core/app/task_pipeline/workflow_cycle_manage.py +++ b/api/core/app/task_pipeline/workflow_cycle_manage.py @@ -93,7 +93,7 @@ class WorkflowCycleManage: ) # handle special values - inputs = WorkflowEntry.handle_special_values(inputs) + inputs = dict(WorkflowEntry.handle_special_values(inputs) or {}) # init workflow run with Session(db.engine, expire_on_commit=False) as session: @@ -192,7 +192,7 @@ class WorkflowCycleManage: """ workflow_run = self._refetch_workflow_run(workflow_run.id) - outputs = WorkflowEntry.handle_special_values(outputs) + outputs = WorkflowEntry.handle_special_values(dict(outputs) if outputs else None) workflow_run.status = WorkflowRunStatus.PARTIAL_SUCCESSED.value workflow_run.outputs = json.dumps(outputs or {}) @@ -500,7 +500,7 @@ class WorkflowCycleManage: id=workflow_run.id, workflow_id=workflow_run.workflow_id, sequence_number=workflow_run.sequence_number, - inputs=workflow_run.inputs_dict, + inputs=dict(workflow_run.inputs_dict or {}), created_at=int(workflow_run.created_at.timestamp()), ), ) @@ -545,7 +545,7 @@ class WorkflowCycleManage: workflow_id=workflow_run.workflow_id, sequence_number=workflow_run.sequence_number, status=workflow_run.status, - outputs=workflow_run.outputs_dict, + outputs=dict(workflow_run.outputs_dict) if workflow_run.outputs_dict else None, error=workflow_run.error, elapsed_time=workflow_run.elapsed_time, total_tokens=workflow_run.total_tokens, @@ -553,7 +553,7 @@ class WorkflowCycleManage: created_by=created_by, created_at=int(workflow_run.created_at.timestamp()), finished_at=int(workflow_run.finished_at.timestamp()), - files=self._fetch_files_from_node_outputs(workflow_run.outputs_dict), + files=self._fetch_files_from_node_outputs(dict(workflow_run.outputs_dict)), exceptions_count=workflow_run.exceptions_count, ), ) @@ -655,7 +655,7 @@ class WorkflowCycleManage: event: QueueNodeRetryEvent, task_id: str, workflow_node_execution: WorkflowNodeExecution, - ) -> Optional[NodeFinishStreamResponse]: + ) -> Optional[Union[NodeRetryStreamResponse, NodeFinishStreamResponse]]: """ Workflow node finish to stream response. :param event: queue node succeeded or failed event @@ -838,7 +838,7 @@ class WorkflowCycleManage: ), ) - def _fetch_files_from_node_outputs(self, outputs_dict: dict) -> Sequence[Mapping[str, Any]]: + def _fetch_files_from_node_outputs(self, outputs_dict: Mapping[str, Any]) -> Sequence[Mapping[str, Any]]: """ Fetch files from node outputs :param outputs_dict: node outputs dict @@ -851,9 +851,11 @@ class WorkflowCycleManage: # Remove None files = [file for file in files if file] # Flatten list - files = [file for sublist in files for file in sublist] + # Flatten the list of sequences into a single list of mappings + flattened_files = [file for sublist in files if sublist for file in sublist] - return files + # Convert to tuple to match Sequence type + return tuple(flattened_files) def _fetch_files_from_variable_value(self, value: Union[dict, list]) -> Sequence[Mapping[str, Any]]: """ @@ -891,6 +893,8 @@ class WorkflowCycleManage: elif isinstance(value, File): return value.to_dict() + return None + def _refetch_workflow_run(self, workflow_run_id: str) -> WorkflowRun: """ Refetch workflow run diff --git a/api/core/callback_handler/agent_tool_callback_handler.py b/api/core/callback_handler/agent_tool_callback_handler.py index d826edf6a0..effc7eff91 100644 --- a/api/core/callback_handler/agent_tool_callback_handler.py +++ b/api/core/callback_handler/agent_tool_callback_handler.py @@ -57,7 +57,7 @@ class DifyAgentCallbackHandler(BaseModel): self, tool_name: str, tool_inputs: Mapping[str, Any], - tool_outputs: Sequence[ToolInvokeMessage], + tool_outputs: Sequence[ToolInvokeMessage] | str, message_id: Optional[str] = None, timer: Optional[Any] = None, trace_manager: Optional[TraceQueueManager] = None, diff --git a/api/core/callback_handler/index_tool_callback_handler.py b/api/core/callback_handler/index_tool_callback_handler.py index 1481578630..8f8aaa93d6 100644 --- a/api/core/callback_handler/index_tool_callback_handler.py +++ b/api/core/callback_handler/index_tool_callback_handler.py @@ -40,17 +40,18 @@ class DatasetIndexToolCallbackHandler: def on_tool_end(self, documents: list[Document]) -> None: """Handle tool end.""" for document in documents: - query = db.session.query(DocumentSegment).filter( - DocumentSegment.index_node_id == document.metadata["doc_id"] - ) + if document.metadata is not None: + query = db.session.query(DocumentSegment).filter( + DocumentSegment.index_node_id == document.metadata["doc_id"] + ) - if "dataset_id" in document.metadata: - query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) + if "dataset_id" in document.metadata: + query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) - # add hit count to document segment - query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) + # add hit count to document segment + query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) - db.session.commit() + db.session.commit() def return_retriever_resource_info(self, resource: list): """Handle return_retriever_resource_info.""" diff --git a/api/core/entities/model_entities.py b/api/core/entities/model_entities.py index 9ed5528e43..5017835565 100644 --- a/api/core/entities/model_entities.py +++ b/api/core/entities/model_entities.py @@ -1,3 +1,4 @@ +from collections.abc import Sequence from enum import Enum from typing import Optional @@ -72,7 +73,7 @@ class DefaultModelProviderEntity(BaseModel): label: I18nObject icon_small: Optional[I18nObject] = None icon_large: Optional[I18nObject] = None - supported_model_types: list[ModelType] + supported_model_types: Sequence[ModelType] = [] class DefaultModelEntity(BaseModel): diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index d1b34db2fe..2e27b362d3 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -40,7 +40,7 @@ from models.provider import ( logger = logging.getLogger(__name__) -original_provider_configurate_methods = {} +original_provider_configurate_methods: dict[str, list[ConfigurateMethod]] = {} class ProviderConfiguration(BaseModel): @@ -99,7 +99,8 @@ class ProviderConfiguration(BaseModel): continue restrict_models = quota_configuration.restrict_models - + if self.system_configuration.credentials is None: + return None copy_credentials = self.system_configuration.credentials.copy() if restrict_models: for restrict_model in restrict_models: @@ -124,7 +125,7 @@ class ProviderConfiguration(BaseModel): return credentials - def get_system_configuration_status(self) -> SystemConfigurationStatus: + def get_system_configuration_status(self) -> Optional[SystemConfigurationStatus]: """ Get system configuration status. :return: @@ -136,6 +137,8 @@ class ProviderConfiguration(BaseModel): current_quota_configuration = next( (q for q in self.system_configuration.quota_configurations if q.quota_type == current_quota_type), None ) + if current_quota_configuration is None: + return None return ( SystemConfigurationStatus.ACTIVE @@ -150,7 +153,7 @@ class ProviderConfiguration(BaseModel): """ return self.custom_configuration.provider is not None or len(self.custom_configuration.models) > 0 - def get_custom_credentials(self, obfuscated: bool = False) -> Optional[dict]: + def get_custom_credentials(self, obfuscated: bool = False): """ Get custom credentials. @@ -172,7 +175,7 @@ class ProviderConfiguration(BaseModel): else [], ) - def custom_credentials_validate(self, credentials: dict) -> tuple[Provider, dict]: + def custom_credentials_validate(self, credentials: dict) -> tuple[Optional[Provider], dict]: """ Validate custom credentials. :param credentials: provider credentials @@ -324,7 +327,7 @@ class ProviderConfiguration(BaseModel): def custom_model_credentials_validate( self, model_type: ModelType, model: str, credentials: dict - ) -> tuple[ProviderModel, dict]: + ) -> tuple[Optional[ProviderModel], dict]: """ Validate custom model credentials. @@ -740,10 +743,10 @@ class ProviderConfiguration(BaseModel): if model_type: model_types.append(model_type) else: - model_types = provider_instance.get_provider_schema().supported_model_types + model_types = list(provider_instance.get_provider_schema().supported_model_types) # Group model settings by model type and model - model_setting_map = defaultdict(dict) + model_setting_map: defaultdict[ModelType, dict[str, ModelSettings]] = defaultdict(dict) for model_setting in self.model_settings: model_setting_map[model_setting.model_type][model_setting.model] = model_setting @@ -822,54 +825,57 @@ class ProviderConfiguration(BaseModel): ]: # only customizable model for restrict_model in restrict_models: - copy_credentials = self.system_configuration.credentials.copy() - if restrict_model.base_model_name: - copy_credentials["base_model_name"] = restrict_model.base_model_name + if self.system_configuration.credentials is not None: + copy_credentials = self.system_configuration.credentials.copy() + if restrict_model.base_model_name: + copy_credentials["base_model_name"] = restrict_model.base_model_name - try: - custom_model_schema = provider_instance.get_model_instance( - restrict_model.model_type - ).get_customizable_model_schema_from_credentials(restrict_model.model, copy_credentials) - except Exception as ex: - logger.warning(f"get custom model schema failed, {ex}") - continue + try: + custom_model_schema = provider_instance.get_model_instance( + restrict_model.model_type + ).get_customizable_model_schema_from_credentials(restrict_model.model, copy_credentials) + except Exception as ex: + logger.warning(f"get custom model schema failed, {ex}") + continue - if not custom_model_schema: - continue + if not custom_model_schema: + continue - if custom_model_schema.model_type not in model_types: - continue + if custom_model_schema.model_type not in model_types: + continue - status = ModelStatus.ACTIVE - if ( - custom_model_schema.model_type in model_setting_map - and custom_model_schema.model in model_setting_map[custom_model_schema.model_type] - ): - model_setting = model_setting_map[custom_model_schema.model_type][custom_model_schema.model] - if model_setting.enabled is False: - status = ModelStatus.DISABLED + status = ModelStatus.ACTIVE + if ( + custom_model_schema.model_type in model_setting_map + and custom_model_schema.model in model_setting_map[custom_model_schema.model_type] + ): + model_setting = model_setting_map[custom_model_schema.model_type][ + custom_model_schema.model + ] + if model_setting.enabled is False: + status = ModelStatus.DISABLED - provider_models.append( - ModelWithProviderEntity( - model=custom_model_schema.model, - label=custom_model_schema.label, - model_type=custom_model_schema.model_type, - features=custom_model_schema.features, - fetch_from=FetchFrom.PREDEFINED_MODEL, - model_properties=custom_model_schema.model_properties, - deprecated=custom_model_schema.deprecated, - provider=SimpleModelProviderEntity(self.provider), - status=status, + provider_models.append( + ModelWithProviderEntity( + model=custom_model_schema.model, + label=custom_model_schema.label, + model_type=custom_model_schema.model_type, + features=custom_model_schema.features, + fetch_from=FetchFrom.PREDEFINED_MODEL, + model_properties=custom_model_schema.model_properties, + deprecated=custom_model_schema.deprecated, + provider=SimpleModelProviderEntity(self.provider), + status=status, + ) ) - ) # if llm name not in restricted llm list, remove it restrict_model_names = [rm.model for rm in restrict_models] - for m in provider_models: - if m.model_type == ModelType.LLM and m.model not in restrict_model_names: - m.status = ModelStatus.NO_PERMISSION + for model in provider_models: + if model.model_type == ModelType.LLM and m.model not in restrict_model_names: + model.status = ModelStatus.NO_PERMISSION elif not quota_configuration.is_valid: - m.status = ModelStatus.QUOTA_EXCEEDED + model.status = ModelStatus.QUOTA_EXCEEDED return provider_models @@ -1043,7 +1049,7 @@ class ProviderConfigurations(BaseModel): return iter(self.configurations) def values(self) -> Iterator[ProviderConfiguration]: - return self.configurations.values() + return iter(self.configurations.values()) def get(self, key, default=None): return self.configurations.get(key, default) diff --git a/api/core/extension/api_based_extension_requestor.py b/api/core/extension/api_based_extension_requestor.py index 38cebb6b6b..3f4e20ec24 100644 --- a/api/core/extension/api_based_extension_requestor.py +++ b/api/core/extension/api_based_extension_requestor.py @@ -1,3 +1,5 @@ +from typing import cast + import requests from configs import dify_config @@ -5,7 +7,7 @@ from models.api_based_extension import APIBasedExtensionPoint class APIBasedExtensionRequestor: - timeout: (int, int) = (5, 60) + timeout: tuple[int, int] = (5, 60) """timeout for request connect and read""" def __init__(self, api_endpoint: str, api_key: str) -> None: @@ -51,4 +53,4 @@ class APIBasedExtensionRequestor: "request error, status_code: {}, content: {}".format(response.status_code, response.text[:100]) ) - return response.json() + return cast(dict, response.json()) diff --git a/api/core/extension/extensible.py b/api/core/extension/extensible.py index 97dbaf2026..231743bf2a 100644 --- a/api/core/extension/extensible.py +++ b/api/core/extension/extensible.py @@ -38,8 +38,8 @@ class Extensible: @classmethod def scan_extensions(cls): - extensions: list[ModuleExtension] = [] - position_map = {} + extensions = [] + position_map: dict[str, int] = {} # get the path of the current class current_path = os.path.abspath(cls.__module__.replace(".", os.path.sep) + ".py") @@ -58,7 +58,8 @@ class Extensible: # is builtin extension, builtin extension # in the front-end page and business logic, there are special treatments. builtin = False - position = None + # default position is 0 can not be None for sort_to_dict_by_position_map + position = 0 if "__builtin__" in file_names: builtin = True @@ -89,7 +90,7 @@ class Extensible: logging.warning(f"Missing subclass of {cls.__name__} in {py_path}, Skip.") continue - json_data = {} + json_data: dict[str, Any] = {} if not builtin: if "schema.json" not in file_names: logging.warning(f"Missing schema.json file in {subdir_path}, Skip.") diff --git a/api/core/extension/extension.py b/api/core/extension/extension.py index 3da170455e..9eb9e0306b 100644 --- a/api/core/extension/extension.py +++ b/api/core/extension/extension.py @@ -1,4 +1,6 @@ -from core.extension.extensible import ExtensionModule, ModuleExtension +from typing import cast + +from core.extension.extensible import Extensible, ExtensionModule, ModuleExtension from core.external_data_tool.base import ExternalDataTool from core.moderation.base import Moderation @@ -10,7 +12,8 @@ class Extension: def init(self): for module, module_class in self.module_classes.items(): - self.__module_extensions[module.value] = module_class.scan_extensions() + m = cast(Extensible, module_class) + self.__module_extensions[module.value] = m.scan_extensions() def module_extensions(self, module: str) -> list[ModuleExtension]: module_extensions = self.__module_extensions.get(module) @@ -35,7 +38,8 @@ class Extension: def extension_class(self, module: ExtensionModule, extension_name: str) -> type: module_extension = self.module_extension(module, extension_name) - return module_extension.extension_class + t: type = module_extension.extension_class + return t def validate_form_schema(self, module: ExtensionModule, extension_name: str, config: dict) -> None: module_extension = self.module_extension(module, extension_name) diff --git a/api/core/external_data_tool/api/api.py b/api/core/external_data_tool/api/api.py index 54ec97a493..9989c8a090 100644 --- a/api/core/external_data_tool/api/api.py +++ b/api/core/external_data_tool/api/api.py @@ -48,7 +48,10 @@ class ApiExternalDataTool(ExternalDataTool): :return: the tool query result """ # get params from config + if not self.config: + raise ValueError("config is required, config: {}".format(self.config)) api_based_extension_id = self.config.get("api_based_extension_id") + assert api_based_extension_id is not None, "api_based_extension_id is required" # get api_based_extension api_based_extension = ( diff --git a/api/core/external_data_tool/external_data_fetch.py b/api/core/external_data_tool/external_data_fetch.py index 84b94e117f..6a9703a569 100644 --- a/api/core/external_data_tool/external_data_fetch.py +++ b/api/core/external_data_tool/external_data_fetch.py @@ -1,7 +1,7 @@ -import concurrent import logging -from concurrent.futures import ThreadPoolExecutor -from typing import Optional +from collections.abc import Mapping +from concurrent.futures import Future, ThreadPoolExecutor, as_completed +from typing import Any, Optional from flask import Flask, current_app @@ -17,9 +17,9 @@ class ExternalDataFetch: tenant_id: str, app_id: str, external_data_tools: list[ExternalDataVariableEntity], - inputs: dict, + inputs: Mapping[str, Any], query: str, - ) -> dict: + ) -> Mapping[str, Any]: """ Fill in variable inputs from external data tools if exists. @@ -30,13 +30,14 @@ class ExternalDataFetch: :param query: the query :return: the filled inputs """ - results = {} + results: dict[str, Any] = {} + inputs = dict(inputs) with ThreadPoolExecutor() as executor: futures = {} for tool in external_data_tools: - future = executor.submit( + future: Future[tuple[str | None, str | None]] = executor.submit( self._query_external_data_tool, - current_app._get_current_object(), + current_app._get_current_object(), # type: ignore tenant_id, app_id, tool, @@ -46,9 +47,10 @@ class ExternalDataFetch: futures[future] = tool - for future in concurrent.futures.as_completed(futures): + for future in as_completed(futures): tool_variable, result = future.result() - results[tool_variable] = result + if tool_variable is not None: + results[tool_variable] = result inputs.update(results) return inputs @@ -59,7 +61,7 @@ class ExternalDataFetch: tenant_id: str, app_id: str, external_data_tool: ExternalDataVariableEntity, - inputs: dict, + inputs: Mapping[str, Any], query: str, ) -> tuple[Optional[str], Optional[str]]: """ diff --git a/api/core/external_data_tool/factory.py b/api/core/external_data_tool/factory.py index 2872109859..245507e17c 100644 --- a/api/core/external_data_tool/factory.py +++ b/api/core/external_data_tool/factory.py @@ -1,4 +1,5 @@ -from typing import Optional +from collections.abc import Mapping +from typing import Any, Optional, cast from core.extension.extensible import ExtensionModule from extensions.ext_code_based_extension import code_based_extension @@ -23,9 +24,10 @@ class ExternalDataToolFactory: """ code_based_extension.validate_form_schema(ExtensionModule.EXTERNAL_DATA_TOOL, name, config) extension_class = code_based_extension.extension_class(ExtensionModule.EXTERNAL_DATA_TOOL, name) - extension_class.validate_config(tenant_id, config) + # FIXME mypy issue here, figure out how to fix it + extension_class.validate_config(tenant_id, config) # type: ignore - def query(self, inputs: dict, query: Optional[str] = None) -> str: + def query(self, inputs: Mapping[str, Any], query: Optional[str] = None) -> str: """ Query the external data tool. @@ -33,4 +35,4 @@ class ExternalDataToolFactory: :param query: the query of chat app :return: the tool query result """ - return self.__extension_instance.query(inputs, query) + return cast(str, self.__extension_instance.query(inputs, query)) diff --git a/api/core/file/file_manager.py b/api/core/file/file_manager.py index 15eb351a7e..4a50fb85c9 100644 --- a/api/core/file/file_manager.py +++ b/api/core/file/file_manager.py @@ -1,4 +1,5 @@ import base64 +from collections.abc import Mapping from configs import dify_config from core.helper import ssrf_proxy @@ -55,7 +56,7 @@ def to_prompt_message_content( if f.type == FileType.IMAGE: params["detail"] = image_detail_config or ImagePromptMessageContent.DETAIL.LOW - prompt_class_map = { + prompt_class_map: Mapping[FileType, type[MultiModalPromptMessageContent]] = { FileType.IMAGE: ImagePromptMessageContent, FileType.AUDIO: AudioPromptMessageContent, FileType.VIDEO: VideoPromptMessageContent, @@ -63,7 +64,7 @@ def to_prompt_message_content( } try: - return prompt_class_map[f.type](**params) + return prompt_class_map[f.type].model_validate(params) except KeyError: raise ValueError(f"file type {f.type} is not supported") diff --git a/api/core/file/tool_file_parser.py b/api/core/file/tool_file_parser.py index a17b7be367..6fa101cf36 100644 --- a/api/core/file/tool_file_parser.py +++ b/api/core/file/tool_file_parser.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, cast if TYPE_CHECKING: from core.tools.tool_file_manager import ToolFileManager @@ -9,4 +9,4 @@ tool_file_manager: dict[str, Any] = {"manager": None} class ToolFileParser: @staticmethod def get_tool_file_manager() -> "ToolFileManager": - return tool_file_manager["manager"] + return cast("ToolFileManager", tool_file_manager["manager"]) diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 584e3e9698..15b501780e 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -38,7 +38,7 @@ class CodeLanguage(StrEnum): class CodeExecutor: - dependencies_cache = {} + dependencies_cache: dict[str, str] = {} dependencies_cache_lock = Lock() code_template_transformers: dict[CodeLanguage, type[TemplateTransformer]] = { @@ -103,19 +103,19 @@ class CodeExecutor: ) try: - response = response.json() + response_data = response.json() except: raise CodeExecutionError("Failed to parse response") - if (code := response.get("code")) != 0: - raise CodeExecutionError(f"Got error code: {code}. Got error msg: {response.get('message')}") + if (code := response_data.get("code")) != 0: + raise CodeExecutionError(f"Got error code: {code}. Got error msg: {response_data.get('message')}") - response = CodeExecutionResponse(**response) + response_code = CodeExecutionResponse(**response_data) - if response.data.error: - raise CodeExecutionError(response.data.error) + if response_code.data.error: + raise CodeExecutionError(response_code.data.error) - return response.data.stdout or "" + return response_code.data.stdout or "" @classmethod def execute_workflow_code_template(cls, language: CodeLanguage, code: str, inputs: Mapping[str, Any]): diff --git a/api/core/helper/code_executor/jinja2/jinja2_formatter.py b/api/core/helper/code_executor/jinja2/jinja2_formatter.py index db2eb5ebb6..264947b568 100644 --- a/api/core/helper/code_executor/jinja2/jinja2_formatter.py +++ b/api/core/helper/code_executor/jinja2/jinja2_formatter.py @@ -1,9 +1,11 @@ +from collections.abc import Mapping + from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage class Jinja2Formatter: @classmethod - def format(cls, template: str, inputs: dict) -> str: + def format(cls, template: str, inputs: Mapping[str, str]) -> str: """ Format template :param template: template @@ -11,5 +13,4 @@ class Jinja2Formatter: :return: """ result = CodeExecutor.execute_workflow_code_template(language=CodeLanguage.JINJA2, code=template, inputs=inputs) - - return result["result"] + return str(result.get("result", "")) diff --git a/api/core/helper/code_executor/template_transformer.py b/api/core/helper/code_executor/template_transformer.py index 605719747a..baa792b5bc 100644 --- a/api/core/helper/code_executor/template_transformer.py +++ b/api/core/helper/code_executor/template_transformer.py @@ -29,8 +29,7 @@ class TemplateTransformer(ABC): result = re.search(rf"{cls._result_tag}(.*){cls._result_tag}", response, re.DOTALL) if not result: raise ValueError("Failed to parse result") - result = result.group(1) - return result + return result.group(1) @classmethod def transform_response(cls, response: str) -> Mapping[str, Any]: diff --git a/api/core/helper/lru_cache.py b/api/core/helper/lru_cache.py index 518962c165..81501d2e4e 100644 --- a/api/core/helper/lru_cache.py +++ b/api/core/helper/lru_cache.py @@ -4,7 +4,7 @@ from typing import Any class LRUCache: def __init__(self, capacity: int): - self.cache = OrderedDict() + self.cache: OrderedDict[Any, Any] = OrderedDict() self.capacity = capacity def get(self, key: Any) -> Any: diff --git a/api/core/helper/model_provider_cache.py b/api/core/helper/model_provider_cache.py index 5e274f8916..35349210bd 100644 --- a/api/core/helper/model_provider_cache.py +++ b/api/core/helper/model_provider_cache.py @@ -30,7 +30,7 @@ class ProviderCredentialsCache: except JSONDecodeError: return None - return cached_provider_credentials + return dict(cached_provider_credentials) else: return None diff --git a/api/core/helper/moderation.py b/api/core/helper/moderation.py index da0fd0031c..543444463b 100644 --- a/api/core/helper/moderation.py +++ b/api/core/helper/moderation.py @@ -22,6 +22,7 @@ def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str) provider_name = model_config.provider if using_provider_type == ProviderType.SYSTEM and provider_name in moderation_config.providers: hosting_openai_config = hosting_configuration.provider_map["openai"] + assert hosting_openai_config is not None # 2000 text per chunk length = 2000 @@ -34,8 +35,9 @@ def check_moderation(model_config: ModelConfigWithCredentialsEntity, text: str) try: model_type_instance = OpenAIModerationModel() + # FIXME, for type hint using assert or raise ValueError is better here? moderation_result = model_type_instance.invoke( - model="text-moderation-stable", credentials=hosting_openai_config.credentials, text=text_chunk + model="text-moderation-stable", credentials=hosting_openai_config.credentials or {}, text=text_chunk ) if moderation_result is True: diff --git a/api/core/helper/module_import_helper.py b/api/core/helper/module_import_helper.py index 1e2fefce88..9a041667e4 100644 --- a/api/core/helper/module_import_helper.py +++ b/api/core/helper/module_import_helper.py @@ -14,12 +14,13 @@ def import_module_from_source(*, module_name: str, py_file_path: AnyStr, use_laz if existed_spec: spec = existed_spec if not spec.loader: - raise Exception(f"Failed to load module {module_name} from {py_file_path}") + raise Exception(f"Failed to load module {module_name} from {py_file_path!r}") else: # Refer to: https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly - spec = importlib.util.spec_from_file_location(module_name, py_file_path) + # FIXME: mypy does not support the type of spec.loader + spec = importlib.util.spec_from_file_location(module_name, py_file_path) # type: ignore if not spec or not spec.loader: - raise Exception(f"Failed to load module {module_name} from {py_file_path}") + raise Exception(f"Failed to load module {module_name} from {py_file_path!r}") if use_lazy_loader: # Refer to: https://docs.python.org/3/library/importlib.html#implementing-lazy-imports spec.loader = importlib.util.LazyLoader(spec.loader) @@ -29,7 +30,7 @@ def import_module_from_source(*, module_name: str, py_file_path: AnyStr, use_laz spec.loader.exec_module(module) return module except Exception as e: - logging.exception(f"Failed to load module {module_name} from script file '{py_file_path}'") + logging.exception(f"Failed to load module {module_name} from script file '{py_file_path!r}'") raise e @@ -57,6 +58,6 @@ def load_single_subclass_from_source( case 1: return subclasses[0] case 0: - raise Exception(f"Missing subclass of {parent_type.__name__} in {script_path}") + raise Exception(f"Missing subclass of {parent_type.__name__} in {script_path!r}") case _: - raise Exception(f"Multiple subclasses of {parent_type.__name__} in {script_path}") + raise Exception(f"Multiple subclasses of {parent_type.__name__} in {script_path!r}") diff --git a/api/core/helper/tool_parameter_cache.py b/api/core/helper/tool_parameter_cache.py index e848b46c56..3b67b3f848 100644 --- a/api/core/helper/tool_parameter_cache.py +++ b/api/core/helper/tool_parameter_cache.py @@ -33,7 +33,7 @@ class ToolParameterCache: except JSONDecodeError: return None - return cached_tool_parameter + return dict(cached_tool_parameter) else: return None diff --git a/api/core/helper/tool_provider_cache.py b/api/core/helper/tool_provider_cache.py index 94b02cf985..6de5e704ab 100644 --- a/api/core/helper/tool_provider_cache.py +++ b/api/core/helper/tool_provider_cache.py @@ -28,7 +28,7 @@ class ToolProviderCredentialsCache: except JSONDecodeError: return None - return cached_provider_credentials + return dict(cached_provider_credentials) else: return None diff --git a/api/core/hosting_configuration.py b/api/core/hosting_configuration.py index b47ba67f2f..f9fb7275f3 100644 --- a/api/core/hosting_configuration.py +++ b/api/core/hosting_configuration.py @@ -42,7 +42,7 @@ class HostedModerationConfig(BaseModel): class HostingConfiguration: provider_map: dict[str, HostingProvider] = {} - moderation_config: HostedModerationConfig = None + moderation_config: Optional[HostedModerationConfig] = None def init_app(self, app: Flask) -> None: if dify_config.EDITION != "CLOUD": @@ -67,7 +67,7 @@ class HostingConfiguration: "base_model_name": "gpt-35-turbo", } - quotas = [] + quotas: list[HostingQuota] = [] hosted_quota_limit = dify_config.HOSTED_AZURE_OPENAI_QUOTA_LIMIT trial_quota = TrialHostingQuota( quota_limit=hosted_quota_limit, @@ -123,7 +123,7 @@ class HostingConfiguration: def init_openai(self) -> HostingProvider: quota_unit = QuotaUnit.CREDITS - quotas = [] + quotas: list[HostingQuota] = [] if dify_config.HOSTED_OPENAI_TRIAL_ENABLED: hosted_quota_limit = dify_config.HOSTED_OPENAI_QUOTA_LIMIT @@ -157,7 +157,7 @@ class HostingConfiguration: @staticmethod def init_anthropic() -> HostingProvider: quota_unit = QuotaUnit.TOKENS - quotas = [] + quotas: list[HostingQuota] = [] if dify_config.HOSTED_ANTHROPIC_TRIAL_ENABLED: hosted_quota_limit = dify_config.HOSTED_ANTHROPIC_QUOTA_LIMIT @@ -187,7 +187,7 @@ class HostingConfiguration: def init_minimax() -> HostingProvider: quota_unit = QuotaUnit.TOKENS if dify_config.HOSTED_MINIMAX_ENABLED: - quotas = [FreeHostingQuota()] + quotas: list[HostingQuota] = [FreeHostingQuota()] return HostingProvider( enabled=True, @@ -205,7 +205,7 @@ class HostingConfiguration: def init_spark() -> HostingProvider: quota_unit = QuotaUnit.TOKENS if dify_config.HOSTED_SPARK_ENABLED: - quotas = [FreeHostingQuota()] + quotas: list[HostingQuota] = [FreeHostingQuota()] return HostingProvider( enabled=True, @@ -223,7 +223,7 @@ class HostingConfiguration: def init_zhipuai() -> HostingProvider: quota_unit = QuotaUnit.TOKENS if dify_config.HOSTED_ZHIPUAI_ENABLED: - quotas = [FreeHostingQuota()] + quotas: list[HostingQuota] = [FreeHostingQuota()] return HostingProvider( enabled=True, diff --git a/api/core/indexing_runner.py b/api/core/indexing_runner.py index 29e161cb74..1f0a0d0ef1 100644 --- a/api/core/indexing_runner.py +++ b/api/core/indexing_runner.py @@ -6,10 +6,10 @@ import re import threading import time import uuid -from typing import Optional, cast +from typing import Any, Optional, cast from flask import Flask, current_app -from flask_login import current_user +from flask_login import current_user # type: ignore from sqlalchemy.orm.exc import ObjectDeletedError from configs import dify_config @@ -62,6 +62,8 @@ class IndexingRunner: .filter(DatasetProcessRule.id == dataset_document.dataset_process_rule_id) .first() ) + if not processing_rule: + raise ValueError("no process rule found") index_type = dataset_document.doc_form index_processor = IndexProcessorFactory(index_type).init_index_processor() # extract @@ -120,6 +122,8 @@ class IndexingRunner: .filter(DatasetProcessRule.id == dataset_document.dataset_process_rule_id) .first() ) + if not processing_rule: + raise ValueError("no process rule found") index_type = dataset_document.doc_form index_processor = IndexProcessorFactory(index_type).init_index_processor() @@ -254,7 +258,7 @@ class IndexingRunner: tenant_id=tenant_id, model_type=ModelType.TEXT_EMBEDDING, ) - preview_texts = [] + preview_texts: list[str] = [] total_segments = 0 index_type = doc_form index_processor = IndexProcessorFactory(index_type).init_index_processor() @@ -285,7 +289,8 @@ class IndexingRunner: for upload_file_id in image_upload_file_ids: image_file = db.session.query(UploadFile).filter(UploadFile.id == upload_file_id).first() try: - storage.delete(image_file.key) + if image_file: + storage.delete(image_file.key) except Exception: logging.exception( "Delete image_files failed while indexing_estimate, \ @@ -379,8 +384,9 @@ class IndexingRunner: # replace doc id to document model id text_docs = cast(list[Document], text_docs) for text_doc in text_docs: - text_doc.metadata["document_id"] = dataset_document.id - text_doc.metadata["dataset_id"] = dataset_document.dataset_id + if text_doc.metadata is not None: + text_doc.metadata["document_id"] = dataset_document.id + text_doc.metadata["dataset_id"] = dataset_document.dataset_id return text_docs @@ -400,6 +406,7 @@ class IndexingRunner: """ Get the NodeParser object according to the processing rule. """ + character_splitter: TextSplitter if processing_rule.mode == "custom": # The user-defined segmentation rule rules = json.loads(processing_rule.rules) @@ -426,9 +433,10 @@ class IndexingRunner: ) else: # Automatic segmentation + automatic_rules: dict[str, Any] = dict(DatasetProcessRule.AUTOMATIC_RULES["segmentation"]) character_splitter = EnhanceRecursiveCharacterTextSplitter.from_encoder( - chunk_size=DatasetProcessRule.AUTOMATIC_RULES["segmentation"]["max_tokens"], - chunk_overlap=DatasetProcessRule.AUTOMATIC_RULES["segmentation"]["chunk_overlap"], + chunk_size=automatic_rules["max_tokens"], + chunk_overlap=automatic_rules["chunk_overlap"], separators=["\n\n", "。", ". ", " ", ""], embedding_model_instance=embedding_model_instance, ) @@ -497,8 +505,8 @@ class IndexingRunner: """ Split the text documents into nodes. """ - all_documents = [] - all_qa_documents = [] + all_documents: list[Document] = [] + all_qa_documents: list[Document] = [] for text_doc in text_docs: # document clean document_text = self._document_clean(text_doc.page_content, processing_rule) @@ -509,10 +517,11 @@ class IndexingRunner: split_documents = [] for document_node in documents: if document_node.page_content.strip(): - doc_id = str(uuid.uuid4()) - hash = helper.generate_text_hash(document_node.page_content) - document_node.metadata["doc_id"] = doc_id - document_node.metadata["doc_hash"] = hash + if document_node.metadata is not None: + doc_id = str(uuid.uuid4()) + hash = helper.generate_text_hash(document_node.page_content) + document_node.metadata["doc_id"] = doc_id + document_node.metadata["doc_hash"] = hash # delete Splitter character page_content = document_node.page_content document_node.page_content = remove_leading_symbols(page_content) @@ -529,7 +538,7 @@ class IndexingRunner: document_format_thread = threading.Thread( target=self.format_qa_document, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "tenant_id": tenant_id, "document_node": doc, "all_qa_documents": all_qa_documents, @@ -557,11 +566,12 @@ class IndexingRunner: qa_document = Document( page_content=result["question"], metadata=document_node.metadata.model_copy() ) - doc_id = str(uuid.uuid4()) - hash = helper.generate_text_hash(result["question"]) - qa_document.metadata["answer"] = result["answer"] - qa_document.metadata["doc_id"] = doc_id - qa_document.metadata["doc_hash"] = hash + if qa_document.metadata is not None: + doc_id = str(uuid.uuid4()) + hash = helper.generate_text_hash(result["question"]) + qa_document.metadata["answer"] = result["answer"] + qa_document.metadata["doc_id"] = doc_id + qa_document.metadata["doc_hash"] = hash qa_documents.append(qa_document) format_documents.extend(qa_documents) except Exception as e: @@ -575,7 +585,7 @@ class IndexingRunner: """ Split the text documents into nodes. """ - all_documents = [] + all_documents: list[Document] = [] for text_doc in text_docs: # document clean document_text = self._document_clean(text_doc.page_content, processing_rule) @@ -588,11 +598,11 @@ class IndexingRunner: for document in documents: if document.page_content is None or not document.page_content.strip(): continue - doc_id = str(uuid.uuid4()) - hash = helper.generate_text_hash(document.page_content) - - document.metadata["doc_id"] = doc_id - document.metadata["doc_hash"] = hash + if document.metadata is not None: + doc_id = str(uuid.uuid4()) + hash = helper.generate_text_hash(document.page_content) + document.metadata["doc_id"] = doc_id + document.metadata["doc_hash"] = hash split_documents.append(document) @@ -648,7 +658,7 @@ class IndexingRunner: # create keyword index create_keyword_thread = threading.Thread( target=self._process_keyword_index, - args=(current_app._get_current_object(), dataset.id, dataset_document.id, documents), + args=(current_app._get_current_object(), dataset.id, dataset_document.id, documents), # type: ignore ) create_keyword_thread.start() if dataset.indexing_technique == "high_quality": @@ -659,7 +669,7 @@ class IndexingRunner: futures.append( executor.submit( self._process_chunk, - current_app._get_current_object(), + current_app._get_current_object(), # type: ignore index_processor, chunk_documents, dataset, diff --git a/api/core/llm_generator/llm_generator.py b/api/core/llm_generator/llm_generator.py index 3a92c8d9d2..9fe3f68f2a 100644 --- a/api/core/llm_generator/llm_generator.py +++ b/api/core/llm_generator/llm_generator.py @@ -1,7 +1,7 @@ import json import logging import re -from typing import Optional +from typing import Optional, cast from core.llm_generator.output_parser.rule_config_generator import RuleConfigGeneratorOutputParser from core.llm_generator.output_parser.suggested_questions_after_answer import SuggestedQuestionsAfterAnswerOutputParser @@ -13,6 +13,7 @@ from core.llm_generator.prompts import ( WORKFLOW_RULE_CONFIG_PROMPT_GENERATE_TEMPLATE, ) from core.model_manager import ModelManager +from core.model_runtime.entities.llm_entities import LLMResult from core.model_runtime.entities.message_entities import SystemPromptMessage, UserPromptMessage from core.model_runtime.entities.model_entities import ModelType from core.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeError @@ -44,10 +45,13 @@ class LLMGenerator: prompts = [UserPromptMessage(content=prompt)] with measure_time() as timer: - response = model_instance.invoke_llm( - prompt_messages=prompts, model_parameters={"max_tokens": 100, "temperature": 1}, stream=False + response = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompts, model_parameters={"max_tokens": 100, "temperature": 1}, stream=False + ), ) - answer = response.message.content + answer = cast(str, response.message.content) cleaned_answer = re.sub(r"^.*(\{.*\}).*$", r"\1", answer, flags=re.DOTALL) if cleaned_answer is None: return "" @@ -94,11 +98,16 @@ class LLMGenerator: prompt_messages = [UserPromptMessage(content=prompt)] try: - response = model_instance.invoke_llm( - prompt_messages=prompt_messages, model_parameters={"max_tokens": 256, "temperature": 0}, stream=False + response = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, + model_parameters={"max_tokens": 256, "temperature": 0}, + stream=False, + ), ) - questions = output_parser.parse(response.message.content) + questions = output_parser.parse(cast(str, response.message.content)) except InvokeError: questions = [] except Exception as e: @@ -138,11 +147,14 @@ class LLMGenerator: ) try: - response = model_instance.invoke_llm( - prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + response = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + ), ) - rule_config["prompt"] = response.message.content + rule_config["prompt"] = cast(str, response.message.content) except InvokeError as e: error = str(e) @@ -178,15 +190,18 @@ class LLMGenerator: model_instance = model_manager.get_model_instance( tenant_id=tenant_id, model_type=ModelType.LLM, - provider=model_config.get("provider") if model_config else None, - model=model_config.get("name") if model_config else None, + provider=model_config.get("provider", ""), + model=model_config.get("name", ""), ) try: try: # the first step to generate the task prompt - prompt_content = model_instance.invoke_llm( - prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + prompt_content = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + ), ) except InvokeError as e: error = str(e) @@ -195,8 +210,10 @@ class LLMGenerator: return rule_config - rule_config["prompt"] = prompt_content.message.content + rule_config["prompt"] = cast(str, prompt_content.message.content) + if not isinstance(prompt_content.message.content, str): + raise NotImplementedError("prompt content is not a string") parameter_generate_prompt = parameter_template.format( inputs={ "INPUT_TEXT": prompt_content.message.content, @@ -216,19 +233,25 @@ class LLMGenerator: statement_messages = [UserPromptMessage(content=statement_generate_prompt)] try: - parameter_content = model_instance.invoke_llm( - prompt_messages=parameter_messages, model_parameters=model_parameters, stream=False + parameter_content = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=parameter_messages, model_parameters=model_parameters, stream=False + ), ) - rule_config["variables"] = re.findall(r'"\s*([^"]+)\s*"', parameter_content.message.content) + rule_config["variables"] = re.findall(r'"\s*([^"]+)\s*"', cast(str, parameter_content.message.content)) except InvokeError as e: error = str(e) error_step = "generate variables" try: - statement_content = model_instance.invoke_llm( - prompt_messages=statement_messages, model_parameters=model_parameters, stream=False + statement_content = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=statement_messages, model_parameters=model_parameters, stream=False + ), ) - rule_config["opening_statement"] = statement_content.message.content + rule_config["opening_statement"] = cast(str, statement_content.message.content) except InvokeError as e: error = str(e) error_step = "generate conversation opener" @@ -267,19 +290,22 @@ class LLMGenerator: model_instance = model_manager.get_model_instance( tenant_id=tenant_id, model_type=ModelType.LLM, - provider=model_config.get("provider") if model_config else None, - model=model_config.get("name") if model_config else None, + provider=model_config.get("provider", ""), + model=model_config.get("name", ""), ) prompt_messages = [UserPromptMessage(content=prompt)] model_parameters = {"max_tokens": max_tokens, "temperature": 0.01} try: - response = model_instance.invoke_llm( - prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + response = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, model_parameters=model_parameters, stream=False + ), ) - generated_code = response.message.content + generated_code = cast(str, response.message.content) return {"code": generated_code, "language": code_language, "error": ""} except InvokeError as e: @@ -303,9 +329,14 @@ class LLMGenerator: prompt_messages = [SystemPromptMessage(content=prompt), UserPromptMessage(content=query)] - response = model_instance.invoke_llm( - prompt_messages=prompt_messages, model_parameters={"temperature": 0.01, "max_tokens": 2000}, stream=False + response = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, + model_parameters={"temperature": 0.01, "max_tokens": 2000}, + stream=False, + ), ) - answer = response.message.content + answer = cast(str, response.message.content) return answer.strip() diff --git a/api/core/memory/token_buffer_memory.py b/api/core/memory/token_buffer_memory.py index 81d08dc885..003a0c85b1 100644 --- a/api/core/memory/token_buffer_memory.py +++ b/api/core/memory/token_buffer_memory.py @@ -68,7 +68,7 @@ class TokenBufferMemory: messages = list(reversed(thread_messages)) - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] for message in messages: files = db.session.query(MessageFile).filter(MessageFile.message_id == message.id).all() if files: diff --git a/api/core/model_manager.py b/api/core/model_manager.py index 1986688551..d1e71148cd 100644 --- a/api/core/model_manager.py +++ b/api/core/model_manager.py @@ -124,17 +124,20 @@ class ModelInstance: raise Exception("Model type instance is not LargeLanguageModel") self.model_type_instance = cast(LargeLanguageModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=tools, - stop=stop, - stream=stream, - user=user, - callbacks=callbacks, + return cast( + Union[LLMResult, Generator], + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + prompt_messages=prompt_messages, + model_parameters=model_parameters, + tools=tools, + stop=stop, + stream=stream, + user=user, + callbacks=callbacks, + ), ) def get_llm_num_tokens( @@ -151,12 +154,15 @@ class ModelInstance: raise Exception("Model type instance is not LargeLanguageModel") self.model_type_instance = cast(LargeLanguageModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.get_num_tokens, - model=self.model, - credentials=self.credentials, - prompt_messages=prompt_messages, - tools=tools, + return cast( + int, + self._round_robin_invoke( + function=self.model_type_instance.get_num_tokens, + model=self.model, + credentials=self.credentials, + prompt_messages=prompt_messages, + tools=tools, + ), ) def invoke_text_embedding( @@ -174,13 +180,16 @@ class ModelInstance: raise Exception("Model type instance is not TextEmbeddingModel") self.model_type_instance = cast(TextEmbeddingModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - texts=texts, - user=user, - input_type=input_type, + return cast( + TextEmbeddingResult, + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + texts=texts, + user=user, + input_type=input_type, + ), ) def get_text_embedding_num_tokens(self, texts: list[str]) -> int: @@ -194,11 +203,14 @@ class ModelInstance: raise Exception("Model type instance is not TextEmbeddingModel") self.model_type_instance = cast(TextEmbeddingModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.get_num_tokens, - model=self.model, - credentials=self.credentials, - texts=texts, + return cast( + int, + self._round_robin_invoke( + function=self.model_type_instance.get_num_tokens, + model=self.model, + credentials=self.credentials, + texts=texts, + ), ) def invoke_rerank( @@ -223,15 +235,18 @@ class ModelInstance: raise Exception("Model type instance is not RerankModel") self.model_type_instance = cast(RerankModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - query=query, - docs=docs, - score_threshold=score_threshold, - top_n=top_n, - user=user, + return cast( + RerankResult, + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + query=query, + docs=docs, + score_threshold=score_threshold, + top_n=top_n, + user=user, + ), ) def invoke_moderation(self, text: str, user: Optional[str] = None) -> bool: @@ -246,12 +261,15 @@ class ModelInstance: raise Exception("Model type instance is not ModerationModel") self.model_type_instance = cast(ModerationModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - text=text, - user=user, + return cast( + bool, + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + text=text, + user=user, + ), ) def invoke_speech2text(self, file: IO[bytes], user: Optional[str] = None) -> str: @@ -266,12 +284,15 @@ class ModelInstance: raise Exception("Model type instance is not Speech2TextModel") self.model_type_instance = cast(Speech2TextModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - file=file, - user=user, + return cast( + str, + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + file=file, + user=user, + ), ) def invoke_tts(self, content_text: str, tenant_id: str, voice: str, user: Optional[str] = None) -> Iterable[bytes]: @@ -288,17 +309,20 @@ class ModelInstance: raise Exception("Model type instance is not TTSModel") self.model_type_instance = cast(TTSModel, self.model_type_instance) - return self._round_robin_invoke( - function=self.model_type_instance.invoke, - model=self.model, - credentials=self.credentials, - content_text=content_text, - user=user, - tenant_id=tenant_id, - voice=voice, + return cast( + Iterable[bytes], + self._round_robin_invoke( + function=self.model_type_instance.invoke, + model=self.model, + credentials=self.credentials, + content_text=content_text, + user=user, + tenant_id=tenant_id, + voice=voice, + ), ) - def _round_robin_invoke(self, function: Callable[..., Any], *args, **kwargs): + def _round_robin_invoke(self, function: Callable[..., Any], *args, **kwargs) -> Any: """ Round-robin invoke :param function: function to invoke @@ -309,7 +333,7 @@ class ModelInstance: if not self.load_balancing_manager: return function(*args, **kwargs) - last_exception = None + last_exception: Union[InvokeRateLimitError, InvokeAuthorizationError, InvokeConnectionError, None] = None while True: lb_config = self.load_balancing_manager.fetch_next() if not lb_config: @@ -463,7 +487,7 @@ class LBModelManager: if real_index > max_index: real_index = 0 - config = self._load_balancing_configs[real_index] + config: ModelLoadBalancingConfiguration = self._load_balancing_configs[real_index] if self.in_cooldown(config): cooldown_load_balancing_configs.append(config) @@ -507,8 +531,7 @@ class LBModelManager: self._tenant_id, self._provider, self._model_type.value, self._model, config.id ) - res = redis_client.exists(cooldown_cache_key) - res = cast(bool, res) + res: bool = redis_client.exists(cooldown_cache_key) return res @staticmethod diff --git a/api/core/model_runtime/callbacks/logging_callback.py b/api/core/model_runtime/callbacks/logging_callback.py index 3b6b825244..1f21a2d376 100644 --- a/api/core/model_runtime/callbacks/logging_callback.py +++ b/api/core/model_runtime/callbacks/logging_callback.py @@ -1,7 +1,8 @@ import json import logging import sys -from typing import Optional +from collections.abc import Sequence +from typing import Optional, cast from core.model_runtime.callbacks.base_callback import Callback from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk @@ -20,7 +21,7 @@ class LoggingCallback(Callback): prompt_messages: list[PromptMessage], model_parameters: dict, tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, + stop: Optional[Sequence[str]] = None, stream: bool = True, user: Optional[str] = None, ) -> None: @@ -76,7 +77,7 @@ class LoggingCallback(Callback): prompt_messages: list[PromptMessage], model_parameters: dict, tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, + stop: Optional[Sequence[str]] = None, stream: bool = True, user: Optional[str] = None, ): @@ -94,7 +95,7 @@ class LoggingCallback(Callback): :param stream: is stream response :param user: unique user id """ - sys.stdout.write(chunk.delta.message.content) + sys.stdout.write(cast(str, chunk.delta.message.content)) sys.stdout.flush() def on_after_invoke( @@ -106,7 +107,7 @@ class LoggingCallback(Callback): prompt_messages: list[PromptMessage], model_parameters: dict, tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, + stop: Optional[Sequence[str]] = None, stream: bool = True, user: Optional[str] = None, ) -> None: @@ -147,7 +148,7 @@ class LoggingCallback(Callback): prompt_messages: list[PromptMessage], model_parameters: dict, tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[list[str]] = None, + stop: Optional[Sequence[str]] = None, stream: bool = True, user: Optional[str] = None, ) -> None: diff --git a/api/core/model_runtime/entities/message_entities.py b/api/core/model_runtime/entities/message_entities.py index 0efe46f87d..2f682ceef5 100644 --- a/api/core/model_runtime/entities/message_entities.py +++ b/api/core/model_runtime/entities/message_entities.py @@ -3,7 +3,7 @@ from collections.abc import Sequence from enum import Enum, StrEnum from typing import Optional -from pydantic import BaseModel, Field, computed_field, field_validator +from pydantic import BaseModel, Field, field_validator class PromptMessageRole(Enum): @@ -89,7 +89,6 @@ class MultiModalPromptMessageContent(PromptMessageContent): url: str = Field(default="", description="the url of multi-modal file") mime_type: str = Field(default=..., description="the mime type of multi-modal file") - @computed_field(return_type=str) @property def data(self): return self.url or f"data:{self.mime_type};base64,{self.base64_data}" diff --git a/api/core/model_runtime/model_providers/__base/ai_model.py b/api/core/model_runtime/model_providers/__base/ai_model.py index 79a1d28ebe..e2b9560337 100644 --- a/api/core/model_runtime/model_providers/__base/ai_model.py +++ b/api/core/model_runtime/model_providers/__base/ai_model.py @@ -1,7 +1,6 @@ import decimal import os from abc import ABC, abstractmethod -from collections.abc import Mapping from typing import Optional from pydantic import ConfigDict @@ -36,7 +35,7 @@ class AIModel(ABC): model_config = ConfigDict(protected_namespaces=()) @abstractmethod - def validate_credentials(self, model: str, credentials: Mapping) -> None: + def validate_credentials(self, model: str, credentials: dict) -> None: """ Validate model credentials @@ -214,7 +213,7 @@ class AIModel(ABC): return model_schemas - def get_model_schema(self, model: str, credentials: Optional[Mapping] = None) -> Optional[AIModelEntity]: + def get_model_schema(self, model: str, credentials: Optional[dict] = None) -> Optional[AIModelEntity]: """ Get model schema by model name and credentials @@ -236,9 +235,7 @@ class AIModel(ABC): return None - def get_customizable_model_schema_from_credentials( - self, model: str, credentials: Mapping - ) -> Optional[AIModelEntity]: + def get_customizable_model_schema_from_credentials(self, model: str, credentials: dict) -> Optional[AIModelEntity]: """ Get customizable model schema from credentials @@ -248,7 +245,7 @@ class AIModel(ABC): """ return self._get_customizable_model_schema(model, credentials) - def _get_customizable_model_schema(self, model: str, credentials: Mapping) -> Optional[AIModelEntity]: + def _get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: """ Get customizable model schema and fill in the template """ @@ -301,7 +298,7 @@ class AIModel(ABC): return schema - def get_customizable_model_schema(self, model: str, credentials: Mapping) -> Optional[AIModelEntity]: + def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: """ Get customizable model schema diff --git a/api/core/model_runtime/model_providers/__base/large_language_model.py b/api/core/model_runtime/model_providers/__base/large_language_model.py index 8faeffa872..402a30376b 100644 --- a/api/core/model_runtime/model_providers/__base/large_language_model.py +++ b/api/core/model_runtime/model_providers/__base/large_language_model.py @@ -2,7 +2,7 @@ import logging import re import time from abc import abstractmethod -from collections.abc import Generator, Mapping, Sequence +from collections.abc import Generator, Sequence from typing import Optional, Union from pydantic import ConfigDict @@ -48,7 +48,7 @@ class LargeLanguageModel(AIModel): prompt_messages: list[PromptMessage], model_parameters: Optional[dict] = None, tools: Optional[list[PromptMessageTool]] = None, - stop: Optional[Sequence[str]] = None, + stop: Optional[list[str]] = None, stream: bool = True, user: Optional[str] = None, callbacks: Optional[list[Callback]] = None, @@ -291,12 +291,12 @@ if you are not sure about the structure. content = piece.delta.message.content piece.delta.message.content = "" yield piece - piece = content + content_piece = content else: yield piece continue new_piece: str = "" - for char in piece: + for char in content_piece: char = str(char) if state == "normal": if char == "`": @@ -350,7 +350,7 @@ if you are not sure about the structure. piece.delta.message.content = "" # Yield a piece with cleared content before processing it to maintain the generator structure yield piece - piece = content + content_piece = content else: # Yield pieces without content directly yield piece @@ -360,7 +360,7 @@ if you are not sure about the structure. continue new_piece: str = "" - for char in piece: + for char in content_piece: if state == "search_start": if char == "`": backtick_count += 1 @@ -535,7 +535,7 @@ if you are not sure about the structure. return [] - def get_model_mode(self, model: str, credentials: Optional[Mapping] = None) -> LLMMode: + def get_model_mode(self, model: str, credentials: Optional[dict] = None) -> LLMMode: """ Get model mode diff --git a/api/core/model_runtime/model_providers/__base/model_provider.py b/api/core/model_runtime/model_providers/__base/model_provider.py index 4374093de4..36e3e7bd55 100644 --- a/api/core/model_runtime/model_providers/__base/model_provider.py +++ b/api/core/model_runtime/model_providers/__base/model_provider.py @@ -104,9 +104,10 @@ class ModelProvider(ABC): mod = import_module_from_source( module_name=f"{parent_module}.{model_type_name}.{model_type_name}", py_file_path=model_type_py_path ) + # FIXME "type" has no attribute "__abstractmethods__" ignore it for now fix it later model_class = next( filter( - lambda x: x.__module__ == mod.__name__ and not x.__abstractmethods__, + lambda x: x.__module__ == mod.__name__ and not x.__abstractmethods__, # type: ignore get_subclasses_from_module(mod, AIModel), ), None, diff --git a/api/core/model_runtime/model_providers/__base/text_embedding_model.py b/api/core/model_runtime/model_providers/__base/text_embedding_model.py index 2d38fba955..3313512908 100644 --- a/api/core/model_runtime/model_providers/__base/text_embedding_model.py +++ b/api/core/model_runtime/model_providers/__base/text_embedding_model.py @@ -89,7 +89,8 @@ class TextEmbeddingModel(AIModel): model_schema = self.get_model_schema(model, credentials) if model_schema and ModelPropertyKey.CONTEXT_SIZE in model_schema.model_properties: - return model_schema.model_properties[ModelPropertyKey.CONTEXT_SIZE] + content_size: int = model_schema.model_properties[ModelPropertyKey.CONTEXT_SIZE] + return content_size return 1000 @@ -104,6 +105,7 @@ class TextEmbeddingModel(AIModel): model_schema = self.get_model_schema(model, credentials) if model_schema and ModelPropertyKey.MAX_CHUNKS in model_schema.model_properties: - return model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] + max_chunks: int = model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] + return max_chunks return 1 diff --git a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2_tokenzier.py b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2_tokenzier.py index 5fe6dda6ad..6dab0aaf2d 100644 --- a/api/core/model_runtime/model_providers/__base/tokenizers/gpt2_tokenzier.py +++ b/api/core/model_runtime/model_providers/__base/tokenizers/gpt2_tokenzier.py @@ -2,9 +2,9 @@ from os.path import abspath, dirname, join from threading import Lock from typing import Any -from transformers import GPT2Tokenizer as TransformerGPT2Tokenizer +from transformers import GPT2Tokenizer as TransformerGPT2Tokenizer # type: ignore -_tokenizer = None +_tokenizer: Any = None _lock = Lock() diff --git a/api/core/model_runtime/model_providers/__base/tts_model.py b/api/core/model_runtime/model_providers/__base/tts_model.py index b394ea4e9d..6ce316b137 100644 --- a/api/core/model_runtime/model_providers/__base/tts_model.py +++ b/api/core/model_runtime/model_providers/__base/tts_model.py @@ -127,7 +127,8 @@ class TTSModel(AIModel): if not model_schema or ModelPropertyKey.AUDIO_TYPE not in model_schema.model_properties: raise ValueError("this model does not support audio type") - return model_schema.model_properties[ModelPropertyKey.AUDIO_TYPE] + audio_type: str = model_schema.model_properties[ModelPropertyKey.AUDIO_TYPE] + return audio_type def _get_model_word_limit(self, model: str, credentials: dict) -> int: """ @@ -138,8 +139,9 @@ class TTSModel(AIModel): if not model_schema or ModelPropertyKey.WORD_LIMIT not in model_schema.model_properties: raise ValueError("this model does not support word limit") + world_limit: int = model_schema.model_properties[ModelPropertyKey.WORD_LIMIT] - return model_schema.model_properties[ModelPropertyKey.WORD_LIMIT] + return world_limit def _get_model_workers_limit(self, model: str, credentials: dict) -> int: """ @@ -150,8 +152,9 @@ class TTSModel(AIModel): if not model_schema or ModelPropertyKey.MAX_WORKERS not in model_schema.model_properties: raise ValueError("this model does not support max workers") + workers_limit: int = model_schema.model_properties[ModelPropertyKey.MAX_WORKERS] - return model_schema.model_properties[ModelPropertyKey.MAX_WORKERS] + return workers_limit @staticmethod def _split_text_into_sentences(org_text, max_length=2000, pattern=r"[。.!?]"): diff --git a/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py b/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py index a2b14cf3db..4aa09e61fd 100644 --- a/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py +++ b/api/core/model_runtime/model_providers/azure_openai/speech2text/speech2text.py @@ -64,10 +64,12 @@ class AzureOpenAISpeech2TextModel(_CommonAzureOpenAI, Speech2TextModel): def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: ai_model_entity = self._get_ai_model_entity(credentials["base_model_name"], model) + if not ai_model_entity: + return None return ai_model_entity.entity @staticmethod - def _get_ai_model_entity(base_model_name: str, model: str) -> AzureBaseModel: + def _get_ai_model_entity(base_model_name: str, model: str) -> Optional[AzureBaseModel]: for ai_model_entity in SPEECH2TEXT_BASE_MODELS: if ai_model_entity.base_model_name == base_model_name: ai_model_entity_copy = copy.deepcopy(ai_model_entity) diff --git a/api/core/model_runtime/model_providers/azure_openai/tts/tts.py b/api/core/model_runtime/model_providers/azure_openai/tts/tts.py index 173b9d250c..6d50ba9163 100644 --- a/api/core/model_runtime/model_providers/azure_openai/tts/tts.py +++ b/api/core/model_runtime/model_providers/azure_openai/tts/tts.py @@ -114,6 +114,8 @@ class AzureOpenAIText2SpeechModel(_CommonAzureOpenAI, TTSModel): def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: ai_model_entity = self._get_ai_model_entity(credentials["base_model_name"], model) + if not ai_model_entity: + return None return ai_model_entity.entity @staticmethod diff --git a/api/core/model_runtime/model_providers/bedrock/llm/llm.py b/api/core/model_runtime/model_providers/bedrock/llm/llm.py index 75ed7ad624..29bd673d57 100644 --- a/api/core/model_runtime/model_providers/bedrock/llm/llm.py +++ b/api/core/model_runtime/model_providers/bedrock/llm/llm.py @@ -6,9 +6,9 @@ from collections.abc import Generator from typing import Optional, Union, cast # 3rd import -import boto3 -from botocore.config import Config -from botocore.exceptions import ( +import boto3 # type: ignore +from botocore.config import Config # type: ignore +from botocore.exceptions import ( # type: ignore ClientError, EndpointConnectionError, NoRegionError, diff --git a/api/core/model_runtime/model_providers/cohere/rerank/rerank.py b/api/core/model_runtime/model_providers/cohere/rerank/rerank.py index aba8fedbc0..3a0a241f7e 100644 --- a/api/core/model_runtime/model_providers/cohere/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/cohere/rerank/rerank.py @@ -44,7 +44,7 @@ class CohereRerankModel(RerankModel): :return: rerank result """ if len(docs) == 0: - return RerankResult(model=model, docs=docs) + return RerankResult(model=model, docs=[]) # initialize client client = cohere.Client(credentials.get("api_key"), base_url=credentials.get("base_url")) @@ -62,7 +62,7 @@ class CohereRerankModel(RerankModel): # format document rerank_document = RerankDocument( index=result.index, - text=result.document.text, + text=result.document.text if result.document else "", score=result.relevance_score, ) diff --git a/api/core/model_runtime/model_providers/fireworks/_common.py b/api/core/model_runtime/model_providers/fireworks/_common.py index 378ced3a40..38d0a9dfbc 100644 --- a/api/core/model_runtime/model_providers/fireworks/_common.py +++ b/api/core/model_runtime/model_providers/fireworks/_common.py @@ -1,5 +1,3 @@ -from collections.abc import Mapping - import openai from core.model_runtime.errors.invoke import ( @@ -13,7 +11,7 @@ from core.model_runtime.errors.invoke import ( class _CommonFireworks: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: + def _to_credential_kwargs(self, credentials: dict) -> dict: """ Transform credentials to kwargs for model instance diff --git a/api/core/model_runtime/model_providers/fireworks/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/fireworks/text_embedding/text_embedding.py index c745a7e978..4c03628389 100644 --- a/api/core/model_runtime/model_providers/fireworks/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/fireworks/text_embedding/text_embedding.py @@ -1,5 +1,4 @@ import time -from collections.abc import Mapping from typing import Optional, Union import numpy as np @@ -93,7 +92,7 @@ class FireworksTextEmbeddingModel(_CommonFireworks, TextEmbeddingModel): """ return sum(self._get_num_tokens_by_gpt2(text) for text in texts) - def validate_credentials(self, model: str, credentials: Mapping) -> None: + def validate_credentials(self, model: str, credentials: dict) -> None: """ Validate model credentials diff --git a/api/core/model_runtime/model_providers/gitee_ai/_common.py b/api/core/model_runtime/model_providers/gitee_ai/_common.py index 0750f3b75d..ad6600faf7 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/_common.py +++ b/api/core/model_runtime/model_providers/gitee_ai/_common.py @@ -1,4 +1,4 @@ -from dashscope.common.error import ( +from dashscope.common.error import ( # type: ignore AuthenticationError, InvalidParameter, RequestFailure, diff --git a/api/core/model_runtime/model_providers/gitee_ai/rerank/rerank.py b/api/core/model_runtime/model_providers/gitee_ai/rerank/rerank.py index 832ba92740..737d3d5c93 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/gitee_ai/rerank/rerank.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any, Optional import httpx @@ -51,7 +51,7 @@ class GiteeAIRerankModel(RerankModel): base_url = base_url.removesuffix("/") try: - body = {"model": model, "query": query, "documents": docs} + body: dict[str, Any] = {"model": model, "query": query, "documents": docs} if top_n is not None: body["top_n"] = top_n response = httpx.post( diff --git a/api/core/model_runtime/model_providers/gitee_ai/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/gitee_ai/text_embedding/text_embedding.py index b833c5652c..a1fa89c5b3 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/gitee_ai/text_embedding/text_embedding.py @@ -24,7 +24,7 @@ class GiteeAIEmbeddingModel(OAICompatEmbeddingModel): super().validate_credentials(model, credentials) @staticmethod - def _add_custom_parameters(credentials: dict, model: str) -> None: + def _add_custom_parameters(credentials: dict, model: Optional[str]) -> None: if model is None: model = "bge-m3" diff --git a/api/core/model_runtime/model_providers/gitee_ai/tts/tts.py b/api/core/model_runtime/model_providers/gitee_ai/tts/tts.py index 36dcea405d..dc91257daf 100644 --- a/api/core/model_runtime/model_providers/gitee_ai/tts/tts.py +++ b/api/core/model_runtime/model_providers/gitee_ai/tts/tts.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any, Optional import requests @@ -13,9 +13,10 @@ class GiteeAIText2SpeechModel(_CommonGiteeAI, TTSModel): Model class for OpenAI text2speech model. """ + # FIXME this Any return will be better type def _invoke( self, model: str, tenant_id: str, credentials: dict, content_text: str, voice: str, user: Optional[str] = None - ) -> any: + ) -> Any: """ _invoke text2speech model @@ -47,7 +48,8 @@ class GiteeAIText2SpeechModel(_CommonGiteeAI, TTSModel): except Exception as ex: raise CredentialsValidateFailedError(str(ex)) - def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> any: + # FIXME this Any return will be better type + def _tts_invoke_streaming(self, model: str, credentials: dict, content_text: str, voice: str) -> Any: """ _tts_invoke_streaming text2speech model :param model: model name diff --git a/api/core/model_runtime/model_providers/google/llm/llm.py b/api/core/model_runtime/model_providers/google/llm/llm.py index 7d19ccbb74..98273f60a4 100644 --- a/api/core/model_runtime/model_providers/google/llm/llm.py +++ b/api/core/model_runtime/model_providers/google/llm/llm.py @@ -7,7 +7,7 @@ from collections.abc import Generator from typing import Optional, Union import google.ai.generativelanguage as glm -import google.generativeai as genai +import google.generativeai as genai # type: ignore import requests from google.api_core import exceptions from google.generativeai.types import ContentType, File, GenerateContentResponse diff --git a/api/core/model_runtime/model_providers/huggingface_hub/_common.py b/api/core/model_runtime/model_providers/huggingface_hub/_common.py index 3c4020b6ee..d8a09265e2 100644 --- a/api/core/model_runtime/model_providers/huggingface_hub/_common.py +++ b/api/core/model_runtime/model_providers/huggingface_hub/_common.py @@ -1,4 +1,4 @@ -from huggingface_hub.utils import BadRequestError, HfHubHTTPError +from huggingface_hub.utils import BadRequestError, HfHubHTTPError # type: ignore from core.model_runtime.errors.invoke import InvokeBadRequestError, InvokeError diff --git a/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py b/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py index 9d29237fdd..cdb4103cd8 100644 --- a/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py +++ b/api/core/model_runtime/model_providers/huggingface_hub/llm/llm.py @@ -1,9 +1,9 @@ from collections.abc import Generator from typing import Optional, Union -from huggingface_hub import InferenceClient -from huggingface_hub.hf_api import HfApi -from huggingface_hub.utils import BadRequestError +from huggingface_hub import InferenceClient # type: ignore +from huggingface_hub.hf_api import HfApi # type: ignore +from huggingface_hub.utils import BadRequestError # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.defaults import PARAMETER_RULE_TEMPLATE diff --git a/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/text_embedding.py index 8278d1e64d..4ca5379405 100644 --- a/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/huggingface_hub/text_embedding/text_embedding.py @@ -4,7 +4,7 @@ from typing import Optional import numpy as np import requests -from huggingface_hub import HfApi, InferenceClient +from huggingface_hub import HfApi, InferenceClient # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.common_entities import I18nObject diff --git a/api/core/model_runtime/model_providers/hunyuan/llm/llm.py b/api/core/model_runtime/model_providers/hunyuan/llm/llm.py index 2014de8516..2dd45f065d 100644 --- a/api/core/model_runtime/model_providers/hunyuan/llm/llm.py +++ b/api/core/model_runtime/model_providers/hunyuan/llm/llm.py @@ -3,11 +3,11 @@ import logging from collections.abc import Generator from typing import cast -from tencentcloud.common import credential -from tencentcloud.common.exception import TencentCloudSDKException -from tencentcloud.common.profile.client_profile import ClientProfile -from tencentcloud.common.profile.http_profile import HttpProfile -from tencentcloud.hunyuan.v20230901 import hunyuan_client, models +from tencentcloud.common import credential # type: ignore +from tencentcloud.common.exception import TencentCloudSDKException # type: ignore +from tencentcloud.common.profile.client_profile import ClientProfile # type: ignore +from tencentcloud.common.profile.http_profile import HttpProfile # type: ignore +from tencentcloud.hunyuan.v20230901 import hunyuan_client, models # type: ignore from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( @@ -305,7 +305,7 @@ class HunyuanLargeLanguageModel(LargeLanguageModel): elif isinstance(message, ToolPromptMessage): message_text = f"{tool_prompt} {content}" elif isinstance(message, SystemPromptMessage): - message_text = content + message_text = content if isinstance(content, str) else "" else: raise ValueError(f"Got unknown type {message}") diff --git a/api/core/model_runtime/model_providers/hunyuan/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/hunyuan/text_embedding/text_embedding.py index b6d857cb37..856cda90d3 100644 --- a/api/core/model_runtime/model_providers/hunyuan/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/hunyuan/text_embedding/text_embedding.py @@ -3,11 +3,11 @@ import logging import time from typing import Optional -from tencentcloud.common import credential -from tencentcloud.common.exception import TencentCloudSDKException -from tencentcloud.common.profile.client_profile import ClientProfile -from tencentcloud.common.profile.http_profile import HttpProfile -from tencentcloud.hunyuan.v20230901 import hunyuan_client, models +from tencentcloud.common import credential # type: ignore +from tencentcloud.common.exception import TencentCloudSDKException # type: ignore +from tencentcloud.common.profile.client_profile import ClientProfile # type: ignore +from tencentcloud.common.profile.http_profile import HttpProfile # type: ignore +from tencentcloud.hunyuan.v20230901 import hunyuan_client, models # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.model_entities import PriceType diff --git a/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py b/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py index d80cbfa83d..1fc0f8c028 100644 --- a/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py +++ b/api/core/model_runtime/model_providers/jina/text_embedding/jina_tokenizer.py @@ -1,11 +1,11 @@ from os.path import abspath, dirname, join from threading import Lock -from transformers import AutoTokenizer +from transformers import AutoTokenizer # type: ignore class JinaTokenizer: - _tokenizer = None + _tokenizer: AutoTokenizer | None = None _lock = Lock() @classmethod diff --git a/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py b/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py index 88cc0e8e0f..357631b2db 100644 --- a/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py +++ b/api/core/model_runtime/model_providers/minimax/llm/chat_completion.py @@ -40,7 +40,7 @@ class MinimaxChatCompletion: url = f"https://api.minimax.chat/v1/text/chatcompletion?GroupId={group_id}" - extra_kwargs = {} + extra_kwargs: dict[str, Any] = {} if "max_tokens" in model_parameters and type(model_parameters["max_tokens"]) == int: extra_kwargs["tokens_to_generate"] = model_parameters["max_tokens"] @@ -117,19 +117,19 @@ class MinimaxChatCompletion: """ handle chat generate response """ - response = response.json() - if "base_resp" in response and response["base_resp"]["status_code"] != 0: - code = response["base_resp"]["status_code"] - msg = response["base_resp"]["status_msg"] + response_data = response.json() + if "base_resp" in response_data and response_data["base_resp"]["status_code"] != 0: + code = response_data["base_resp"]["status_code"] + msg = response_data["base_resp"]["status_msg"] self._handle_error(code, msg) - message = MinimaxMessage(content=response["reply"], role=MinimaxMessage.Role.ASSISTANT.value) + message = MinimaxMessage(content=response_data["reply"], role=MinimaxMessage.Role.ASSISTANT.value) message.usage = { "prompt_tokens": 0, - "completion_tokens": response["usage"]["total_tokens"], - "total_tokens": response["usage"]["total_tokens"], + "completion_tokens": response_data["usage"]["total_tokens"], + "total_tokens": response_data["usage"]["total_tokens"], } - message.stop_reason = response["choices"][0]["finish_reason"] + message.stop_reason = response_data["choices"][0]["finish_reason"] return message def _handle_stream_chat_generate_response(self, response: Response) -> Generator[MinimaxMessage, None, None]: @@ -139,10 +139,10 @@ class MinimaxChatCompletion: for line in response.iter_lines(): if not line: continue - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() - data = loads(line) + line_str: str = line.decode("utf-8") + if line_str.startswith("data: "): + line_str = line_str[6:].strip() + data = loads(line_str) if "base_resp" in data and data["base_resp"]["status_code"] != 0: code = data["base_resp"]["status_code"] @@ -162,5 +162,5 @@ class MinimaxChatCompletion: continue for choice in choices: - message = choice["delta"] - yield MinimaxMessage(content=message, role=MinimaxMessage.Role.ASSISTANT.value) + message_choice = choice["delta"] + yield MinimaxMessage(content=message_choice, role=MinimaxMessage.Role.ASSISTANT.value) diff --git a/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py b/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py index 8b8fdbb6bd..284b61829f 100644 --- a/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py +++ b/api/core/model_runtime/model_providers/minimax/llm/chat_completion_pro.py @@ -41,7 +41,7 @@ class MinimaxChatCompletionPro: url = f"https://api.minimax.chat/v1/text/chatcompletion_pro?GroupId={group_id}" - extra_kwargs = {} + extra_kwargs: dict[str, Any] = {} if "max_tokens" in model_parameters and type(model_parameters["max_tokens"]) == int: extra_kwargs["tokens_to_generate"] = model_parameters["max_tokens"] @@ -122,19 +122,19 @@ class MinimaxChatCompletionPro: """ handle chat generate response """ - response = response.json() - if "base_resp" in response and response["base_resp"]["status_code"] != 0: - code = response["base_resp"]["status_code"] - msg = response["base_resp"]["status_msg"] + response_data = response.json() + if "base_resp" in response_data and response_data["base_resp"]["status_code"] != 0: + code = response_data["base_resp"]["status_code"] + msg = response_data["base_resp"]["status_msg"] self._handle_error(code, msg) - message = MinimaxMessage(content=response["reply"], role=MinimaxMessage.Role.ASSISTANT.value) + message = MinimaxMessage(content=response_data["reply"], role=MinimaxMessage.Role.ASSISTANT.value) message.usage = { "prompt_tokens": 0, - "completion_tokens": response["usage"]["total_tokens"], - "total_tokens": response["usage"]["total_tokens"], + "completion_tokens": response_data["usage"]["total_tokens"], + "total_tokens": response_data["usage"]["total_tokens"], } - message.stop_reason = response["choices"][0]["finish_reason"] + message.stop_reason = response_data["choices"][0]["finish_reason"] return message def _handle_stream_chat_generate_response(self, response: Response) -> Generator[MinimaxMessage, None, None]: @@ -144,10 +144,10 @@ class MinimaxChatCompletionPro: for line in response.iter_lines(): if not line: continue - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() - data = loads(line) + line_str: str = line.decode("utf-8") + if line_str.startswith("data: "): + line_str = line_str[6:].strip() + data = loads(line_str) if "base_resp" in data and data["base_resp"]["status_code"] != 0: code = data["base_resp"]["status_code"] diff --git a/api/core/model_runtime/model_providers/minimax/llm/types.py b/api/core/model_runtime/model_providers/minimax/llm/types.py index 88ebe5e2e0..c248db374a 100644 --- a/api/core/model_runtime/model_providers/minimax/llm/types.py +++ b/api/core/model_runtime/model_providers/minimax/llm/types.py @@ -11,9 +11,9 @@ class MinimaxMessage: role: str = Role.USER.value content: str - usage: dict[str, int] = None + usage: dict[str, int] | None = None stop_reason: str = "" - function_call: dict[str, Any] = None + function_call: dict[str, Any] | None = None def to_dict(self) -> dict[str, Any]: if self.function_call and self.role == MinimaxMessage.Role.ASSISTANT.value: diff --git a/api/core/model_runtime/model_providers/nomic/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/nomic/text_embedding/text_embedding.py index 56a707333c..8a4c19d4d8 100644 --- a/api/core/model_runtime/model_providers/nomic/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/nomic/text_embedding/text_embedding.py @@ -2,8 +2,8 @@ import time from functools import wraps from typing import Optional -from nomic import embed -from nomic import login as nomic_login +from nomic import embed # type: ignore +from nomic import login as nomic_login # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.model_entities import PriceType diff --git a/api/core/model_runtime/model_providers/oci/llm/llm.py b/api/core/model_runtime/model_providers/oci/llm/llm.py index 1e1fc5b3ea..9f676573fc 100644 --- a/api/core/model_runtime/model_providers/oci/llm/llm.py +++ b/api/core/model_runtime/model_providers/oci/llm/llm.py @@ -5,8 +5,8 @@ import logging from collections.abc import Generator from typing import Optional, Union -import oci -from oci.generative_ai_inference.models.base_chat_response import BaseChatResponse +import oci # type: ignore +from oci.generative_ai_inference.models.base_chat_response import BaseChatResponse # type: ignore from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( diff --git a/api/core/model_runtime/model_providers/oci/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/oci/text_embedding/text_embedding.py index 50fa63768c..5a428c9fed 100644 --- a/api/core/model_runtime/model_providers/oci/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/oci/text_embedding/text_embedding.py @@ -4,7 +4,7 @@ import time from typing import Optional import numpy as np -import oci +import oci # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.model_entities import PriceType diff --git a/api/core/model_runtime/model_providers/ollama/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/ollama/text_embedding/text_embedding.py index 83c4facc8d..3543fe58bb 100644 --- a/api/core/model_runtime/model_providers/ollama/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/ollama/text_embedding/text_embedding.py @@ -61,6 +61,7 @@ class OllamaEmbeddingModel(TextEmbeddingModel): headers = {"Content-Type": "application/json"} endpoint_url = credentials.get("base_url") + assert endpoint_url is not None, "Base URL is required for Ollama API" if not endpoint_url.endswith("/"): endpoint_url += "/" diff --git a/api/core/model_runtime/model_providers/openai/_common.py b/api/core/model_runtime/model_providers/openai/_common.py index 2181bb4f08..ac2b3e6881 100644 --- a/api/core/model_runtime/model_providers/openai/_common.py +++ b/api/core/model_runtime/model_providers/openai/_common.py @@ -1,5 +1,3 @@ -from collections.abc import Mapping - import openai from httpx import Timeout @@ -14,7 +12,7 @@ from core.model_runtime.errors.invoke import ( class _CommonOpenAI: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: + def _to_credential_kwargs(self, credentials: dict) -> dict: """ Transform credentials to kwargs for model instance diff --git a/api/core/model_runtime/model_providers/openai/moderation/moderation.py b/api/core/model_runtime/model_providers/openai/moderation/moderation.py index 619044d808..227e4b0c15 100644 --- a/api/core/model_runtime/model_providers/openai/moderation/moderation.py +++ b/api/core/model_runtime/model_providers/openai/moderation/moderation.py @@ -93,7 +93,8 @@ class OpenAIModerationModel(_CommonOpenAI, ModerationModel): model_schema = self.get_model_schema(model, credentials) if model_schema and ModelPropertyKey.MAX_CHARACTERS_PER_CHUNK in model_schema.model_properties: - return model_schema.model_properties[ModelPropertyKey.MAX_CHARACTERS_PER_CHUNK] + max_characters_per_chunk: int = model_schema.model_properties[ModelPropertyKey.MAX_CHARACTERS_PER_CHUNK] + return max_characters_per_chunk return 2000 @@ -108,6 +109,7 @@ class OpenAIModerationModel(_CommonOpenAI, ModerationModel): model_schema = self.get_model_schema(model, credentials) if model_schema and ModelPropertyKey.MAX_CHUNKS in model_schema.model_properties: - return model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] + max_chunks: int = model_schema.model_properties[ModelPropertyKey.MAX_CHUNKS] + return max_chunks return 1 diff --git a/api/core/model_runtime/model_providers/openai/openai.py b/api/core/model_runtime/model_providers/openai/openai.py index aa6f38ce9f..c546441af6 100644 --- a/api/core/model_runtime/model_providers/openai/openai.py +++ b/api/core/model_runtime/model_providers/openai/openai.py @@ -1,5 +1,4 @@ import logging -from collections.abc import Mapping from core.model_runtime.entities.model_entities import ModelType from core.model_runtime.errors.validate import CredentialsValidateFailedError @@ -9,7 +8,7 @@ logger = logging.getLogger(__name__) class OpenAIProvider(ModelProvider): - def validate_provider_credentials(self, credentials: Mapping) -> None: + def validate_provider_credentials(self, credentials: dict) -> None: """ Validate provider credentials if validate failed, raise exception diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py b/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py index a490537e51..74229a089a 100644 --- a/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py +++ b/api/core/model_runtime/model_providers/openai_api_compatible/speech2text/speech2text.py @@ -33,6 +33,7 @@ class OAICompatSpeech2TextModel(_CommonOaiApiCompat, Speech2TextModel): headers["Authorization"] = f"Bearer {api_key}" endpoint_url = credentials.get("endpoint_url") + assert endpoint_url is not None, "endpoint_url is required in credentials" if not endpoint_url.endswith("/"): endpoint_url += "/" endpoint_url = urljoin(endpoint_url, "audio/transcriptions") diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/text_embedding.py index 9da8f55d0a..b4d6c6c6ca 100644 --- a/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/openai_api_compatible/text_embedding/text_embedding.py @@ -55,6 +55,7 @@ class OAICompatEmbeddingModel(_CommonOaiApiCompat, TextEmbeddingModel): headers["Authorization"] = f"Bearer {api_key}" endpoint_url = credentials.get("endpoint_url") + assert endpoint_url is not None, "endpoint_url is required in credentials" if not endpoint_url.endswith("/"): endpoint_url += "/" diff --git a/api/core/model_runtime/model_providers/openai_api_compatible/tts/tts.py b/api/core/model_runtime/model_providers/openai_api_compatible/tts/tts.py index 8239c625f7..53e895b0ec 100644 --- a/api/core/model_runtime/model_providers/openai_api_compatible/tts/tts.py +++ b/api/core/model_runtime/model_providers/openai_api_compatible/tts/tts.py @@ -44,6 +44,7 @@ class OAICompatText2SpeechModel(_CommonOaiApiCompat, TTSModel): # Construct endpoint URL endpoint_url = credentials.get("endpoint_url") + assert endpoint_url is not None, "endpoint_url is required in credentials" if not endpoint_url.endswith("/"): endpoint_url += "/" endpoint_url = urljoin(endpoint_url, "audio/speech") diff --git a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py b/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py index 2789a9250a..e9509b544d 100644 --- a/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py +++ b/api/core/model_runtime/model_providers/openllm/llm/openllm_generate.py @@ -1,7 +1,7 @@ from collections.abc import Generator from enum import Enum from json import dumps, loads -from typing import Any, Union +from typing import Any, Optional, Union from requests import Response, post from requests.exceptions import ConnectionError, InvalidSchema, MissingSchema @@ -20,7 +20,7 @@ class OpenLLMGenerateMessage: role: str = Role.USER.value content: str - usage: dict[str, int] = None + usage: Optional[dict[str, int]] = None stop_reason: str = "" def to_dict(self) -> dict[str, Any]: @@ -165,17 +165,17 @@ class OpenLLMGenerate: if not line: continue - line: str = line.decode("utf-8") - if line.startswith("data: "): - line = line[6:].strip() + line_str: str = line.decode("utf-8") + if line_str.startswith("data: "): + line_str = line_str[6:].strip() - if line == "[DONE]": + if line_str == "[DONE]": return try: - data = loads(line) + data = loads(line_str) except Exception as e: - raise InternalServerError(f"Failed to convert response to json: {e} with text: {line}") + raise InternalServerError(f"Failed to convert response to json: {e} with text: {line_str}") output = data["outputs"] diff --git a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/text_embedding.py index 7bbd31e87c..40ea4dc011 100644 --- a/api/core/model_runtime/model_providers/perfxcloud/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/perfxcloud/text_embedding/text_embedding.py @@ -53,14 +53,16 @@ class OAICompatEmbeddingModel(_CommonOaiApiCompat, TextEmbeddingModel): api_key = credentials.get("api_key") if api_key: headers["Authorization"] = f"Bearer {api_key}" - + endpoint_url: Optional[str] if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": endpoint_url = "https://cloud.perfxlab.cn/v1/" else: endpoint_url = credentials.get("endpoint_url") + assert endpoint_url is not None, "endpoint_url is required in credentials" if not endpoint_url.endswith("/"): endpoint_url += "/" + assert isinstance(endpoint_url, str) endpoint_url = urljoin(endpoint_url, "embeddings") extra_model_kwargs = {} @@ -142,13 +144,16 @@ class OAICompatEmbeddingModel(_CommonOaiApiCompat, TextEmbeddingModel): if api_key: headers["Authorization"] = f"Bearer {api_key}" + endpoint_url: Optional[str] if "endpoint_url" not in credentials or credentials["endpoint_url"] == "": endpoint_url = "https://cloud.perfxlab.cn/v1/" else: endpoint_url = credentials.get("endpoint_url") + assert endpoint_url is not None, "endpoint_url is required in credentials" if not endpoint_url.endswith("/"): endpoint_url += "/" + assert isinstance(endpoint_url, str) endpoint_url = urljoin(endpoint_url, "embeddings") payload = {"input": "ping", "model": model} diff --git a/api/core/model_runtime/model_providers/replicate/_common.py b/api/core/model_runtime/model_providers/replicate/_common.py index 915f6e0eef..3e2cf2adb3 100644 --- a/api/core/model_runtime/model_providers/replicate/_common.py +++ b/api/core/model_runtime/model_providers/replicate/_common.py @@ -1,4 +1,4 @@ -from replicate.exceptions import ModelError, ReplicateError +from replicate.exceptions import ModelError, ReplicateError # type: ignore from core.model_runtime.errors.invoke import InvokeBadRequestError, InvokeError diff --git a/api/core/model_runtime/model_providers/replicate/llm/llm.py b/api/core/model_runtime/model_providers/replicate/llm/llm.py index 3641b35dc0..1e7858100b 100644 --- a/api/core/model_runtime/model_providers/replicate/llm/llm.py +++ b/api/core/model_runtime/model_providers/replicate/llm/llm.py @@ -1,9 +1,9 @@ from collections.abc import Generator from typing import Optional, Union -from replicate import Client as ReplicateClient -from replicate.exceptions import ReplicateError -from replicate.prediction import Prediction +from replicate import Client as ReplicateClient # type: ignore +from replicate.exceptions import ReplicateError # type: ignore +from replicate.prediction import Prediction # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta diff --git a/api/core/model_runtime/model_providers/replicate/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/replicate/text_embedding/text_embedding.py index 41759fe07d..aaf825388a 100644 --- a/api/core/model_runtime/model_providers/replicate/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/replicate/text_embedding/text_embedding.py @@ -2,11 +2,11 @@ import json import time from typing import Optional -from replicate import Client as ReplicateClient +from replicate import Client as ReplicateClient # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.common_entities import I18nObject -from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType, PriceType +from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelPropertyKey, ModelType, PriceType from core.model_runtime.entities.text_embedding_entities import EmbeddingUsage, TextEmbeddingResult from core.model_runtime.errors.validate import CredentialsValidateFailedError from core.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel @@ -86,7 +86,7 @@ class ReplicateEmbeddingModel(_CommonReplicate, TextEmbeddingModel): label=I18nObject(en_US=model), fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, model_type=ModelType.TEXT_EMBEDDING, - model_properties={"context_size": 4096, "max_chunks": 1}, + model_properties={ModelPropertyKey.CONTEXT_SIZE: 4096, ModelPropertyKey.MAX_CHUNKS: 1}, ) return entity diff --git a/api/core/model_runtime/model_providers/sagemaker/llm/llm.py b/api/core/model_runtime/model_providers/sagemaker/llm/llm.py index 5ff00f008e..b8c979b1f5 100644 --- a/api/core/model_runtime/model_providers/sagemaker/llm/llm.py +++ b/api/core/model_runtime/model_providers/sagemaker/llm/llm.py @@ -4,7 +4,7 @@ import re from collections.abc import Generator, Iterator from typing import Any, Optional, Union, cast -import boto3 +import boto3 # type: ignore from core.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( @@ -83,7 +83,7 @@ class SageMakerLargeLanguageModel(LargeLanguageModel): sagemaker_session: Any = None predictor: Any = None - sagemaker_endpoint: str = None + sagemaker_endpoint: str | None = None def _handle_chat_generate_response( self, @@ -209,8 +209,8 @@ class SageMakerLargeLanguageModel(LargeLanguageModel): :param user: unique user id :return: full response or stream response chunk generator result """ - from sagemaker import Predictor, serializers - from sagemaker.session import Session + from sagemaker import Predictor, serializers # type: ignore + from sagemaker.session import Session # type: ignore if not self.sagemaker_session: access_key = credentials.get("aws_access_key_id") diff --git a/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py b/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py index df797bae26..7daab6d865 100644 --- a/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/sagemaker/rerank/rerank.py @@ -3,7 +3,7 @@ import logging import operator from typing import Any, Optional -import boto3 +import boto3 # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType @@ -114,6 +114,7 @@ class SageMakerRerankModel(RerankModel): except Exception as e: logger.exception(f"Failed to invoke rerank model, model: {model}") + raise InvokeError(f"Failed to invoke rerank model, model: {model}, error: {str(e)}") def validate_credentials(self, model: str, credentials: dict) -> None: """ diff --git a/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py b/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py index 2d50e9c7b4..a6aca13045 100644 --- a/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py +++ b/api/core/model_runtime/model_providers/sagemaker/speech2text/speech2text.py @@ -2,7 +2,7 @@ import json import logging from typing import IO, Any, Optional -import boto3 +import boto3 # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType @@ -67,6 +67,7 @@ class SageMakerSpeech2TextModel(Speech2TextModel): s3_prefix = "dify/speech2text/" sagemaker_endpoint = credentials.get("sagemaker_endpoint") bucket = credentials.get("audio_s3_cache_bucket") + assert bucket is not None, "audio_s3_cache_bucket is required in credentials" s3_presign_url = generate_presigned_url(self.s3_client, file, bucket, s3_prefix) payload = {"audio_s3_presign_uri": s3_presign_url} diff --git a/api/core/model_runtime/model_providers/sagemaker/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/sagemaker/text_embedding/text_embedding.py index ef4ddcd6a7..e7eccd997d 100644 --- a/api/core/model_runtime/model_providers/sagemaker/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/sagemaker/text_embedding/text_embedding.py @@ -4,7 +4,7 @@ import logging import time from typing import Any, Optional -import boto3 +import boto3 # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.common_entities import I18nObject @@ -118,6 +118,7 @@ class SageMakerEmbeddingModel(TextEmbeddingModel): except Exception as e: logger.exception(f"Failed to invoke text embedding model, model: {model}, line: {line}") + raise InvokeError(str(e)) def get_num_tokens(self, model: str, credentials: dict, texts: list[str]) -> int: """ diff --git a/api/core/model_runtime/model_providers/sagemaker/tts/tts.py b/api/core/model_runtime/model_providers/sagemaker/tts/tts.py index 6a5946453b..62231c518d 100644 --- a/api/core/model_runtime/model_providers/sagemaker/tts/tts.py +++ b/api/core/model_runtime/model_providers/sagemaker/tts/tts.py @@ -5,7 +5,7 @@ import logging from enum import Enum from typing import Any, Optional -import boto3 +import boto3 # type: ignore import requests from core.model_runtime.entities.common_entities import I18nObject diff --git a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py b/api/core/model_runtime/model_providers/siliconflow/llm/llm.py index e3a323a496..f61e8b82e4 100644 --- a/api/core/model_runtime/model_providers/siliconflow/llm/llm.py +++ b/api/core/model_runtime/model_providers/siliconflow/llm/llm.py @@ -43,7 +43,7 @@ class SiliconflowLargeLanguageModel(OAIAPICompatLargeLanguageModel): credentials["mode"] = "chat" credentials["endpoint_url"] = "https://api.siliconflow.cn/v1" - def get_customizable_model_schema(self, model: str, credentials: dict) -> Optional[AIModelEntity]: + def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: return AIModelEntity( model=model, label=I18nObject(en_US=model, zh_Hans=model), diff --git a/api/core/model_runtime/model_providers/spark/llm/llm.py b/api/core/model_runtime/model_providers/spark/llm/llm.py index 1181ba699a..cb6f28b6c2 100644 --- a/api/core/model_runtime/model_providers/spark/llm/llm.py +++ b/api/core/model_runtime/model_providers/spark/llm/llm.py @@ -1,6 +1,6 @@ import threading from collections.abc import Generator -from typing import Optional, Union +from typing import Optional, Union, cast from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( @@ -270,7 +270,7 @@ class SparkLargeLanguageModel(LargeLanguageModel): elif isinstance(message, AssistantPromptMessage): message_text = f"{ai_prompt} {content}" elif isinstance(message, SystemPromptMessage): - message_text = content + message_text = cast(str, content) else: raise ValueError(f"Got unknown type {message}") diff --git a/api/core/model_runtime/model_providers/togetherai/llm/llm.py b/api/core/model_runtime/model_providers/togetherai/llm/llm.py index b96d43979e..03eac19423 100644 --- a/api/core/model_runtime/model_providers/togetherai/llm/llm.py +++ b/api/core/model_runtime/model_providers/togetherai/llm/llm.py @@ -12,6 +12,7 @@ from core.model_runtime.entities.model_entities import ( AIModelEntity, DefaultParameterName, FetchFrom, + ModelFeature, ModelPropertyKey, ModelType, ParameterRule, @@ -67,7 +68,7 @@ class TogetherAILargeLanguageModel(OAIAPICompatLargeLanguageModel): cred_with_endpoint = self._update_endpoint_url(credentials=credentials) REPETITION_PENALTY = "repetition_penalty" TOP_K = "top_k" - features = [] + features: list[ModelFeature] = [] entity = AIModelEntity( model=model, diff --git a/api/core/model_runtime/model_providers/tongyi/_common.py b/api/core/model_runtime/model_providers/tongyi/_common.py index 8a50c7aa05..bb68319555 100644 --- a/api/core/model_runtime/model_providers/tongyi/_common.py +++ b/api/core/model_runtime/model_providers/tongyi/_common.py @@ -1,4 +1,4 @@ -from dashscope.common.error import ( +from dashscope.common.error import ( # type: ignore AuthenticationError, InvalidParameter, RequestFailure, diff --git a/api/core/model_runtime/model_providers/tongyi/llm/llm.py b/api/core/model_runtime/model_providers/tongyi/llm/llm.py index 0c1f651881..61ebd45ed6 100644 --- a/api/core/model_runtime/model_providers/tongyi/llm/llm.py +++ b/api/core/model_runtime/model_providers/tongyi/llm/llm.py @@ -7,9 +7,9 @@ from http import HTTPStatus from pathlib import Path from typing import Optional, Union, cast -from dashscope import Generation, MultiModalConversation, get_tokenizer -from dashscope.api_entities.dashscope_response import GenerationResponse -from dashscope.common.error import ( +from dashscope import Generation, MultiModalConversation, get_tokenizer # type: ignore +from dashscope.api_entities.dashscope_response import GenerationResponse # type: ignore +from dashscope.common.error import ( # type: ignore AuthenticationError, InvalidParameter, RequestFailure, diff --git a/api/core/model_runtime/model_providers/tongyi/rerank/rerank.py b/api/core/model_runtime/model_providers/tongyi/rerank/rerank.py index a5ce9ead6e..ed682cb0f3 100644 --- a/api/core/model_runtime/model_providers/tongyi/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/tongyi/rerank/rerank.py @@ -1,7 +1,7 @@ from typing import Optional -import dashscope -from dashscope.common.error import ( +import dashscope # type: ignore +from dashscope.common.error import ( # type: ignore AuthenticationError, InvalidParameter, RequestFailure, @@ -51,7 +51,7 @@ class GTERerankModel(RerankModel): :return: rerank result """ if len(docs) == 0: - return RerankResult(model=model, docs=docs) + return RerankResult(model=model, docs=[]) # initialize client dashscope.api_key = credentials["dashscope_api_key"] @@ -64,7 +64,7 @@ class GTERerankModel(RerankModel): return_documents=True, ) - rerank_documents = [] + rerank_documents: list[RerankDocument] = [] if not response.output: return RerankResult(model=model, docs=rerank_documents) for _, result in enumerate(response.output.results): diff --git a/api/core/model_runtime/model_providers/tongyi/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/tongyi/text_embedding/text_embedding.py index 2ef7f3f577..8c53be4130 100644 --- a/api/core/model_runtime/model_providers/tongyi/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/tongyi/text_embedding/text_embedding.py @@ -1,7 +1,7 @@ import time from typing import Optional -import dashscope +import dashscope # type: ignore import numpy as np from core.entities.embedding_type import EmbeddingInputType diff --git a/api/core/model_runtime/model_providers/tongyi/tts/tts.py b/api/core/model_runtime/model_providers/tongyi/tts/tts.py index ca3b9fbc1c..a654e2d760 100644 --- a/api/core/model_runtime/model_providers/tongyi/tts/tts.py +++ b/api/core/model_runtime/model_providers/tongyi/tts/tts.py @@ -2,10 +2,10 @@ import threading from queue import Queue from typing import Any, Optional -import dashscope -from dashscope import SpeechSynthesizer -from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse -from dashscope.audio.tts import ResultCallback, SpeechSynthesisResult +import dashscope # type: ignore +from dashscope import SpeechSynthesizer # type: ignore +from dashscope.api_entities.dashscope_response import SpeechSynthesisResponse # type: ignore +from dashscope.audio.tts import ResultCallback, SpeechSynthesisResult # type: ignore from core.model_runtime.errors.invoke import InvokeBadRequestError from core.model_runtime.errors.validate import CredentialsValidateFailedError diff --git a/api/core/model_runtime/model_providers/upstage/_common.py b/api/core/model_runtime/model_providers/upstage/_common.py index 47ebaccd84..f6609bba77 100644 --- a/api/core/model_runtime/model_providers/upstage/_common.py +++ b/api/core/model_runtime/model_providers/upstage/_common.py @@ -1,5 +1,3 @@ -from collections.abc import Mapping - import openai from httpx import Timeout @@ -14,7 +12,7 @@ from core.model_runtime.errors.invoke import ( class _CommonUpstage: - def _to_credential_kwargs(self, credentials: Mapping) -> dict: + def _to_credential_kwargs(self, credentials: dict) -> dict: """ Transform credentials to kwargs for model instance diff --git a/api/core/model_runtime/model_providers/upstage/llm/llm.py b/api/core/model_runtime/model_providers/upstage/llm/llm.py index a18ee90624..2bf6796ca5 100644 --- a/api/core/model_runtime/model_providers/upstage/llm/llm.py +++ b/api/core/model_runtime/model_providers/upstage/llm/llm.py @@ -6,7 +6,7 @@ from openai import OpenAI, Stream from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageToolCall from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall from openai.types.chat.chat_completion_message import FunctionCall -from tokenizers import Tokenizer +from tokenizers import Tokenizer # type: ignore from core.model_runtime.callbacks.base_callback import Callback from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta diff --git a/api/core/model_runtime/model_providers/upstage/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/upstage/text_embedding/text_embedding.py index 5b340e53bb..87693eca76 100644 --- a/api/core/model_runtime/model_providers/upstage/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/upstage/text_embedding/text_embedding.py @@ -1,11 +1,10 @@ import base64 import time -from collections.abc import Mapping from typing import Union import numpy as np from openai import OpenAI -from tokenizers import Tokenizer +from tokenizers import Tokenizer # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.model_entities import PriceType @@ -132,7 +131,7 @@ class UpstageTextEmbeddingModel(_CommonUpstage, TextEmbeddingModel): return total_num_tokens - def validate_credentials(self, model: str, credentials: Mapping) -> None: + def validate_credentials(self, model: str, credentials: dict) -> None: """ Validate model credentials diff --git a/api/core/model_runtime/model_providers/vertex_ai/_common.py b/api/core/model_runtime/model_providers/vertex_ai/_common.py index 8f7c859e38..4e3df7574e 100644 --- a/api/core/model_runtime/model_providers/vertex_ai/_common.py +++ b/api/core/model_runtime/model_providers/vertex_ai/_common.py @@ -12,4 +12,4 @@ class _CommonVertexAi: :return: Invoke error mapping """ - pass + raise NotImplementedError diff --git a/api/core/model_runtime/model_providers/vertex_ai/llm/llm.py b/api/core/model_runtime/model_providers/vertex_ai/llm/llm.py index c50e0f7946..85be34f3f0 100644 --- a/api/core/model_runtime/model_providers/vertex_ai/llm/llm.py +++ b/api/core/model_runtime/model_providers/vertex_ai/llm/llm.py @@ -6,7 +6,7 @@ import time from collections.abc import Generator from typing import TYPE_CHECKING, Optional, Union, cast -import google.auth.transport.requests +import google.auth.transport.requests # type: ignore import requests from anthropic import AnthropicVertex, Stream from anthropic.types import ( diff --git a/api/core/model_runtime/model_providers/vessl_ai/llm/llm.py b/api/core/model_runtime/model_providers/vessl_ai/llm/llm.py index 034c066ab5..782e4fd623 100644 --- a/api/core/model_runtime/model_providers/vessl_ai/llm/llm.py +++ b/api/core/model_runtime/model_providers/vessl_ai/llm/llm.py @@ -17,14 +17,12 @@ from core.model_runtime.model_providers.openai_api_compatible.llm.llm import OAI class VesslAILargeLanguageModel(OAIAPICompatLargeLanguageModel): def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: - features = [] - entity = AIModelEntity( model=model, label=I18nObject(en_US=model), model_type=ModelType.LLM, fetch_from=FetchFrom.CUSTOMIZABLE_MODEL, - features=features, + features=[], model_properties={ ModelPropertyKey.MODE: credentials.get("mode"), }, diff --git a/api/core/model_runtime/model_providers/volcengine_maas/client.py b/api/core/model_runtime/model_providers/volcengine_maas/client.py index 1cffd902c7..a8a015167e 100644 --- a/api/core/model_runtime/model_providers/volcengine_maas/client.py +++ b/api/core/model_runtime/model_providers/volcengine_maas/client.py @@ -1,8 +1,8 @@ from collections.abc import Generator from typing import Optional, cast -from volcenginesdkarkruntime import Ark -from volcenginesdkarkruntime.types.chat import ( +from volcenginesdkarkruntime import Ark # type: ignore +from volcenginesdkarkruntime.types.chat import ( # type: ignore ChatCompletion, ChatCompletionAssistantMessageParam, ChatCompletionChunk, @@ -15,10 +15,10 @@ from volcenginesdkarkruntime.types.chat import ( ChatCompletionToolParam, ChatCompletionUserMessageParam, ) -from volcenginesdkarkruntime.types.chat.chat_completion_content_part_image_param import ImageURL -from volcenginesdkarkruntime.types.chat.chat_completion_message_tool_call_param import Function -from volcenginesdkarkruntime.types.create_embedding_response import CreateEmbeddingResponse -from volcenginesdkarkruntime.types.shared_params import FunctionDefinition +from volcenginesdkarkruntime.types.chat.chat_completion_content_part_image_param import ImageURL # type: ignore +from volcenginesdkarkruntime.types.chat.chat_completion_message_tool_call_param import Function # type: ignore +from volcenginesdkarkruntime.types.create_embedding_response import CreateEmbeddingResponse # type: ignore +from volcenginesdkarkruntime.types.shared_params import FunctionDefinition # type: ignore from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, diff --git a/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py b/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py index 91dbe21a61..aa837b8318 100644 --- a/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py +++ b/api/core/model_runtime/model_providers/volcengine_maas/legacy/errors.py @@ -152,5 +152,6 @@ ErrorCodeMap = { def wrap_error(e: MaasError) -> Exception: if ErrorCodeMap.get(e.code): - return ErrorCodeMap.get(e.code)(e.code_n, e.code, e.message, e.req_id) + # FIXME: mypy type error, try to fix it instead of using type: ignore + return ErrorCodeMap.get(e.code)(e.code_n, e.code, e.message, e.req_id) # type: ignore return e diff --git a/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py b/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py index 9e19b7deda..f0b2b101b7 100644 --- a/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py +++ b/api/core/model_runtime/model_providers/volcengine_maas/llm/llm.py @@ -2,7 +2,7 @@ import logging from collections.abc import Generator from typing import Optional -from volcenginesdkarkruntime.types.chat import ChatCompletion, ChatCompletionChunk +from volcenginesdkarkruntime.types.chat import ChatCompletion, ChatCompletionChunk # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta diff --git a/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py b/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py index cf3cf23cfb..7c37368086 100644 --- a/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py +++ b/api/core/model_runtime/model_providers/volcengine_maas/llm/models.py @@ -1,3 +1,5 @@ +from typing import Any + from pydantic import BaseModel from core.model_runtime.entities.llm_entities import LLMMode @@ -102,7 +104,7 @@ def get_model_config(credentials: dict) -> ModelConfig: def get_v2_req_params(credentials: dict, model_parameters: dict, stop: list[str] | None = None): - req_params = {} + req_params: dict[str, Any] = {} # predefined properties model_configs = get_model_config(credentials) if model_configs: @@ -130,7 +132,7 @@ def get_v2_req_params(credentials: dict, model_parameters: dict, stop: list[str] def get_v3_req_params(credentials: dict, model_parameters: dict, stop: list[str] | None = None): - req_params = {} + req_params: dict[str, Any] = {} # predefined properties model_configs = get_model_config(credentials) if model_configs: diff --git a/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py b/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py index 07b970f810..d289979569 100644 --- a/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py +++ b/api/core/model_runtime/model_providers/wenxin/llm/ernie_bot.py @@ -1,7 +1,7 @@ from collections.abc import Generator from enum import Enum from json import dumps, loads -from typing import Any, Union +from typing import Any, Optional, Union from requests import Response, post @@ -22,7 +22,7 @@ class ErnieMessage: role: str = Role.USER.value content: str - usage: dict[str, int] = None + usage: Optional[dict[str, int]] = None stop_reason: str = "" def to_dict(self) -> dict[str, Any]: @@ -135,6 +135,7 @@ class ErnieBotModel(_CommonWenxin): """ TODO: implement function calling """ + raise NotImplementedError("Function calling is not supported yet.") def _build_chat_request_body( self, diff --git a/api/core/model_runtime/model_providers/wenxin/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/wenxin/text_embedding/text_embedding.py index 19135deb27..816b3b98c4 100644 --- a/api/core/model_runtime/model_providers/wenxin/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/wenxin/text_embedding/text_embedding.py @@ -1,6 +1,5 @@ import time from abc import abstractmethod -from collections.abc import Mapping from json import dumps from typing import Any, Optional @@ -23,12 +22,12 @@ from core.model_runtime.model_providers.wenxin.wenxin_errors import ( class TextEmbedding: @abstractmethod - def embed_documents(self, model: str, texts: list[str], user: str) -> (list[list[float]], int, int): + def embed_documents(self, model: str, texts: list[str], user: str) -> tuple[list[list[float]], int, int]: raise NotImplementedError class WenxinTextEmbedding(_CommonWenxin, TextEmbedding): - def embed_documents(self, model: str, texts: list[str], user: str) -> (list[list[float]], int, int): + def embed_documents(self, model: str, texts: list[str], user: str) -> tuple[list[list[float]], int, int]: access_token = self._get_access_token() url = f"{self.api_bases[model]}?access_token={access_token}" body = self._build_embed_request_body(model, texts, user) @@ -50,7 +49,7 @@ class WenxinTextEmbedding(_CommonWenxin, TextEmbedding): } return body - def _handle_embed_response(self, model: str, response: Response) -> (list[list[float]], int, int): + def _handle_embed_response(self, model: str, response: Response) -> tuple[list[list[float]], int, int]: data = response.json() if "error_code" in data: code = data["error_code"] @@ -147,7 +146,7 @@ class WenxinTextEmbeddingModel(TextEmbeddingModel): return total_num_tokens - def validate_credentials(self, model: str, credentials: Mapping) -> None: + def validate_credentials(self, model: str, credentials: dict) -> None: api_key = credentials["api_key"] secret_key = credentials["secret_key"] try: diff --git a/api/core/model_runtime/model_providers/xinference/llm/llm.py b/api/core/model_runtime/model_providers/xinference/llm/llm.py index 8d86d6937d..7db1203641 100644 --- a/api/core/model_runtime/model_providers/xinference/llm/llm.py +++ b/api/core/model_runtime/model_providers/xinference/llm/llm.py @@ -17,7 +17,7 @@ from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletio from openai.types.chat.chat_completion_chunk import ChoiceDeltaFunctionCall, ChoiceDeltaToolCall from openai.types.chat.chat_completion_message import FunctionCall from openai.types.completion import Completion -from xinference_client.client.restful.restful_client import ( +from xinference_client.client.restful.restful_client import ( # type: ignore Client, RESTfulChatModelHandle, RESTfulGenerateModelHandle, diff --git a/api/core/model_runtime/model_providers/xinference/rerank/rerank.py b/api/core/model_runtime/model_providers/xinference/rerank/rerank.py index efaf114854..078ec0537a 100644 --- a/api/core/model_runtime/model_providers/xinference/rerank/rerank.py +++ b/api/core/model_runtime/model_providers/xinference/rerank/rerank.py @@ -1,6 +1,6 @@ from typing import Optional -from xinference_client.client.restful.restful_client import Client, RESTfulRerankModelHandle +from xinference_client.client.restful.restful_client import Client, RESTfulRerankModelHandle # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType diff --git a/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py b/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py index 3d7aefeb6d..5f330ece1a 100644 --- a/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py +++ b/api/core/model_runtime/model_providers/xinference/speech2text/speech2text.py @@ -1,6 +1,6 @@ from typing import IO, Optional -from xinference_client.client.restful.restful_client import Client, RESTfulAudioModelHandle +from xinference_client.client.restful.restful_client import Client, RESTfulAudioModelHandle # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType diff --git a/api/core/model_runtime/model_providers/xinference/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/xinference/text_embedding/text_embedding.py index e51e6a941c..9054aabab2 100644 --- a/api/core/model_runtime/model_providers/xinference/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/xinference/text_embedding/text_embedding.py @@ -1,7 +1,7 @@ import time from typing import Optional -from xinference_client.client.restful.restful_client import Client, RESTfulEmbeddingModelHandle +from xinference_client.client.restful.restful_client import Client, RESTfulEmbeddingModelHandle # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.common_entities import I18nObject @@ -134,7 +134,7 @@ class XinferenceTextEmbeddingModel(TextEmbeddingModel): try: handle = client.get_model(model_uid=model_uid) except RuntimeError as e: - raise InvokeAuthorizationError(e) + raise InvokeAuthorizationError(str(e)) if not isinstance(handle, RESTfulEmbeddingModelHandle): raise InvokeBadRequestError( diff --git a/api/core/model_runtime/model_providers/xinference/tts/tts.py b/api/core/model_runtime/model_providers/xinference/tts/tts.py index ad7b64efb5..8aa39d4de0 100644 --- a/api/core/model_runtime/model_providers/xinference/tts/tts.py +++ b/api/core/model_runtime/model_providers/xinference/tts/tts.py @@ -1,7 +1,7 @@ import concurrent.futures from typing import Any, Optional -from xinference_client.client.restful.restful_client import RESTfulAudioModelHandle +from xinference_client.client.restful.restful_client import RESTfulAudioModelHandle # type: ignore from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import AIModelEntity, FetchFrom, ModelType @@ -74,11 +74,14 @@ class XinferenceText2SpeechModel(TTSModel): raise CredentialsValidateFailedError("model_uid should not contain /, ?, or #") credentials["server_url"] = credentials["server_url"].removesuffix("/") + api_key = credentials.get("api_key") + if api_key is None: + raise CredentialsValidateFailedError("api_key is required") extra_param = XinferenceHelper.get_xinference_extra_parameter( server_url=credentials["server_url"], model_uid=credentials["model_uid"], - api_key=credentials.get("api_key"), + api_key=api_key, ) if "text-to-audio" not in extra_param.model_ability: diff --git a/api/core/model_runtime/model_providers/xinference/xinference_helper.py b/api/core/model_runtime/model_providers/xinference/xinference_helper.py index baa3ccbe8a..b51423f4ed 100644 --- a/api/core/model_runtime/model_providers/xinference/xinference_helper.py +++ b/api/core/model_runtime/model_providers/xinference/xinference_helper.py @@ -1,6 +1,6 @@ from threading import Lock from time import time -from typing import Optional +from typing import Any, Optional from requests.adapters import HTTPAdapter from requests.exceptions import ConnectionError, MissingSchema, Timeout @@ -39,13 +39,15 @@ class XinferenceModelExtraParameter: self.model_family = model_family -cache = {} +cache: dict[str, dict[str, Any]] = {} cache_lock = Lock() class XinferenceHelper: @staticmethod - def get_xinference_extra_parameter(server_url: str, model_uid: str, api_key: str) -> XinferenceModelExtraParameter: + def get_xinference_extra_parameter( + server_url: str, model_uid: str, api_key: str | None + ) -> XinferenceModelExtraParameter: XinferenceHelper._clean_cache() with cache_lock: if model_uid not in cache: @@ -66,7 +68,9 @@ class XinferenceHelper: pass @staticmethod - def _get_xinference_extra_parameter(server_url: str, model_uid: str, api_key: str) -> XinferenceModelExtraParameter: + def _get_xinference_extra_parameter( + server_url: str, model_uid: str, api_key: str | None + ) -> XinferenceModelExtraParameter: """ get xinference model extra parameter like model_format and model_handle_type """ diff --git a/api/core/model_runtime/model_providers/yi/llm/llm.py b/api/core/model_runtime/model_providers/yi/llm/llm.py index 0642e72ed5..f5b61e2076 100644 --- a/api/core/model_runtime/model_providers/yi/llm/llm.py +++ b/api/core/model_runtime/model_providers/yi/llm/llm.py @@ -136,7 +136,7 @@ class YiLargeLanguageModel(OpenAILargeLanguageModel): parsed_url = urlparse(credentials["endpoint_url"]) credentials["openai_api_base"] = f"{parsed_url.scheme}://{parsed_url.netloc}" - def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity | None: + def get_customizable_model_schema(self, model: str, credentials: dict) -> AIModelEntity: return AIModelEntity( model=model, label=I18nObject(en_US=model, zh_Hans=model), diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py b/api/core/model_runtime/model_providers/zhipuai/llm/llm.py index 59861507e4..eef86cc52c 100644 --- a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py +++ b/api/core/model_runtime/model_providers/zhipuai/llm/llm.py @@ -1,9 +1,9 @@ from collections.abc import Generator from typing import Optional, Union -from zhipuai import ZhipuAI -from zhipuai.types.chat.chat_completion import Completion -from zhipuai.types.chat.chat_completion_chunk import ChatCompletionChunk +from zhipuai import ZhipuAI # type: ignore +from zhipuai.types.chat.chat_completion import Completion # type: ignore +from zhipuai.types.chat.chat_completion_chunk import ChatCompletionChunk # type: ignore from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta from core.model_runtime.entities.message_entities import ( diff --git a/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.py b/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.py index 2428284ba9..a700304db7 100644 --- a/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.py +++ b/api/core/model_runtime/model_providers/zhipuai/text_embedding/text_embedding.py @@ -1,7 +1,7 @@ import time from typing import Optional -from zhipuai import ZhipuAI +from zhipuai import ZhipuAI # type: ignore from core.entities.embedding_type import EmbeddingInputType from core.model_runtime.entities.model_entities import PriceType diff --git a/api/core/model_runtime/schema_validators/common_validator.py b/api/core/model_runtime/schema_validators/common_validator.py index 029ec1a581..8cc8adfc36 100644 --- a/api/core/model_runtime/schema_validators/common_validator.py +++ b/api/core/model_runtime/schema_validators/common_validator.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Union, cast from core.model_runtime.entities.provider_entities import CredentialFormSchema, FormType @@ -38,7 +38,7 @@ class CommonValidator: def _validate_credential_form_schema( self, credential_form_schema: CredentialFormSchema, credentials: dict - ) -> Optional[str]: + ) -> Union[str, bool, None]: """ Validate credential form schema @@ -47,6 +47,7 @@ class CommonValidator: :return: validated credential form schema value """ # If the variable does not exist in credentials + value: Union[str, bool, None] = None if credential_form_schema.variable not in credentials or not credentials[credential_form_schema.variable]: # If required is True, an exception is thrown if credential_form_schema.required: @@ -61,7 +62,7 @@ class CommonValidator: return None # Get the value corresponding to the variable from credentials - value = credentials[credential_form_schema.variable] + value = cast(str, credentials[credential_form_schema.variable]) # If max_length=0, no validation is performed if credential_form_schema.max_length: diff --git a/api/core/model_runtime/utils/encoders.py b/api/core/model_runtime/utils/encoders.py index ec1bad5698..03e3506271 100644 --- a/api/core/model_runtime/utils/encoders.py +++ b/api/core/model_runtime/utils/encoders.py @@ -129,7 +129,8 @@ def jsonable_encoder( sqlalchemy_safe=sqlalchemy_safe, ) if dataclasses.is_dataclass(obj): - obj_dict = dataclasses.asdict(obj) + # FIXME: mypy error, try to fix it instead of using type: ignore + obj_dict = dataclasses.asdict(obj) # type: ignore return jsonable_encoder( obj_dict, by_alias=by_alias, diff --git a/api/core/model_runtime/utils/helper.py b/api/core/model_runtime/utils/helper.py index 2067092d80..5e8a723ec7 100644 --- a/api/core/model_runtime/utils/helper.py +++ b/api/core/model_runtime/utils/helper.py @@ -4,6 +4,7 @@ from pydantic import BaseModel def dump_model(model: BaseModel) -> dict: if hasattr(pydantic, "model_dump"): - return pydantic.model_dump(model) + # FIXME mypy error, try to fix it instead of using type: ignore + return pydantic.model_dump(model) # type: ignore else: return model.model_dump() diff --git a/api/core/moderation/api/api.py b/api/core/moderation/api/api.py index 094ad78636..c65a3885fd 100644 --- a/api/core/moderation/api/api.py +++ b/api/core/moderation/api/api.py @@ -1,3 +1,5 @@ +from typing import Optional + from pydantic import BaseModel from core.extension.api_based_extension_requestor import APIBasedExtensionPoint, APIBasedExtensionRequestor @@ -43,6 +45,8 @@ class ApiModeration(Moderation): def moderation_for_inputs(self, inputs: dict, query: str = "") -> ModerationInputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["inputs_config"]["enabled"]: params = ModerationInputParams(app_id=self.app_id, inputs=inputs, query=query) @@ -57,6 +61,8 @@ class ApiModeration(Moderation): def moderation_for_outputs(self, text: str) -> ModerationOutputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["outputs_config"]["enabled"]: params = ModerationOutputParams(app_id=self.app_id, text=text) @@ -69,14 +75,18 @@ class ApiModeration(Moderation): ) def _get_config_by_requestor(self, extension_point: APIBasedExtensionPoint, params: dict) -> dict: - extension = self._get_api_based_extension(self.tenant_id, self.config.get("api_based_extension_id")) + if self.config is None: + raise ValueError("The config is not set.") + extension = self._get_api_based_extension(self.tenant_id, self.config.get("api_based_extension_id", "")) + if not extension: + raise ValueError("API-based Extension not found. Please check it again.") requestor = APIBasedExtensionRequestor(extension.api_endpoint, decrypt_token(self.tenant_id, extension.api_key)) result = requestor.request(extension_point, params) return result @staticmethod - def _get_api_based_extension(tenant_id: str, api_based_extension_id: str) -> APIBasedExtension: + def _get_api_based_extension(tenant_id: str, api_based_extension_id: str) -> Optional[APIBasedExtension]: extension = ( db.session.query(APIBasedExtension) .filter(APIBasedExtension.tenant_id == tenant_id, APIBasedExtension.id == api_based_extension_id) diff --git a/api/core/moderation/base.py b/api/core/moderation/base.py index 60898d5547..d8c392d097 100644 --- a/api/core/moderation/base.py +++ b/api/core/moderation/base.py @@ -100,14 +100,14 @@ class Moderation(Extensible, ABC): if not inputs_config.get("preset_response"): raise ValueError("inputs_config.preset_response is required") - if len(inputs_config.get("preset_response")) > 100: + if len(inputs_config.get("preset_response", 0)) > 100: raise ValueError("inputs_config.preset_response must be less than 100 characters") if outputs_config_enabled: if not outputs_config.get("preset_response"): raise ValueError("outputs_config.preset_response is required") - if len(outputs_config.get("preset_response")) > 100: + if len(outputs_config.get("preset_response", 0)) > 100: raise ValueError("outputs_config.preset_response must be less than 100 characters") diff --git a/api/core/moderation/factory.py b/api/core/moderation/factory.py index 96bf2ab54b..0ad4438c14 100644 --- a/api/core/moderation/factory.py +++ b/api/core/moderation/factory.py @@ -22,7 +22,8 @@ class ModerationFactory: """ code_based_extension.validate_form_schema(ExtensionModule.MODERATION, name, config) extension_class = code_based_extension.extension_class(ExtensionModule.MODERATION, name) - extension_class.validate_config(tenant_id, config) + # FIXME: mypy error, try to fix it instead of using type: ignore + extension_class.validate_config(tenant_id, config) # type: ignore def moderation_for_inputs(self, inputs: dict, query: str = "") -> ModerationInputsResult: """ diff --git a/api/core/moderation/input_moderation.py b/api/core/moderation/input_moderation.py index 46d3963bd0..3ac33966cb 100644 --- a/api/core/moderation/input_moderation.py +++ b/api/core/moderation/input_moderation.py @@ -1,5 +1,6 @@ import logging -from typing import Optional +from collections.abc import Mapping +from typing import Any, Optional from core.app.app_config.entities import AppConfig from core.moderation.base import ModerationAction, ModerationError @@ -17,11 +18,11 @@ class InputModeration: app_id: str, tenant_id: str, app_config: AppConfig, - inputs: dict, + inputs: Mapping[str, Any], query: str, message_id: str, trace_manager: Optional[TraceQueueManager] = None, - ) -> tuple[bool, dict, str]: + ) -> tuple[bool, Mapping[str, Any], str]: """ Process sensitive_word_avoidance. :param app_id: app id @@ -33,6 +34,7 @@ class InputModeration: :param trace_manager: trace manager :return: """ + inputs = dict(inputs) if not app_config.sensitive_word_avoidance: return False, inputs, query diff --git a/api/core/moderation/keywords/keywords.py b/api/core/moderation/keywords/keywords.py index 00b3c56c03..9dd2665c3b 100644 --- a/api/core/moderation/keywords/keywords.py +++ b/api/core/moderation/keywords/keywords.py @@ -21,7 +21,7 @@ class KeywordsModeration(Moderation): if not config.get("keywords"): raise ValueError("keywords is required") - if len(config.get("keywords")) > 10000: + if len(config.get("keywords", [])) > 10000: raise ValueError("keywords length must be less than 10000") keywords_row_len = config["keywords"].split("\n") @@ -31,6 +31,8 @@ class KeywordsModeration(Moderation): def moderation_for_inputs(self, inputs: dict, query: str = "") -> ModerationInputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["inputs_config"]["enabled"]: preset_response = self.config["inputs_config"]["preset_response"] @@ -50,6 +52,8 @@ class KeywordsModeration(Moderation): def moderation_for_outputs(self, text: str) -> ModerationOutputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["outputs_config"]["enabled"]: # Filter out empty values diff --git a/api/core/moderation/openai_moderation/openai_moderation.py b/api/core/moderation/openai_moderation/openai_moderation.py index 6465de23b9..d64f17b383 100644 --- a/api/core/moderation/openai_moderation/openai_moderation.py +++ b/api/core/moderation/openai_moderation/openai_moderation.py @@ -20,6 +20,8 @@ class OpenAIModeration(Moderation): def moderation_for_inputs(self, inputs: dict, query: str = "") -> ModerationInputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["inputs_config"]["enabled"]: preset_response = self.config["inputs_config"]["preset_response"] @@ -35,6 +37,8 @@ class OpenAIModeration(Moderation): def moderation_for_outputs(self, text: str) -> ModerationOutputsResult: flagged = False preset_response = "" + if self.config is None: + raise ValueError("The config is not set.") if self.config["outputs_config"]["enabled"]: flagged = self._is_violated({"text": text}) diff --git a/api/core/moderation/output_moderation.py b/api/core/moderation/output_moderation.py index 4635bd9c25..e595be126c 100644 --- a/api/core/moderation/output_moderation.py +++ b/api/core/moderation/output_moderation.py @@ -70,7 +70,7 @@ class OutputModeration(BaseModel): thread = threading.Thread( target=self.worker, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "buffer_size": buffer_size if buffer_size > 0 else dify_config.MODERATION_BUFFER_SIZE, }, ) diff --git a/api/core/ops/entities/trace_entity.py b/api/core/ops/entities/trace_entity.py index 71ff03b6ef..f0e34c0cd7 100644 --- a/api/core/ops/entities/trace_entity.py +++ b/api/core/ops/entities/trace_entity.py @@ -1,3 +1,4 @@ +from collections.abc import Mapping from datetime import datetime from enum import StrEnum from typing import Any, Optional, Union @@ -38,8 +39,8 @@ class WorkflowTraceInfo(BaseTraceInfo): workflow_run_id: str workflow_run_elapsed_time: Union[int, float] workflow_run_status: str - workflow_run_inputs: dict[str, Any] - workflow_run_outputs: dict[str, Any] + workflow_run_inputs: Mapping[str, Any] + workflow_run_outputs: Mapping[str, Any] workflow_run_version: str error: Optional[str] = None total_tokens: int diff --git a/api/core/ops/langfuse_trace/langfuse_trace.py b/api/core/ops/langfuse_trace/langfuse_trace.py index 29fdebd8fe..b9ba068b19 100644 --- a/api/core/ops/langfuse_trace/langfuse_trace.py +++ b/api/core/ops/langfuse_trace/langfuse_trace.py @@ -77,8 +77,8 @@ class LangFuseDataTrace(BaseTraceInstance): id=trace_id, user_id=user_id, name=name, - input=trace_info.workflow_run_inputs, - output=trace_info.workflow_run_outputs, + input=dict(trace_info.workflow_run_inputs), + output=dict(trace_info.workflow_run_outputs), metadata=metadata, session_id=trace_info.conversation_id, tags=["message", "workflow"], @@ -87,8 +87,8 @@ class LangFuseDataTrace(BaseTraceInstance): workflow_span_data = LangfuseSpan( id=trace_info.workflow_run_id, name=TraceTaskName.WORKFLOW_TRACE.value, - input=trace_info.workflow_run_inputs, - output=trace_info.workflow_run_outputs, + input=dict(trace_info.workflow_run_inputs), + output=dict(trace_info.workflow_run_outputs), trace_id=trace_id, start_time=trace_info.start_time, end_time=trace_info.end_time, @@ -102,8 +102,8 @@ class LangFuseDataTrace(BaseTraceInstance): id=trace_id, user_id=user_id, name=TraceTaskName.WORKFLOW_TRACE.value, - input=trace_info.workflow_run_inputs, - output=trace_info.workflow_run_outputs, + input=dict(trace_info.workflow_run_inputs), + output=dict(trace_info.workflow_run_outputs), metadata=metadata, session_id=trace_info.conversation_id, tags=["workflow"], diff --git a/api/core/ops/langsmith_trace/entities/langsmith_trace_entity.py b/api/core/ops/langsmith_trace/entities/langsmith_trace_entity.py index 99221d669b..348b7ba501 100644 --- a/api/core/ops/langsmith_trace/entities/langsmith_trace_entity.py +++ b/api/core/ops/langsmith_trace/entities/langsmith_trace_entity.py @@ -49,7 +49,6 @@ class LangSmithRunModel(LangSmithTokenUsage, LangSmithMultiModel): reference_example_id: Optional[str] = Field(None, description="Reference example ID associated with the run") input_attachments: Optional[dict[str, Any]] = Field(None, description="Input attachments of the run") output_attachments: Optional[dict[str, Any]] = Field(None, description="Output attachments of the run") - dotted_order: Optional[str] = Field(None, description="Dotted order of the run") @field_validator("inputs", "outputs") @classmethod diff --git a/api/core/ops/langsmith_trace/langsmith_trace.py b/api/core/ops/langsmith_trace/langsmith_trace.py index 672843e5a8..4ffd888bdd 100644 --- a/api/core/ops/langsmith_trace/langsmith_trace.py +++ b/api/core/ops/langsmith_trace/langsmith_trace.py @@ -3,6 +3,7 @@ import logging import os import uuid from datetime import datetime, timedelta +from typing import Optional, cast from langsmith import Client from langsmith.schemas import RunBase @@ -63,6 +64,8 @@ class LangSmithDataTrace(BaseTraceInstance): def workflow_trace(self, trace_info: WorkflowTraceInfo): trace_id = trace_info.message_id or trace_info.workflow_run_id + if trace_info.start_time is None: + trace_info.start_time = datetime.now() message_dotted_order = ( generate_dotted_order(trace_info.message_id, trace_info.start_time) if trace_info.message_id else None ) @@ -78,8 +81,8 @@ class LangSmithDataTrace(BaseTraceInstance): message_run = LangSmithRunModel( id=trace_info.message_id, name=TraceTaskName.MESSAGE_TRACE.value, - inputs=trace_info.workflow_run_inputs, - outputs=trace_info.workflow_run_outputs, + inputs=dict(trace_info.workflow_run_inputs), + outputs=dict(trace_info.workflow_run_outputs), run_type=LangSmithRunType.chain, start_time=trace_info.start_time, end_time=trace_info.end_time, @@ -90,6 +93,15 @@ class LangSmithDataTrace(BaseTraceInstance): error=trace_info.error, trace_id=trace_id, dotted_order=message_dotted_order, + file_list=[], + serialized=None, + parent_run_id=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, ) self.add_run(message_run) @@ -98,11 +110,11 @@ class LangSmithDataTrace(BaseTraceInstance): total_tokens=trace_info.total_tokens, id=trace_info.workflow_run_id, name=TraceTaskName.WORKFLOW_TRACE.value, - inputs=trace_info.workflow_run_inputs, + inputs=dict(trace_info.workflow_run_inputs), run_type=LangSmithRunType.tool, start_time=trace_info.workflow_data.created_at, end_time=trace_info.workflow_data.finished_at, - outputs=trace_info.workflow_run_outputs, + outputs=dict(trace_info.workflow_run_outputs), extra={ "metadata": metadata, }, @@ -111,6 +123,13 @@ class LangSmithDataTrace(BaseTraceInstance): parent_run_id=trace_info.message_id or None, trace_id=trace_id, dotted_order=workflow_dotted_order, + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, ) self.add_run(langsmith_run) @@ -211,25 +230,35 @@ class LangSmithDataTrace(BaseTraceInstance): id=node_execution_id, trace_id=trace_id, dotted_order=node_dotted_order, + error="", + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, ) self.add_run(langsmith_run) def message_trace(self, trace_info: MessageTraceInfo): # get message file data - file_list = trace_info.file_list - message_file_data: MessageFile = trace_info.message_file_data + file_list = cast(list[str], trace_info.file_list) or [] + message_file_data: Optional[MessageFile] = trace_info.message_file_data file_url = f"{self.file_base_url}/{message_file_data.url}" if message_file_data else "" file_list.append(file_url) metadata = trace_info.metadata message_data = trace_info.message_data + if message_data is None: + return message_id = message_data.id user_id = message_data.from_account_id metadata["user_id"] = user_id if message_data.from_end_user_id: - end_user_data: EndUser = ( + end_user_data: Optional[EndUser] = ( db.session.query(EndUser).filter(EndUser.id == message_data.from_end_user_id).first() ) if end_user_data is not None: @@ -247,12 +276,20 @@ class LangSmithDataTrace(BaseTraceInstance): start_time=trace_info.start_time, end_time=trace_info.end_time, outputs=message_data.answer, - extra={ - "metadata": metadata, - }, + extra={"metadata": metadata}, tags=["message", str(trace_info.conversation_mode)], error=trace_info.error, file_list=file_list, + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + parent_run_id=None, ) self.add_run(message_run) @@ -267,17 +304,27 @@ class LangSmithDataTrace(BaseTraceInstance): start_time=trace_info.start_time, end_time=trace_info.end_time, outputs=message_data.answer, - extra={ - "metadata": metadata, - }, + extra={"metadata": metadata}, parent_run_id=message_id, tags=["llm", str(trace_info.conversation_mode)], error=trace_info.error, file_list=file_list, + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + id=str(uuid.uuid4()), ) self.add_run(llm_run) def moderation_trace(self, trace_info: ModerationTraceInfo): + if trace_info.message_data is None: + return langsmith_run = LangSmithRunModel( name=TraceTaskName.MODERATION_TRACE.value, inputs=trace_info.inputs, @@ -288,48 +335,82 @@ class LangSmithDataTrace(BaseTraceInstance): "inputs": trace_info.inputs, }, run_type=LangSmithRunType.tool, - extra={ - "metadata": trace_info.metadata, - }, + extra={"metadata": trace_info.metadata}, tags=["moderation"], parent_run_id=trace_info.message_id, start_time=trace_info.start_time or trace_info.message_data.created_at, end_time=trace_info.end_time or trace_info.message_data.updated_at, + id=str(uuid.uuid4()), + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + error="", + file_list=[], ) self.add_run(langsmith_run) def suggested_question_trace(self, trace_info: SuggestedQuestionTraceInfo): message_data = trace_info.message_data + if message_data is None: + return suggested_question_run = LangSmithRunModel( name=TraceTaskName.SUGGESTED_QUESTION_TRACE.value, inputs=trace_info.inputs, outputs=trace_info.suggested_question, run_type=LangSmithRunType.tool, - extra={ - "metadata": trace_info.metadata, - }, + extra={"metadata": trace_info.metadata}, tags=["suggested_question"], parent_run_id=trace_info.message_id, start_time=trace_info.start_time or message_data.created_at, end_time=trace_info.end_time or message_data.updated_at, + id=str(uuid.uuid4()), + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + error="", + file_list=[], ) self.add_run(suggested_question_run) def dataset_retrieval_trace(self, trace_info: DatasetRetrievalTraceInfo): + if trace_info.message_data is None: + return dataset_retrieval_run = LangSmithRunModel( name=TraceTaskName.DATASET_RETRIEVAL_TRACE.value, inputs=trace_info.inputs, outputs={"documents": trace_info.documents}, run_type=LangSmithRunType.retriever, - extra={ - "metadata": trace_info.metadata, - }, + extra={"metadata": trace_info.metadata}, tags=["dataset_retrieval"], parent_run_id=trace_info.message_id, start_time=trace_info.start_time or trace_info.message_data.created_at, end_time=trace_info.end_time or trace_info.message_data.updated_at, + id=str(uuid.uuid4()), + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + error="", + file_list=[], ) self.add_run(dataset_retrieval_run) @@ -347,7 +428,18 @@ class LangSmithDataTrace(BaseTraceInstance): parent_run_id=trace_info.message_id, start_time=trace_info.start_time, end_time=trace_info.end_time, - file_list=[trace_info.file_url], + file_list=[cast(str, trace_info.file_url)], + id=str(uuid.uuid4()), + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + error=trace_info.error or "", ) self.add_run(tool_run) @@ -358,12 +450,23 @@ class LangSmithDataTrace(BaseTraceInstance): inputs=trace_info.inputs, outputs=trace_info.outputs, run_type=LangSmithRunType.tool, - extra={ - "metadata": trace_info.metadata, - }, + extra={"metadata": trace_info.metadata}, tags=["generate_name"], start_time=trace_info.start_time or datetime.now(), end_time=trace_info.end_time or datetime.now(), + id=str(uuid.uuid4()), + serialized=None, + events=[], + session_id=None, + session_name=None, + reference_example_id=None, + input_attachments={}, + output_attachments={}, + trace_id=None, + dotted_order=None, + error="", + file_list=[], + parent_run_id=None, ) self.add_run(name_run) diff --git a/api/core/ops/ops_trace_manager.py b/api/core/ops/ops_trace_manager.py index 4f41b6ed97..f538eaef5b 100644 --- a/api/core/ops/ops_trace_manager.py +++ b/api/core/ops/ops_trace_manager.py @@ -33,11 +33,11 @@ from core.ops.langsmith_trace.langsmith_trace import LangSmithDataTrace from core.ops.utils import get_message_data from extensions.ext_database import db from extensions.ext_storage import storage -from models.model import App, AppModelConfig, Conversation, Message, MessageAgentThought, MessageFile, TraceAppConfig +from models.model import App, AppModelConfig, Conversation, Message, MessageFile, TraceAppConfig from models.workflow import WorkflowAppLog, WorkflowRun from tasks.ops_trace_task import process_trace_tasks -provider_config_map = { +provider_config_map: dict[str, dict[str, Any]] = { TracingProviderEnum.LANGFUSE.value: { "config_class": LangfuseConfig, "secret_keys": ["public_key", "secret_key"], @@ -145,7 +145,7 @@ class OpsTraceManager: :param tracing_provider: tracing provider :return: """ - trace_config_data: TraceAppConfig = ( + trace_config_data: Optional[TraceAppConfig] = ( db.session.query(TraceAppConfig) .filter(TraceAppConfig.app_id == app_id, TraceAppConfig.tracing_provider == tracing_provider) .first() @@ -155,7 +155,11 @@ class OpsTraceManager: return None # decrypt_token - tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id + app = db.session.query(App).filter(App.id == app_id).first() + if not app: + raise ValueError("App not found") + + tenant_id = app.tenant_id decrypt_tracing_config = cls.decrypt_tracing_config( tenant_id, tracing_provider, trace_config_data.tracing_config ) @@ -178,7 +182,7 @@ class OpsTraceManager: if app_id is None: return None - app: App = db.session.query(App).filter(App.id == app_id).first() + app: Optional[App] = db.session.query(App).filter(App.id == app_id).first() if app is None: return None @@ -209,8 +213,12 @@ class OpsTraceManager: def get_app_config_through_message_id(cls, message_id: str): app_model_config = None message_data = db.session.query(Message).filter(Message.id == message_id).first() + if not message_data: + return None conversation_id = message_data.conversation_id conversation_data = db.session.query(Conversation).filter(Conversation.id == conversation_id).first() + if not conversation_data: + return None if conversation_data.app_model_config_id: app_model_config = ( @@ -236,7 +244,9 @@ class OpsTraceManager: if tracing_provider not in provider_config_map and tracing_provider is not None: raise ValueError(f"Invalid tracing provider: {tracing_provider}") - app_config: App = db.session.query(App).filter(App.id == app_id).first() + app_config: Optional[App] = db.session.query(App).filter(App.id == app_id).first() + if not app_config: + raise ValueError("App not found") app_config.tracing = json.dumps( { "enabled": enabled, @@ -252,7 +262,9 @@ class OpsTraceManager: :param app_id: app id :return: """ - app: App = db.session.query(App).filter(App.id == app_id).first() + app: Optional[App] = db.session.query(App).filter(App.id == app_id).first() + if not app: + raise ValueError("App not found") if not app.tracing: return {"enabled": False, "tracing_provider": None} app_trace_config = json.loads(app.tracing) @@ -483,6 +495,8 @@ class TraceTask: def moderation_trace(self, message_id, timer, **kwargs): moderation_result = kwargs.get("moderation_result") + if not moderation_result: + return {} inputs = kwargs.get("inputs") message_data = get_message_data(message_id) if not message_data: @@ -518,7 +532,7 @@ class TraceTask: return moderation_trace_info def suggested_question_trace(self, message_id, timer, **kwargs): - suggested_question = kwargs.get("suggested_question") + suggested_question = kwargs.get("suggested_question", []) message_data = get_message_data(message_id) if not message_data: return {} @@ -586,7 +600,7 @@ class TraceTask: dataset_retrieval_trace_info = DatasetRetrievalTraceInfo( message_id=message_id, inputs=message_data.query or message_data.inputs, - documents=[doc.model_dump() for doc in documents], + documents=[doc.model_dump() for doc in documents] if documents else [], start_time=timer.get("start"), end_time=timer.get("end"), metadata=metadata, @@ -596,9 +610,9 @@ class TraceTask: return dataset_retrieval_trace_info def tool_trace(self, message_id, timer, **kwargs): - tool_name = kwargs.get("tool_name") - tool_inputs = kwargs.get("tool_inputs") - tool_outputs = kwargs.get("tool_outputs") + tool_name = kwargs.get("tool_name", "") + tool_inputs = kwargs.get("tool_inputs", {}) + tool_outputs = kwargs.get("tool_outputs", {}) message_data = get_message_data(message_id) if not message_data: return {} @@ -608,7 +622,7 @@ class TraceTask: tool_parameters = {} created_time = message_data.created_at end_time = message_data.updated_at - agent_thoughts: list[MessageAgentThought] = message_data.agent_thoughts + agent_thoughts = message_data.agent_thoughts for agent_thought in agent_thoughts: if tool_name in agent_thought.tools: created_time = agent_thought.created_at @@ -672,6 +686,8 @@ class TraceTask: generate_conversation_name = kwargs.get("generate_conversation_name") inputs = kwargs.get("inputs") tenant_id = kwargs.get("tenant_id") + if not tenant_id: + return {} start_time = timer.get("start") end_time = timer.get("end") @@ -693,8 +709,8 @@ class TraceTask: return generate_name_trace_info -trace_manager_timer = None -trace_manager_queue = queue.Queue() +trace_manager_timer: Optional[threading.Timer] = None +trace_manager_queue: queue.Queue = queue.Queue() trace_manager_interval = int(os.getenv("TRACE_QUEUE_MANAGER_INTERVAL", 5)) trace_manager_batch_size = int(os.getenv("TRACE_QUEUE_MANAGER_BATCH_SIZE", 100)) @@ -706,7 +722,7 @@ class TraceQueueManager: self.app_id = app_id self.user_id = user_id self.trace_instance = OpsTraceManager.get_ops_trace_instance(app_id) - self.flask_app = current_app._get_current_object() + self.flask_app = current_app._get_current_object() # type: ignore if trace_manager_timer is None: self.start_timer() @@ -723,7 +739,7 @@ class TraceQueueManager: def collect_tasks(self): global trace_manager_queue - tasks = [] + tasks: list[TraceTask] = [] while len(tasks) < trace_manager_batch_size and not trace_manager_queue.empty(): task = trace_manager_queue.get_nowait() tasks.append(task) @@ -749,6 +765,8 @@ class TraceQueueManager: def send_to_celery(self, tasks: list[TraceTask]): with self.flask_app.app_context(): for task in tasks: + if task.app_id is None: + continue file_id = uuid4().hex trace_info = task.execute() task_data = TaskData( diff --git a/api/core/prompt/advanced_prompt_transform.py b/api/core/prompt/advanced_prompt_transform.py index 0f3f824966..87c7a79fb0 100644 --- a/api/core/prompt/advanced_prompt_transform.py +++ b/api/core/prompt/advanced_prompt_transform.py @@ -1,5 +1,5 @@ -from collections.abc import Sequence -from typing import Optional +from collections.abc import Mapping, Sequence +from typing import Optional, cast from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.file import file_manager @@ -39,7 +39,7 @@ class AdvancedPromptTransform(PromptTransform): self, *, prompt_template: Sequence[ChatModelMessage] | CompletionModelPromptTemplate, - inputs: dict[str, str], + inputs: Mapping[str, str], query: str, files: Sequence[File], context: Optional[str], @@ -77,7 +77,7 @@ class AdvancedPromptTransform(PromptTransform): def _get_completion_model_prompt_messages( self, prompt_template: CompletionModelPromptTemplate, - inputs: dict, + inputs: Mapping[str, str], query: Optional[str], files: Sequence[File], context: Optional[str], @@ -90,15 +90,15 @@ class AdvancedPromptTransform(PromptTransform): """ raw_prompt = prompt_template.text - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] if prompt_template.edition_type == "basic" or not prompt_template.edition_type: parser = PromptTemplateParser(template=raw_prompt, with_variable_tmpl=self.with_variable_tmpl) - prompt_inputs = {k: inputs[k] for k in parser.variable_keys if k in inputs} + prompt_inputs: Mapping[str, str] = {k: inputs[k] for k in parser.variable_keys if k in inputs} prompt_inputs = self._set_context_variable(context, parser, prompt_inputs) - if memory and memory_config: + if memory and memory_config and memory_config.role_prefix: role_prefix = memory_config.role_prefix prompt_inputs = self._set_histories_variable( memory=memory, @@ -135,7 +135,7 @@ class AdvancedPromptTransform(PromptTransform): def _get_chat_model_prompt_messages( self, prompt_template: list[ChatModelMessage], - inputs: dict, + inputs: Mapping[str, str], query: Optional[str], files: Sequence[File], context: Optional[str], @@ -146,7 +146,7 @@ class AdvancedPromptTransform(PromptTransform): """ Get chat model prompt messages. """ - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] for prompt_item in prompt_template: raw_prompt = prompt_item.text @@ -160,7 +160,7 @@ class AdvancedPromptTransform(PromptTransform): prompt = vp.convert_template(raw_prompt).text else: parser = PromptTemplateParser(template=raw_prompt, with_variable_tmpl=self.with_variable_tmpl) - prompt_inputs = {k: inputs[k] for k in parser.variable_keys if k in inputs} + prompt_inputs: Mapping[str, str] = {k: inputs[k] for k in parser.variable_keys if k in inputs} prompt_inputs = self._set_context_variable( context=context, parser=parser, prompt_inputs=prompt_inputs ) @@ -207,7 +207,7 @@ class AdvancedPromptTransform(PromptTransform): last_message = prompt_messages[-1] if prompt_messages else None if last_message and last_message.role == PromptMessageRole.USER: # get last user message content and add files - prompt_message_contents = [TextPromptMessageContent(data=last_message.content)] + prompt_message_contents = [TextPromptMessageContent(data=cast(str, last_message.content))] for file in files: prompt_message_contents.append(file_manager.to_prompt_message_content(file)) @@ -229,7 +229,10 @@ class AdvancedPromptTransform(PromptTransform): return prompt_messages - def _set_context_variable(self, context: str | None, parser: PromptTemplateParser, prompt_inputs: dict) -> dict: + def _set_context_variable( + self, context: str | None, parser: PromptTemplateParser, prompt_inputs: Mapping[str, str] + ) -> Mapping[str, str]: + prompt_inputs = dict(prompt_inputs) if "#context#" in parser.variable_keys: if context: prompt_inputs["#context#"] = context @@ -238,7 +241,10 @@ class AdvancedPromptTransform(PromptTransform): return prompt_inputs - def _set_query_variable(self, query: str, parser: PromptTemplateParser, prompt_inputs: dict) -> dict: + def _set_query_variable( + self, query: str, parser: PromptTemplateParser, prompt_inputs: Mapping[str, str] + ) -> Mapping[str, str]: + prompt_inputs = dict(prompt_inputs) if "#query#" in parser.variable_keys: if query: prompt_inputs["#query#"] = query @@ -254,9 +260,10 @@ class AdvancedPromptTransform(PromptTransform): raw_prompt: str, role_prefix: MemoryConfig.RolePrefix, parser: PromptTemplateParser, - prompt_inputs: dict, + prompt_inputs: Mapping[str, str], model_config: ModelConfigWithCredentialsEntity, - ) -> dict: + ) -> Mapping[str, str]: + prompt_inputs = dict(prompt_inputs) if "#histories#" in parser.variable_keys: if memory: inputs = {"#histories#": "", **prompt_inputs} diff --git a/api/core/prompt/agent_history_prompt_transform.py b/api/core/prompt/agent_history_prompt_transform.py index caa1793ea8..09f017a7db 100644 --- a/api/core/prompt/agent_history_prompt_transform.py +++ b/api/core/prompt/agent_history_prompt_transform.py @@ -31,7 +31,7 @@ class AgentHistoryPromptTransform(PromptTransform): self.memory = memory def get_prompt(self) -> list[PromptMessage]: - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] num_system = 0 for prompt_message in self.history_messages: if isinstance(prompt_message, SystemPromptMessage): diff --git a/api/core/prompt/prompt_transform.py b/api/core/prompt/prompt_transform.py index 87acdb3c49..1f040599be 100644 --- a/api/core/prompt/prompt_transform.py +++ b/api/core/prompt/prompt_transform.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Any, Optional from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.memory.token_buffer_memory import TokenBufferMemory @@ -42,7 +42,7 @@ class PromptTransform: ): max_tokens = ( model_config.parameters.get(parameter_rule.name) - or model_config.parameters.get(parameter_rule.use_template) + or model_config.parameters.get(parameter_rule.use_template or "") ) or 0 rest_tokens = model_context_tokens - max_tokens - curr_message_tokens @@ -59,7 +59,7 @@ class PromptTransform: ai_prefix: Optional[str] = None, ) -> str: """Get memory messages.""" - kwargs = {"max_token_limit": max_token_limit} + kwargs: dict[str, Any] = {"max_token_limit": max_token_limit} if human_prefix: kwargs["human_prefix"] = human_prefix @@ -76,11 +76,15 @@ class PromptTransform: self, memory: TokenBufferMemory, memory_config: MemoryConfig, max_token_limit: int ) -> list[PromptMessage]: """Get memory messages.""" - return memory.get_history_prompt_messages( - max_token_limit=max_token_limit, - message_limit=memory_config.window.size - if ( - memory_config.window.enabled and memory_config.window.size is not None and memory_config.window.size > 0 + return list( + memory.get_history_prompt_messages( + max_token_limit=max_token_limit, + message_limit=memory_config.window.size + if ( + memory_config.window.enabled + and memory_config.window.size is not None + and memory_config.window.size > 0 + ) + else None, ) - else None, ) diff --git a/api/core/prompt/simple_prompt_transform.py b/api/core/prompt/simple_prompt_transform.py index 93dd92f188..e75877de9b 100644 --- a/api/core/prompt/simple_prompt_transform.py +++ b/api/core/prompt/simple_prompt_transform.py @@ -1,7 +1,8 @@ import enum import json import os -from typing import TYPE_CHECKING, Optional +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Any, Optional, cast from core.app.app_config.entities import PromptTemplateEntity from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity @@ -41,7 +42,7 @@ class ModelMode(enum.StrEnum): raise ValueError(f"invalid mode value {value}") -prompt_file_contents = {} +prompt_file_contents: dict[str, Any] = {} class SimplePromptTransform(PromptTransform): @@ -53,9 +54,9 @@ class SimplePromptTransform(PromptTransform): self, app_mode: AppMode, prompt_template_entity: PromptTemplateEntity, - inputs: dict, + inputs: Mapping[str, str], query: str, - files: list["File"], + files: Sequence["File"], context: Optional[str], memory: Optional[TokenBufferMemory], model_config: ModelConfigWithCredentialsEntity, @@ -66,7 +67,7 @@ class SimplePromptTransform(PromptTransform): if model_mode == ModelMode.CHAT: prompt_messages, stops = self._get_chat_model_prompt_messages( app_mode=app_mode, - pre_prompt=prompt_template_entity.simple_prompt_template, + pre_prompt=prompt_template_entity.simple_prompt_template or "", inputs=inputs, query=query, files=files, @@ -77,7 +78,7 @@ class SimplePromptTransform(PromptTransform): else: prompt_messages, stops = self._get_completion_model_prompt_messages( app_mode=app_mode, - pre_prompt=prompt_template_entity.simple_prompt_template, + pre_prompt=prompt_template_entity.simple_prompt_template or "", inputs=inputs, query=query, files=files, @@ -171,11 +172,11 @@ class SimplePromptTransform(PromptTransform): inputs: dict, query: str, context: Optional[str], - files: list["File"], + files: Sequence["File"], memory: Optional[TokenBufferMemory], model_config: ModelConfigWithCredentialsEntity, ) -> tuple[list[PromptMessage], Optional[list[str]]]: - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] # get prompt prompt, _ = self.get_prompt_str_and_rules( @@ -216,7 +217,7 @@ class SimplePromptTransform(PromptTransform): inputs: dict, query: str, context: Optional[str], - files: list["File"], + files: Sequence["File"], memory: Optional[TokenBufferMemory], model_config: ModelConfigWithCredentialsEntity, ) -> tuple[list[PromptMessage], Optional[list[str]]]: @@ -263,7 +264,7 @@ class SimplePromptTransform(PromptTransform): return [self.get_last_user_message(prompt, files)], stops - def get_last_user_message(self, prompt: str, files: list["File"]) -> UserPromptMessage: + def get_last_user_message(self, prompt: str, files: Sequence["File"]) -> UserPromptMessage: if files: prompt_message_contents: list[PromptMessageContent] = [] prompt_message_contents.append(TextPromptMessageContent(data=prompt)) @@ -288,7 +289,7 @@ class SimplePromptTransform(PromptTransform): # Check if the prompt file is already loaded if prompt_file_name in prompt_file_contents: - return prompt_file_contents[prompt_file_name] + return cast(dict, prompt_file_contents[prompt_file_name]) # Get the absolute path of the subdirectory prompt_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "prompt_templates") @@ -301,7 +302,7 @@ class SimplePromptTransform(PromptTransform): # Store the content of the prompt file prompt_file_contents[prompt_file_name] = content - return content + return cast(dict, content) def _prompt_file_name(self, app_mode: AppMode, provider: str, model: str) -> str: # baichuan diff --git a/api/core/prompt/utils/prompt_message_util.py b/api/core/prompt/utils/prompt_message_util.py index aa175153bc..2f4e651461 100644 --- a/api/core/prompt/utils/prompt_message_util.py +++ b/api/core/prompt/utils/prompt_message_util.py @@ -1,5 +1,5 @@ from collections.abc import Sequence -from typing import cast +from typing import Any, cast from core.model_runtime.entities import ( AssistantPromptMessage, @@ -72,7 +72,7 @@ class PromptMessageUtil: } ) else: - text = prompt_message.content + text = cast(str, prompt_message.content) prompt = {"role": role, "text": text, "files": files} @@ -99,9 +99,9 @@ class PromptMessageUtil: } ) else: - text = prompt_message.content + text = cast(str, prompt_message.content) - params = { + params: dict[str, Any] = { "role": "user", "text": text, } diff --git a/api/core/prompt/utils/prompt_template_parser.py b/api/core/prompt/utils/prompt_template_parser.py index 0fd08c5d3c..8e40674bc1 100644 --- a/api/core/prompt/utils/prompt_template_parser.py +++ b/api/core/prompt/utils/prompt_template_parser.py @@ -1,4 +1,5 @@ import re +from collections.abc import Mapping REGEX = re.compile(r"\{\{([a-zA-Z_][a-zA-Z0-9_]{0,29}|#histories#|#query#|#context#)\}\}") WITH_VARIABLE_TMPL_REGEX = re.compile( @@ -28,7 +29,7 @@ class PromptTemplateParser: # Regular expression to match the template rules return re.findall(self.regex, self.template) - def format(self, inputs: dict, remove_template_variables: bool = True) -> str: + def format(self, inputs: Mapping[str, str], remove_template_variables: bool = True) -> str: def replacer(match): key = match.group(1) value = inputs.get(key, match.group(0)) # return original matched string if key not found diff --git a/api/core/provider_manager.py b/api/core/provider_manager.py index 3a1fe300df..010abd12d2 100644 --- a/api/core/provider_manager.py +++ b/api/core/provider_manager.py @@ -1,7 +1,7 @@ import json from collections import defaultdict from json import JSONDecodeError -from typing import Optional +from typing import Optional, cast from sqlalchemy.exc import IntegrityError @@ -15,6 +15,7 @@ from core.entities.provider_entities import ( ModelLoadBalancingConfiguration, ModelSettings, QuotaConfiguration, + QuotaUnit, SystemConfiguration, ) from core.helper import encrypter @@ -116,8 +117,8 @@ class ProviderManager: for provider_entity in provider_entities: # handle include, exclude if is_filtered( - include_set=dify_config.POSITION_PROVIDER_INCLUDES_SET, - exclude_set=dify_config.POSITION_PROVIDER_EXCLUDES_SET, + include_set=cast(set[str], dify_config.POSITION_PROVIDER_INCLUDES_SET), + exclude_set=cast(set[str], dify_config.POSITION_PROVIDER_EXCLUDES_SET), data=provider_entity, name_func=lambda x: x.provider, ): @@ -490,12 +491,13 @@ class ProviderManager: # Init trial provider records if not exists if ProviderQuotaType.TRIAL not in provider_quota_to_provider_record_dict: try: + # FIXME ignore the type errork, onyl TrialHostingQuota has limit need to change the logic provider_record = Provider( tenant_id=tenant_id, provider_name=provider_name, provider_type=ProviderType.SYSTEM.value, quota_type=ProviderQuotaType.TRIAL.value, - quota_limit=quota.quota_limit, + quota_limit=quota.quota_limit, # type: ignore quota_used=0, is_valid=True, ) @@ -589,7 +591,9 @@ class ProviderManager: if variable in provider_credentials: try: provider_credentials[variable] = encrypter.decrypt_token_with_decoding( - provider_credentials.get(variable), self.decoding_rsa_key, self.decoding_cipher_rsa + provider_credentials.get(variable) or "", # type: ignore + self.decoding_rsa_key, + self.decoding_cipher_rsa, ) except ValueError: pass @@ -671,13 +675,9 @@ class ProviderManager: # Get hosting configuration hosting_configuration = ext_hosting_provider.hosting_configuration - if ( - provider_entity.provider not in hosting_configuration.provider_map - or not hosting_configuration.provider_map.get(provider_entity.provider).enabled - ): - return SystemConfiguration(enabled=False) - provider_hosting_configuration = hosting_configuration.provider_map.get(provider_entity.provider) + if provider_hosting_configuration is None or not provider_hosting_configuration.enabled: + return SystemConfiguration(enabled=False) # Convert provider_records to dict quota_type_to_provider_records_dict = {} @@ -688,14 +688,13 @@ class ProviderManager: quota_type_to_provider_records_dict[ProviderQuotaType.value_of(provider_record.quota_type)] = ( provider_record ) - quota_configurations = [] for provider_quota in provider_hosting_configuration.quotas: if provider_quota.quota_type not in quota_type_to_provider_records_dict: if provider_quota.quota_type == ProviderQuotaType.FREE: quota_configuration = QuotaConfiguration( quota_type=provider_quota.quota_type, - quota_unit=provider_hosting_configuration.quota_unit, + quota_unit=provider_hosting_configuration.quota_unit or QuotaUnit.TOKENS, quota_used=0, quota_limit=0, is_valid=False, @@ -708,7 +707,7 @@ class ProviderManager: quota_configuration = QuotaConfiguration( quota_type=provider_quota.quota_type, - quota_unit=provider_hosting_configuration.quota_unit, + quota_unit=provider_hosting_configuration.quota_unit or QuotaUnit.TOKENS, quota_used=provider_record.quota_used, quota_limit=provider_record.quota_limit, is_valid=provider_record.quota_limit > provider_record.quota_used @@ -725,12 +724,12 @@ class ProviderManager: current_using_credentials = provider_hosting_configuration.credentials if current_quota_type == ProviderQuotaType.FREE: - provider_record = quota_type_to_provider_records_dict.get(current_quota_type) + provider_record_quota_free = quota_type_to_provider_records_dict.get(current_quota_type) - if provider_record: + if provider_record_quota_free: provider_credentials_cache = ProviderCredentialsCache( tenant_id=tenant_id, - identity_id=provider_record.id, + identity_id=provider_record_quota_free.id, cache_type=ProviderCredentialsCacheType.PROVIDER, ) @@ -763,7 +762,7 @@ class ProviderManager: except ValueError: pass - current_using_credentials = provider_credentials + current_using_credentials = provider_credentials or {} # cache provider credentials provider_credentials_cache.set(credentials=current_using_credentials) @@ -842,7 +841,7 @@ class ProviderManager: else [] ) - model_settings = [] + model_settings: list[ModelSettings] = [] if not provider_model_settings: return model_settings diff --git a/api/core/rag/datasource/keyword/jieba/jieba.py b/api/core/rag/datasource/keyword/jieba/jieba.py index a0153c1e58..95a2316f1d 100644 --- a/api/core/rag/datasource/keyword/jieba/jieba.py +++ b/api/core/rag/datasource/keyword/jieba/jieba.py @@ -32,8 +32,11 @@ class Jieba(BaseKeyword): keywords = keyword_table_handler.extract_keywords( text.page_content, self._config.max_keywords_per_chunk ) - self._update_segment_keywords(self.dataset.id, text.metadata["doc_id"], list(keywords)) - keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata["doc_id"], list(keywords)) + if text.metadata is not None: + self._update_segment_keywords(self.dataset.id, text.metadata["doc_id"], list(keywords)) + keyword_table = self._add_text_to_keyword_table( + keyword_table or {}, text.metadata["doc_id"], list(keywords) + ) self._save_dataset_keyword_table(keyword_table) @@ -58,20 +61,26 @@ class Jieba(BaseKeyword): keywords = keyword_table_handler.extract_keywords( text.page_content, self._config.max_keywords_per_chunk ) - self._update_segment_keywords(self.dataset.id, text.metadata["doc_id"], list(keywords)) - keyword_table = self._add_text_to_keyword_table(keyword_table, text.metadata["doc_id"], list(keywords)) + if text.metadata is not None: + self._update_segment_keywords(self.dataset.id, text.metadata["doc_id"], list(keywords)) + keyword_table = self._add_text_to_keyword_table( + keyword_table or {}, text.metadata["doc_id"], list(keywords) + ) self._save_dataset_keyword_table(keyword_table) def text_exists(self, id: str) -> bool: keyword_table = self._get_dataset_keyword_table() + if keyword_table is None: + return False return id in set.union(*keyword_table.values()) def delete_by_ids(self, ids: list[str]) -> None: lock_name = "keyword_indexing_lock_{}".format(self.dataset.id) with redis_client.lock(lock_name, timeout=600): keyword_table = self._get_dataset_keyword_table() - keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids) + if keyword_table is not None: + keyword_table = self._delete_ids_from_keyword_table(keyword_table, ids) self._save_dataset_keyword_table(keyword_table) @@ -80,7 +89,7 @@ class Jieba(BaseKeyword): k = kwargs.get("top_k", 4) - sorted_chunk_indices = self._retrieve_ids_by_query(keyword_table, query, k) + sorted_chunk_indices = self._retrieve_ids_by_query(keyword_table or {}, query, k) documents = [] for chunk_index in sorted_chunk_indices: @@ -137,7 +146,7 @@ class Jieba(BaseKeyword): if dataset_keyword_table: keyword_table_dict = dataset_keyword_table.keyword_table_dict if keyword_table_dict: - return keyword_table_dict["__data__"]["table"] + return dict(keyword_table_dict["__data__"]["table"]) else: keyword_data_source_type = dify_config.KEYWORD_DATA_SOURCE_TYPE dataset_keyword_table = DatasetKeywordTable( @@ -188,8 +197,8 @@ class Jieba(BaseKeyword): # go through text chunks in order of most matching keywords chunk_indices_count: dict[str, int] = defaultdict(int) - keywords = [keyword for keyword in keywords if keyword in set(keyword_table.keys())] - for keyword in keywords: + keywords_list = [keyword for keyword in keywords if keyword in set(keyword_table.keys())] + for keyword in keywords_list: for node_id in keyword_table[keyword]: chunk_indices_count[node_id] += 1 @@ -215,7 +224,7 @@ class Jieba(BaseKeyword): def create_segment_keywords(self, node_id: str, keywords: list[str]): keyword_table = self._get_dataset_keyword_table() self._update_segment_keywords(self.dataset.id, node_id, keywords) - keyword_table = self._add_text_to_keyword_table(keyword_table, node_id, keywords) + keyword_table = self._add_text_to_keyword_table(keyword_table or {}, node_id, keywords) self._save_dataset_keyword_table(keyword_table) def multi_create_segment_keywords(self, pre_segment_data_list: list): @@ -226,17 +235,19 @@ class Jieba(BaseKeyword): if pre_segment_data["keywords"]: segment.keywords = pre_segment_data["keywords"] keyword_table = self._add_text_to_keyword_table( - keyword_table, segment.index_node_id, pre_segment_data["keywords"] + keyword_table or {}, segment.index_node_id, pre_segment_data["keywords"] ) else: keywords = keyword_table_handler.extract_keywords(segment.content, self._config.max_keywords_per_chunk) segment.keywords = list(keywords) - keyword_table = self._add_text_to_keyword_table(keyword_table, segment.index_node_id, list(keywords)) + keyword_table = self._add_text_to_keyword_table( + keyword_table or {}, segment.index_node_id, list(keywords) + ) self._save_dataset_keyword_table(keyword_table) def update_segment_keywords_index(self, node_id: str, keywords: list[str]): keyword_table = self._get_dataset_keyword_table() - keyword_table = self._add_text_to_keyword_table(keyword_table, node_id, keywords) + keyword_table = self._add_text_to_keyword_table(keyword_table or {}, node_id, keywords) self._save_dataset_keyword_table(keyword_table) diff --git a/api/core/rag/datasource/keyword/jieba/jieba_keyword_table_handler.py b/api/core/rag/datasource/keyword/jieba/jieba_keyword_table_handler.py index ec809cf325..8b17e8dc0a 100644 --- a/api/core/rag/datasource/keyword/jieba/jieba_keyword_table_handler.py +++ b/api/core/rag/datasource/keyword/jieba/jieba_keyword_table_handler.py @@ -4,7 +4,7 @@ from typing import Optional class JiebaKeywordTableHandler: def __init__(self): - import jieba.analyse + import jieba.analyse # type: ignore from core.rag.datasource.keyword.jieba.stopwords import STOPWORDS @@ -12,7 +12,7 @@ class JiebaKeywordTableHandler: def extract_keywords(self, text: str, max_keywords_per_chunk: Optional[int] = 10) -> set[str]: """Extract keywords with JIEBA tfidf.""" - import jieba + import jieba # type: ignore keywords = jieba.analyse.extract_tags( sentence=text, diff --git a/api/core/rag/datasource/keyword/keyword_base.py b/api/core/rag/datasource/keyword/keyword_base.py index be00687abd..b261b40b72 100644 --- a/api/core/rag/datasource/keyword/keyword_base.py +++ b/api/core/rag/datasource/keyword/keyword_base.py @@ -37,6 +37,8 @@ class BaseKeyword(ABC): def _filter_duplicate_texts(self, texts: list[Document]) -> list[Document]: for text in texts.copy(): + if text.metadata is None: + continue doc_id = text.metadata["doc_id"] exists_duplicate_node = self.text_exists(doc_id) if exists_duplicate_node: @@ -45,4 +47,4 @@ class BaseKeyword(ABC): return texts def _get_uuids(self, texts: list[Document]) -> list[str]: - return [text.metadata["doc_id"] for text in texts] + return [text.metadata["doc_id"] for text in texts if text.metadata] diff --git a/api/core/rag/datasource/retrieval_service.py b/api/core/rag/datasource/retrieval_service.py index 18f8d4e839..34343ad60e 100644 --- a/api/core/rag/datasource/retrieval_service.py +++ b/api/core/rag/datasource/retrieval_service.py @@ -6,6 +6,7 @@ from flask import Flask, current_app from core.rag.data_post_processor.data_post_processor import DataPostProcessor from core.rag.datasource.keyword.keyword_factory import Keyword from core.rag.datasource.vdb.vector_factory import Vector +from core.rag.models.document import Document from core.rag.rerank.rerank_type import RerankMode from core.rag.retrieval.retrieval_methods import RetrievalMethod from extensions.ext_database import db @@ -31,7 +32,7 @@ class RetrievalService: top_k: int, score_threshold: Optional[float] = 0.0, reranking_model: Optional[dict] = None, - reranking_mode: Optional[str] = "reranking_model", + reranking_mode: str = "reranking_model", weights: Optional[dict] = None, ): if not query: @@ -42,15 +43,15 @@ class RetrievalService: if not dataset or dataset.available_document_count == 0 or dataset.available_segment_count == 0: return [] - all_documents = [] - threads = [] - exceptions = [] + all_documents: list[Document] = [] + threads: list[threading.Thread] = [] + exceptions: list[str] = [] # retrieval_model source with keyword if retrieval_method == "keyword_search": keyword_thread = threading.Thread( target=RetrievalService.keyword_search, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "dataset_id": dataset_id, "query": query, "top_k": top_k, @@ -65,7 +66,7 @@ class RetrievalService: embedding_thread = threading.Thread( target=RetrievalService.embedding_search, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "dataset_id": dataset_id, "query": query, "top_k": top_k, @@ -84,7 +85,7 @@ class RetrievalService: full_text_index_thread = threading.Thread( target=RetrievalService.full_text_index_search, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "dataset_id": dataset_id, "query": query, "retrieval_method": retrieval_method, @@ -124,7 +125,7 @@ class RetrievalService: if not dataset: return [] all_documents = ExternalDatasetService.fetch_external_knowledge_retrieval( - dataset.tenant_id, dataset_id, query, external_retrieval_model + dataset.tenant_id, dataset_id, query, external_retrieval_model or {} ) return all_documents @@ -135,6 +136,8 @@ class RetrievalService: with flask_app.app_context(): try: dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if not dataset: + raise ValueError("dataset not found") keyword = Keyword(dataset=dataset) @@ -159,6 +162,8 @@ class RetrievalService: with flask_app.app_context(): try: dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if not dataset: + raise ValueError("dataset not found") vector = Vector(dataset=dataset) @@ -209,6 +214,8 @@ class RetrievalService: with flask_app.app_context(): try: dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if not dataset: + raise ValueError("dataset not found") vector_processor = Vector( dataset=dataset, diff --git a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector.py b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector.py index 09104ae422..603d3fdbcd 100644 --- a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector.py +++ b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector.py @@ -17,12 +17,19 @@ from models.dataset import Dataset class AnalyticdbVector(BaseVector): def __init__( - self, collection_name: str, api_config: AnalyticdbVectorOpenAPIConfig, sql_config: AnalyticdbVectorBySqlConfig + self, + collection_name: str, + api_config: AnalyticdbVectorOpenAPIConfig | None, + sql_config: AnalyticdbVectorBySqlConfig | None, ): super().__init__(collection_name) if api_config is not None: - self.analyticdb_vector = AnalyticdbVectorOpenAPI(collection_name, api_config) + self.analyticdb_vector: AnalyticdbVectorOpenAPI | AnalyticdbVectorBySql = AnalyticdbVectorOpenAPI( + collection_name, api_config + ) else: + if sql_config is None: + raise ValueError("Either api_config or sql_config must be provided") self.analyticdb_vector = AnalyticdbVectorBySql(collection_name, sql_config) def get_type(self) -> str: @@ -33,8 +40,8 @@ class AnalyticdbVector(BaseVector): self.analyticdb_vector._create_collection_if_not_exists(dimension) self.analyticdb_vector.add_texts(texts, embeddings) - def add_texts(self, texts: list[Document], embeddings: list[list[float]], **kwargs): - self.analyticdb_vector.add_texts(texts, embeddings) + def add_texts(self, documents: list[Document], embeddings: list[list[float]], **kwargs): + self.analyticdb_vector.add_texts(documents, embeddings) def text_exists(self, id: str) -> bool: return self.analyticdb_vector.text_exists(id) @@ -68,13 +75,13 @@ class AnalyticdbVectorFactory(AbstractVectorFactory): if dify_config.ANALYTICDB_HOST is None: # implemented through OpenAPI apiConfig = AnalyticdbVectorOpenAPIConfig( - access_key_id=dify_config.ANALYTICDB_KEY_ID, - access_key_secret=dify_config.ANALYTICDB_KEY_SECRET, - region_id=dify_config.ANALYTICDB_REGION_ID, - instance_id=dify_config.ANALYTICDB_INSTANCE_ID, - account=dify_config.ANALYTICDB_ACCOUNT, - account_password=dify_config.ANALYTICDB_PASSWORD, - namespace=dify_config.ANALYTICDB_NAMESPACE, + access_key_id=dify_config.ANALYTICDB_KEY_ID or "", + access_key_secret=dify_config.ANALYTICDB_KEY_SECRET or "", + region_id=dify_config.ANALYTICDB_REGION_ID or "", + instance_id=dify_config.ANALYTICDB_INSTANCE_ID or "", + account=dify_config.ANALYTICDB_ACCOUNT or "", + account_password=dify_config.ANALYTICDB_PASSWORD or "", + namespace=dify_config.ANALYTICDB_NAMESPACE or "", namespace_password=dify_config.ANALYTICDB_NAMESPACE_PASSWORD, ) sqlConfig = None @@ -83,11 +90,11 @@ class AnalyticdbVectorFactory(AbstractVectorFactory): sqlConfig = AnalyticdbVectorBySqlConfig( host=dify_config.ANALYTICDB_HOST, port=dify_config.ANALYTICDB_PORT, - account=dify_config.ANALYTICDB_ACCOUNT, - account_password=dify_config.ANALYTICDB_PASSWORD, + account=dify_config.ANALYTICDB_ACCOUNT or "", + account_password=dify_config.ANALYTICDB_PASSWORD or "", min_connection=dify_config.ANALYTICDB_MIN_CONNECTION, max_connection=dify_config.ANALYTICDB_MAX_CONNECTION, - namespace=dify_config.ANALYTICDB_NAMESPACE, + namespace=dify_config.ANALYTICDB_NAMESPACE or "", ) apiConfig = None return AnalyticdbVector( diff --git a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_openapi.py b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_openapi.py index 05e0ebc54f..095752ea8e 100644 --- a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_openapi.py +++ b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_openapi.py @@ -1,5 +1,5 @@ import json -from typing import Any +from typing import Any, Optional from pydantic import BaseModel, model_validator @@ -20,7 +20,7 @@ class AnalyticdbVectorOpenAPIConfig(BaseModel): account: str account_password: str namespace: str = "dify" - namespace_password: str = (None,) + namespace_password: Optional[str] = None metrics: str = "cosine" read_timeout: int = 60000 @@ -55,8 +55,8 @@ class AnalyticdbVectorOpenAPIConfig(BaseModel): class AnalyticdbVectorOpenAPI: def __init__(self, collection_name: str, config: AnalyticdbVectorOpenAPIConfig): try: - from alibabacloud_gpdb20160503.client import Client - from alibabacloud_tea_openapi import models as open_api_models + from alibabacloud_gpdb20160503.client import Client # type: ignore + from alibabacloud_tea_openapi import models as open_api_models # type: ignore except: raise ImportError(_import_err_msg) self._collection_name = collection_name.lower() @@ -77,7 +77,7 @@ class AnalyticdbVectorOpenAPI: redis_client.set(database_exist_cache_key, 1, ex=3600) def _initialize_vector_database(self) -> None: - from alibabacloud_gpdb20160503 import models as gpdb_20160503_models + from alibabacloud_gpdb20160503 import models as gpdb_20160503_models # type: ignore request = gpdb_20160503_models.InitVectorDatabaseRequest( dbinstance_id=self.config.instance_id, @@ -89,7 +89,7 @@ class AnalyticdbVectorOpenAPI: def _create_namespace_if_not_exists(self) -> None: from alibabacloud_gpdb20160503 import models as gpdb_20160503_models - from Tea.exceptions import TeaException + from Tea.exceptions import TeaException # type: ignore try: request = gpdb_20160503_models.DescribeNamespaceRequest( @@ -159,17 +159,18 @@ class AnalyticdbVectorOpenAPI: rows: list[gpdb_20160503_models.UpsertCollectionDataRequestRows] = [] for doc, embedding in zip(documents, embeddings, strict=True): - metadata = { - "ref_doc_id": doc.metadata["doc_id"], - "page_content": doc.page_content, - "metadata_": json.dumps(doc.metadata), - } - rows.append( - gpdb_20160503_models.UpsertCollectionDataRequestRows( - vector=embedding, - metadata=metadata, + if doc.metadata is not None: + metadata = { + "ref_doc_id": doc.metadata["doc_id"], + "page_content": doc.page_content, + "metadata_": json.dumps(doc.metadata), + } + rows.append( + gpdb_20160503_models.UpsertCollectionDataRequestRows( + vector=embedding, + metadata=metadata, + ) ) - ) request = gpdb_20160503_models.UpsertCollectionDataRequest( dbinstance_id=self.config.instance_id, region_id=self.config.region_id, @@ -258,7 +259,7 @@ class AnalyticdbVectorOpenAPI: metadata=metadata, ) documents.append(doc) - documents = sorted(documents, key=lambda x: x.metadata["score"], reverse=True) + documents = sorted(documents, key=lambda x: x.metadata["score"] if x.metadata else 0, reverse=True) return documents def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: @@ -290,7 +291,7 @@ class AnalyticdbVectorOpenAPI: metadata=metadata, ) documents.append(doc) - documents = sorted(documents, key=lambda x: x.metadata["score"], reverse=True) + documents = sorted(documents, key=lambda x: x.metadata["score"] if x.metadata else 0, reverse=True) return documents def delete(self) -> None: diff --git a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py index e474db5cb2..4d8f792941 100644 --- a/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py +++ b/api/core/rag/datasource/vdb/analyticdb/analyticdb_vector_sql.py @@ -3,8 +3,8 @@ import uuid from contextlib import contextmanager from typing import Any -import psycopg2.extras -import psycopg2.pool +import psycopg2.extras # type: ignore +import psycopg2.pool # type: ignore from pydantic import BaseModel, model_validator from core.rag.models.document import Document @@ -75,6 +75,7 @@ class AnalyticdbVectorBySql: @contextmanager def _get_cursor(self): + assert self.pool is not None, "Connection pool is not initialized" conn = self.pool.getconn() cur = conn.cursor() try: @@ -156,16 +157,17 @@ class AnalyticdbVectorBySql: VALUES (%s, %s, %s, %s, %s, to_tsvector('zh_cn', %s)); """ for i, doc in enumerate(documents): - values.append( - ( - id_prefix + str(i), - doc.metadata.get("doc_id", str(uuid.uuid4())), - embeddings[i], - doc.page_content, - json.dumps(doc.metadata), - doc.page_content, + if doc.metadata is not None: + values.append( + ( + id_prefix + str(i), + doc.metadata.get("doc_id", str(uuid.uuid4())), + embeddings[i], + doc.page_content, + json.dumps(doc.metadata), + doc.page_content, + ) ) - ) with self._get_cursor() as cur: psycopg2.extras.execute_batch(cur, sql, values) diff --git a/api/core/rag/datasource/vdb/baidu/baidu_vector.py b/api/core/rag/datasource/vdb/baidu/baidu_vector.py index eb78e8aa69..85596ad20e 100644 --- a/api/core/rag/datasource/vdb/baidu/baidu_vector.py +++ b/api/core/rag/datasource/vdb/baidu/baidu_vector.py @@ -5,13 +5,13 @@ from typing import Any import numpy as np from pydantic import BaseModel, model_validator -from pymochow import MochowClient -from pymochow.auth.bce_credentials import BceCredentials -from pymochow.configuration import Configuration -from pymochow.exception import ServerError -from pymochow.model.enum import FieldType, IndexState, IndexType, MetricType, ServerErrCode, TableState -from pymochow.model.schema import Field, HNSWParams, Schema, VectorIndex -from pymochow.model.table import AnnSearch, HNSWSearchParams, Partition, Row +from pymochow import MochowClient # type: ignore +from pymochow.auth.bce_credentials import BceCredentials # type: ignore +from pymochow.configuration import Configuration # type: ignore +from pymochow.exception import ServerError # type: ignore +from pymochow.model.enum import FieldType, IndexState, IndexType, MetricType, ServerErrCode, TableState # type: ignore +from pymochow.model.schema import Field, HNSWParams, Schema, VectorIndex # type: ignore +from pymochow.model.table import AnnSearch, HNSWSearchParams, Partition, Row # type: ignore from configs import dify_config from core.rag.datasource.vdb.vector_base import BaseVector @@ -75,7 +75,7 @@ class BaiduVector(BaseVector): def add_texts(self, documents: list[Document], embeddings: list[list[float]], **kwargs): texts = [doc.page_content for doc in documents] - metadatas = [doc.metadata for doc in documents] + metadatas = [doc.metadata for doc in documents if doc.metadata is not None] total_count = len(documents) batch_size = 1000 @@ -84,6 +84,8 @@ class BaiduVector(BaseVector): for start in range(0, total_count, batch_size): end = min(start + batch_size, total_count) rows = [] + assert len(metadatas) == total_count, "metadatas length should be equal to total_count" + # FIXME do you need this assert? for i in range(start, end, 1): row = Row( id=metadatas[i].get("doc_id", str(uuid.uuid4())), @@ -136,7 +138,7 @@ class BaiduVector(BaseVector): # baidu vector database doesn't support bm25 search on current version return [] - def _get_search_res(self, res, score_threshold): + def _get_search_res(self, res, score_threshold) -> list[Document]: docs = [] for row in res.rows: row_data = row.get("row", {}) @@ -276,11 +278,11 @@ class BaiduVectorFactory(AbstractVectorFactory): return BaiduVector( collection_name=collection_name, config=BaiduConfig( - endpoint=dify_config.BAIDU_VECTOR_DB_ENDPOINT, + endpoint=dify_config.BAIDU_VECTOR_DB_ENDPOINT or "", connection_timeout_in_mills=dify_config.BAIDU_VECTOR_DB_CONNECTION_TIMEOUT_MS, - account=dify_config.BAIDU_VECTOR_DB_ACCOUNT, - api_key=dify_config.BAIDU_VECTOR_DB_API_KEY, - database=dify_config.BAIDU_VECTOR_DB_DATABASE, + account=dify_config.BAIDU_VECTOR_DB_ACCOUNT or "", + api_key=dify_config.BAIDU_VECTOR_DB_API_KEY or "", + database=dify_config.BAIDU_VECTOR_DB_DATABASE or "", shard=dify_config.BAIDU_VECTOR_DB_SHARD, replicas=dify_config.BAIDU_VECTOR_DB_REPLICAS, ), diff --git a/api/core/rag/datasource/vdb/chroma/chroma_vector.py b/api/core/rag/datasource/vdb/chroma/chroma_vector.py index a9e1486edd..0eab01b507 100644 --- a/api/core/rag/datasource/vdb/chroma/chroma_vector.py +++ b/api/core/rag/datasource/vdb/chroma/chroma_vector.py @@ -71,11 +71,13 @@ class ChromaVector(BaseVector): metadatas = [d.metadata for d in documents] collection = self._client.get_or_create_collection(self._collection_name) - collection.upsert(ids=uuids, documents=texts, embeddings=embeddings, metadatas=metadatas) + # FIXME: chromadb using numpy array, fix the type error later + collection.upsert(ids=uuids, documents=texts, embeddings=embeddings, metadatas=metadatas) # type: ignore def delete_by_metadata_field(self, key: str, value: str): collection = self._client.get_or_create_collection(self._collection_name) - collection.delete(where={key: {"$eq": value}}) + # FIXME: fix the type error later + collection.delete(where={key: {"$eq": value}}) # type: ignore def delete(self): self._client.delete_collection(self._collection_name) @@ -94,15 +96,19 @@ class ChromaVector(BaseVector): results: QueryResult = collection.query(query_embeddings=query_vector, n_results=kwargs.get("top_k", 4)) score_threshold = float(kwargs.get("score_threshold") or 0.0) - ids: list[str] = results["ids"][0] - documents: list[str] = results["documents"][0] - metadatas: dict[str, Any] = results["metadatas"][0] - distances: list[float] = results["distances"][0] + # Check if results contain data + if not results["ids"] or not results["documents"] or not results["metadatas"] or not results["distances"]: + return [] + + ids = results["ids"][0] + documents = results["documents"][0] + metadatas = results["metadatas"][0] + distances = results["distances"][0] docs = [] for index in range(len(ids)): distance = distances[index] - metadata = metadatas[index] + metadata = dict(metadatas[index]) if distance >= score_threshold: metadata["score"] = distance doc = Document( @@ -111,7 +117,7 @@ class ChromaVector(BaseVector): ) docs.append(doc) # Sort the documents by score in descending order - docs = sorted(docs, key=lambda x: x.metadata["score"], reverse=True) + docs = sorted(docs, key=lambda x: x.metadata["score"] if x.metadata is not None else 0, reverse=True) return docs def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: @@ -133,7 +139,7 @@ class ChromaVectorFactory(AbstractVectorFactory): return ChromaVector( collection_name=collection_name, config=ChromaConfig( - host=dify_config.CHROMA_HOST, + host=dify_config.CHROMA_HOST or "", port=dify_config.CHROMA_PORT, tenant=dify_config.CHROMA_TENANT or chromadb.DEFAULT_TENANT, database=dify_config.CHROMA_DATABASE or chromadb.DEFAULT_DATABASE, diff --git a/api/core/rag/datasource/vdb/couchbase/couchbase_vector.py b/api/core/rag/datasource/vdb/couchbase/couchbase_vector.py index d26726e864..68a9952789 100644 --- a/api/core/rag/datasource/vdb/couchbase/couchbase_vector.py +++ b/api/core/rag/datasource/vdb/couchbase/couchbase_vector.py @@ -5,14 +5,14 @@ import uuid from datetime import timedelta from typing import Any -from couchbase import search -from couchbase.auth import PasswordAuthenticator -from couchbase.cluster import Cluster -from couchbase.management.search import SearchIndex +from couchbase import search # type: ignore +from couchbase.auth import PasswordAuthenticator # type: ignore +from couchbase.cluster import Cluster # type: ignore +from couchbase.management.search import SearchIndex # type: ignore # needed for options -- cluster, timeout, SQL++ (N1QL) query, etc. -from couchbase.options import ClusterOptions, SearchOptions -from couchbase.vector_search import VectorQuery, VectorSearch +from couchbase.options import ClusterOptions, SearchOptions # type: ignore +from couchbase.vector_search import VectorQuery, VectorSearch # type: ignore from flask import current_app from pydantic import BaseModel, model_validator @@ -231,7 +231,7 @@ class CouchbaseVector(BaseVector): # Pass the id as a parameter to the query result = self._cluster.query(query, named_parameters={"doc_id": id}).execute() for row in result: - return row["count"] > 0 + return bool(row["count"] > 0) return False # Return False if no rows are returned def delete_by_ids(self, ids: list[str]) -> None: @@ -369,10 +369,10 @@ class CouchbaseVectorFactory(AbstractVectorFactory): return CouchbaseVector( collection_name=collection_name, config=CouchbaseConfig( - connection_string=config.get("COUCHBASE_CONNECTION_STRING"), - user=config.get("COUCHBASE_USER"), - password=config.get("COUCHBASE_PASSWORD"), - bucket_name=config.get("COUCHBASE_BUCKET_NAME"), - scope_name=config.get("COUCHBASE_SCOPE_NAME"), + connection_string=config.get("COUCHBASE_CONNECTION_STRING", ""), + user=config.get("COUCHBASE_USER", ""), + password=config.get("COUCHBASE_PASSWORD", ""), + bucket_name=config.get("COUCHBASE_BUCKET_NAME", ""), + scope_name=config.get("COUCHBASE_SCOPE_NAME", ""), ), ) diff --git a/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py b/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py index b08811a021..8661828dc2 100644 --- a/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py +++ b/api/core/rag/datasource/vdb/elasticsearch/elasticsearch_vector.py @@ -1,7 +1,7 @@ import json import logging import math -from typing import Any, Optional +from typing import Any, Optional, cast from urllib.parse import urlparse import requests @@ -70,7 +70,7 @@ class ElasticSearchVector(BaseVector): def _get_version(self) -> str: info = self._client.info() - return info["version"]["number"] + return cast(str, info["version"]["number"]) def _check_version(self): if self._version < "8.0.0": @@ -135,7 +135,8 @@ class ElasticSearchVector(BaseVector): for doc, score in docs_and_scores: score_threshold = float(kwargs.get("score_threshold") or 0.0) if score > score_threshold: - doc.metadata["score"] = score + if doc.metadata is not None: + doc.metadata["score"] = score docs.append(doc) return docs @@ -156,12 +157,15 @@ class ElasticSearchVector(BaseVector): return docs def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs): - metadatas = [d.metadata for d in texts] + metadatas = [d.metadata if d.metadata is not None else {} for d in texts] self.create_collection(embeddings, metadatas) self.add_texts(texts, embeddings, **kwargs) def create_collection( - self, embeddings: list, metadatas: Optional[list[dict]] = None, index_params: Optional[dict] = None + self, + embeddings: list[list[float]], + metadatas: Optional[list[dict[Any, Any]]] = None, + index_params: Optional[dict] = None, ): lock_name = f"vector_indexing_lock_{self._collection_name}" with redis_client.lock(lock_name, timeout=20): @@ -208,10 +212,10 @@ class ElasticSearchVectorFactory(AbstractVectorFactory): return ElasticSearchVector( index_name=collection_name, config=ElasticSearchConfig( - host=config.get("ELASTICSEARCH_HOST"), - port=config.get("ELASTICSEARCH_PORT"), - username=config.get("ELASTICSEARCH_USERNAME"), - password=config.get("ELASTICSEARCH_PASSWORD"), + host=config.get("ELASTICSEARCH_HOST", "localhost"), + port=config.get("ELASTICSEARCH_PORT", 9200), + username=config.get("ELASTICSEARCH_USERNAME", ""), + password=config.get("ELASTICSEARCH_PASSWORD", ""), ), attributes=[], ) diff --git a/api/core/rag/datasource/vdb/lindorm/lindorm_vector.py b/api/core/rag/datasource/vdb/lindorm/lindorm_vector.py index 8646e52cf4..d7a14207e9 100644 --- a/api/core/rag/datasource/vdb/lindorm/lindorm_vector.py +++ b/api/core/rag/datasource/vdb/lindorm/lindorm_vector.py @@ -42,7 +42,7 @@ class LindormVectorStoreConfig(BaseModel): return values def to_opensearch_params(self) -> dict[str, Any]: - params = {"hosts": self.hosts} + params: dict[str, Any] = {"hosts": self.hosts} if self.username and self.password: params["http_auth"] = (self.username, self.password) return params @@ -53,7 +53,7 @@ class LindormVectorStore(BaseVector): self._routing = None self._routing_field = None if using_ugc: - routing_value: str = kwargs.get("routing_value") + routing_value: str | None = kwargs.get("routing_value") if routing_value is None: raise ValueError("UGC index should init vector with valid 'routing_value' parameter value") self._routing = routing_value.lower() @@ -87,14 +87,15 @@ class LindormVectorStore(BaseVector): "_id": uuids[i], } } - action_values = { + action_values: dict[str, Any] = { Field.CONTENT_KEY.value: documents[i].page_content, Field.VECTOR.value: embeddings[i], # Make sure you pass an array here Field.METADATA_KEY.value: documents[i].metadata, } if self._using_ugc: action_header["index"]["routing"] = self._routing - action_values[self._routing_field] = self._routing + if self._routing_field is not None: + action_values[self._routing_field] = self._routing actions.append(action_header) actions.append(action_values) response = self._client.bulk(actions) @@ -105,7 +106,9 @@ class LindormVectorStore(BaseVector): self.refresh() def get_ids_by_metadata_field(self, key: str, value: str): - query = {"query": {"bool": {"must": [{"term": {f"{Field.METADATA_KEY.value}.{key}.keyword": value}}]}}} + query: dict[str, Any] = { + "query": {"bool": {"must": [{"term": {f"{Field.METADATA_KEY.value}.{key}.keyword": value}}]}} + } if self._using_ugc: query["query"]["bool"]["must"].append({"term": {f"{self._routing_field}.keyword": self._routing}}) response = self._client.search(index=self._collection_name, body=query) @@ -191,7 +194,8 @@ class LindormVectorStore(BaseVector): for doc, score in docs_and_scores: score_threshold = kwargs.get("score_threshold", 0.0) or 0.0 if score > score_threshold: - doc.metadata["score"] = score + if doc.metadata is not None: + doc.metadata["score"] = score docs.append(doc) return docs @@ -366,6 +370,7 @@ def default_text_search_query( routing_field: Optional[str] = None, **kwargs, ) -> dict: + query_clause: dict[str, Any] = {} if routing is not None: query_clause = { "bool": {"must": [{"match": {text_field: query_text}}, {"term": {f"{routing_field}.keyword": routing}}]} @@ -386,7 +391,7 @@ def default_text_search_query( else: must = [query_clause] - boolean_query = {"must": must} + boolean_query: dict[str, Any] = {"must": must} if must_not: if not isinstance(must_not, list): @@ -426,7 +431,7 @@ def default_vector_search_query( filter_type = "post_filter" if filter_type is None else filter_type if not isinstance(filters, list): raise RuntimeError(f"unexpected filter with {type(filters)}") - final_ext = {"lvector": {}} + final_ext: dict[str, Any] = {"lvector": {}} if min_score != "0.0": final_ext["lvector"]["min_score"] = min_score if ef_search: @@ -438,7 +443,7 @@ def default_vector_search_query( if client_refactor: final_ext["lvector"]["client_refactor"] = client_refactor - search_query = { + search_query: dict[str, Any] = { "size": k, "_source": True, # force return '_source' "query": {"knn": {vector_field: {"vector": query_vector, "k": k}}}, @@ -446,8 +451,8 @@ def default_vector_search_query( if filters is not None: # when using filter, transform filter from List[Dict] to Dict as valid format - filters = {"bool": {"must": filters}} if len(filters) > 1 else filters[0] - search_query["query"]["knn"][vector_field]["filter"] = filters # filter should be Dict + filter_dict = {"bool": {"must": filters}} if len(filters) > 1 else filters[0] + search_query["query"]["knn"][vector_field]["filter"] = filter_dict # filter should be Dict if filter_type: final_ext["lvector"]["filter_type"] = filter_type @@ -459,17 +464,19 @@ def default_vector_search_query( class LindormVectorStoreFactory(AbstractVectorFactory): def init_vector(self, dataset: Dataset, attributes: list, embeddings: Embeddings) -> LindormVectorStore: lindorm_config = LindormVectorStoreConfig( - hosts=dify_config.LINDORM_URL, + hosts=dify_config.LINDORM_URL or "", username=dify_config.LINDORM_USERNAME, password=dify_config.LINDORM_PASSWORD, using_ugc=dify_config.USING_UGC_INDEX, ) using_ugc = dify_config.USING_UGC_INDEX + if using_ugc is None: + raise ValueError("USING_UGC_INDEX is not set") routing_value = None if dataset.index_struct: # if an existed record's index_struct_dict doesn't contain using_ugc field, # it actually stores in the normal index format - stored_in_ugc = dataset.index_struct_dict.get("using_ugc", False) + stored_in_ugc: bool = dataset.index_struct_dict.get("using_ugc", False) using_ugc = stored_in_ugc if stored_in_ugc: dimension = dataset.index_struct_dict["dimension"] diff --git a/api/core/rag/datasource/vdb/milvus/milvus_vector.py b/api/core/rag/datasource/vdb/milvus/milvus_vector.py index 5a263d6e78..9b029ffc19 100644 --- a/api/core/rag/datasource/vdb/milvus/milvus_vector.py +++ b/api/core/rag/datasource/vdb/milvus/milvus_vector.py @@ -3,8 +3,8 @@ import logging from typing import Any, Optional from pydantic import BaseModel, model_validator -from pymilvus import MilvusClient, MilvusException -from pymilvus.milvus_client import IndexParams +from pymilvus import MilvusClient, MilvusException # type: ignore +from pymilvus.milvus_client import IndexParams # type: ignore from configs import dify_config from core.rag.datasource.vdb.field import Field @@ -54,14 +54,14 @@ class MilvusVector(BaseVector): self._client_config = config self._client = self._init_client(config) self._consistency_level = "Session" - self._fields = [] + self._fields: list[str] = [] def get_type(self) -> str: return VectorType.MILVUS def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs): index_params = {"metric_type": "IP", "index_type": "HNSW", "params": {"M": 8, "efConstruction": 64}} - metadatas = [d.metadata for d in texts] + metadatas = [d.metadata if d.metadata is not None else {} for d in texts] self.create_collection(embeddings, metadatas, index_params) self.add_texts(texts, embeddings) @@ -161,8 +161,8 @@ class MilvusVector(BaseVector): return # Grab the existing collection if it exists if not self._client.has_collection(self._collection_name): - from pymilvus import CollectionSchema, DataType, FieldSchema - from pymilvus.orm.types import infer_dtype_bydata + from pymilvus import CollectionSchema, DataType, FieldSchema # type: ignore + from pymilvus.orm.types import infer_dtype_bydata # type: ignore # Determine embedding dim dim = len(embeddings[0]) @@ -217,10 +217,10 @@ class MilvusVectorFactory(AbstractVectorFactory): return MilvusVector( collection_name=collection_name, config=MilvusConfig( - uri=dify_config.MILVUS_URI, - token=dify_config.MILVUS_TOKEN, - user=dify_config.MILVUS_USER, - password=dify_config.MILVUS_PASSWORD, - database=dify_config.MILVUS_DATABASE, + uri=dify_config.MILVUS_URI or "", + token=dify_config.MILVUS_TOKEN or "", + user=dify_config.MILVUS_USER or "", + password=dify_config.MILVUS_PASSWORD or "", + database=dify_config.MILVUS_DATABASE or "", ), ) diff --git a/api/core/rag/datasource/vdb/myscale/myscale_vector.py b/api/core/rag/datasource/vdb/myscale/myscale_vector.py index b7b6b803ad..e63e1f522b 100644 --- a/api/core/rag/datasource/vdb/myscale/myscale_vector.py +++ b/api/core/rag/datasource/vdb/myscale/myscale_vector.py @@ -74,15 +74,16 @@ class MyScaleVector(BaseVector): columns = ["id", "text", "vector", "metadata"] values = [] for i, doc in enumerate(documents): - doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) - row = ( - doc_id, - self.escape_str(doc.page_content), - embeddings[i], - json.dumps(doc.metadata) if doc.metadata else {}, - ) - values.append(str(row)) - ids.append(doc_id) + if doc.metadata is not None: + doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) + row = ( + doc_id, + self.escape_str(doc.page_content), + embeddings[i], + json.dumps(doc.metadata) if doc.metadata else {}, + ) + values.append(str(row)) + ids.append(doc_id) sql = f""" INSERT INTO {self._config.database}.{self._collection_name} ({",".join(columns)}) VALUES {",".join(values)} diff --git a/api/core/rag/datasource/vdb/oceanbase/oceanbase_vector.py b/api/core/rag/datasource/vdb/oceanbase/oceanbase_vector.py index c44338d42a..957c799a60 100644 --- a/api/core/rag/datasource/vdb/oceanbase/oceanbase_vector.py +++ b/api/core/rag/datasource/vdb/oceanbase/oceanbase_vector.py @@ -4,7 +4,7 @@ import math from typing import Any from pydantic import BaseModel, model_validator -from pyobvector import VECTOR, ObVecClient +from pyobvector import VECTOR, ObVecClient # type: ignore from sqlalchemy import JSON, Column, String, func from sqlalchemy.dialects.mysql import LONGTEXT @@ -131,7 +131,7 @@ class OceanBaseVector(BaseVector): def text_exists(self, id: str) -> bool: cur = self._client.get(table_name=self._collection_name, id=id) - return cur.rowcount != 0 + return bool(cur.rowcount != 0) def delete_by_ids(self, ids: list[str]) -> None: self._client.delete(table_name=self._collection_name, ids=ids) diff --git a/api/core/rag/datasource/vdb/opensearch/opensearch_vector.py b/api/core/rag/datasource/vdb/opensearch/opensearch_vector.py index 7a976d7c3c..72a1502205 100644 --- a/api/core/rag/datasource/vdb/opensearch/opensearch_vector.py +++ b/api/core/rag/datasource/vdb/opensearch/opensearch_vector.py @@ -66,7 +66,7 @@ class OpenSearchVector(BaseVector): return VectorType.OPENSEARCH def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs): - metadatas = [d.metadata for d in texts] + metadatas = [d.metadata if d.metadata is not None else {} for d in texts] self.create_collection(embeddings, metadatas) self.add_texts(texts, embeddings) @@ -244,7 +244,7 @@ class OpenSearchVectorFactory(AbstractVectorFactory): dataset.index_struct = json.dumps(self.gen_index_struct_dict(VectorType.OPENSEARCH, collection_name)) open_search_config = OpenSearchConfig( - host=dify_config.OPENSEARCH_HOST, + host=dify_config.OPENSEARCH_HOST or "localhost", port=dify_config.OPENSEARCH_PORT, user=dify_config.OPENSEARCH_USER, password=dify_config.OPENSEARCH_PASSWORD, diff --git a/api/core/rag/datasource/vdb/oracle/oraclevector.py b/api/core/rag/datasource/vdb/oracle/oraclevector.py index 74608f1e1a..dfff3563c3 100644 --- a/api/core/rag/datasource/vdb/oracle/oraclevector.py +++ b/api/core/rag/datasource/vdb/oracle/oraclevector.py @@ -5,7 +5,7 @@ import uuid from contextlib import contextmanager from typing import Any -import jieba.posseg as pseg +import jieba.posseg as pseg # type: ignore import numpy import oracledb from pydantic import BaseModel, model_validator @@ -88,12 +88,11 @@ class OracleVector(BaseVector): def numpy_converter_out(self, value): if value.typecode == "b": - dtype = numpy.int8 + return numpy.array(value, copy=False, dtype=numpy.int8) elif value.typecode == "f": - dtype = numpy.float32 + return numpy.array(value, copy=False, dtype=numpy.float32) else: - dtype = numpy.float64 - return numpy.array(value, copy=False, dtype=dtype) + return numpy.array(value, copy=False, dtype=numpy.float64) def output_type_handler(self, cursor, metadata): if metadata.type_code is oracledb.DB_TYPE_VECTOR: @@ -135,17 +134,18 @@ class OracleVector(BaseVector): values = [] pks = [] for i, doc in enumerate(documents): - doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) - pks.append(doc_id) - values.append( - ( - doc_id, - doc.page_content, - json.dumps(doc.metadata), - # array.array("f", embeddings[i]), - numpy.array(embeddings[i]), + if doc.metadata is not None: + doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) + pks.append(doc_id) + values.append( + ( + doc_id, + doc.page_content, + json.dumps(doc.metadata), + # array.array("f", embeddings[i]), + numpy.array(embeddings[i]), + ) ) - ) # print(f"INSERT INTO {self.table_name} (id, text, meta, embedding) VALUES (:1, :2, :3, :4)") with self._get_cursor() as cur: cur.executemany( @@ -201,8 +201,8 @@ class OracleVector(BaseVector): def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: # lazy import - import nltk - from nltk.corpus import stopwords + import nltk # type: ignore + from nltk.corpus import stopwords # type: ignore top_k = kwargs.get("top_k", 5) # just not implement fetch by score_threshold now, may be later @@ -285,10 +285,10 @@ class OracleVectorFactory(AbstractVectorFactory): return OracleVector( collection_name=collection_name, config=OracleVectorConfig( - host=dify_config.ORACLE_HOST, + host=dify_config.ORACLE_HOST or "localhost", port=dify_config.ORACLE_PORT, - user=dify_config.ORACLE_USER, - password=dify_config.ORACLE_PASSWORD, - database=dify_config.ORACLE_DATABASE, + user=dify_config.ORACLE_USER or "system", + password=dify_config.ORACLE_PASSWORD or "oracle", + database=dify_config.ORACLE_DATABASE or "orcl", ), ) diff --git a/api/core/rag/datasource/vdb/pgvecto_rs/pgvecto_rs.py b/api/core/rag/datasource/vdb/pgvecto_rs/pgvecto_rs.py index 7cbbdcc81f..221bc68d68 100644 --- a/api/core/rag/datasource/vdb/pgvecto_rs/pgvecto_rs.py +++ b/api/core/rag/datasource/vdb/pgvecto_rs/pgvecto_rs.py @@ -4,7 +4,7 @@ from typing import Any from uuid import UUID, uuid4 from numpy import ndarray -from pgvecto_rs.sqlalchemy import VECTOR +from pgvecto_rs.sqlalchemy import VECTOR # type: ignore from pydantic import BaseModel, model_validator from sqlalchemy import Float, String, create_engine, insert, select, text from sqlalchemy import text as sql_text @@ -58,7 +58,7 @@ class PGVectoRS(BaseVector): with Session(self._client) as session: session.execute(text("CREATE EXTENSION IF NOT EXISTS vectors")) session.commit() - self._fields = [] + self._fields: list[str] = [] class _Table(CollectionORM): __tablename__ = collection_name @@ -222,11 +222,11 @@ class PGVectoRSFactory(AbstractVectorFactory): return PGVectoRS( collection_name=collection_name, config=PgvectoRSConfig( - host=dify_config.PGVECTO_RS_HOST, - port=dify_config.PGVECTO_RS_PORT, - user=dify_config.PGVECTO_RS_USER, - password=dify_config.PGVECTO_RS_PASSWORD, - database=dify_config.PGVECTO_RS_DATABASE, + host=dify_config.PGVECTO_RS_HOST or "localhost", + port=dify_config.PGVECTO_RS_PORT or 5432, + user=dify_config.PGVECTO_RS_USER or "postgres", + password=dify_config.PGVECTO_RS_PASSWORD or "", + database=dify_config.PGVECTO_RS_DATABASE or "postgres", ), dim=dim, ) diff --git a/api/core/rag/datasource/vdb/pgvector/pgvector.py b/api/core/rag/datasource/vdb/pgvector/pgvector.py index 40a9cdd136..271281ca7e 100644 --- a/api/core/rag/datasource/vdb/pgvector/pgvector.py +++ b/api/core/rag/datasource/vdb/pgvector/pgvector.py @@ -3,8 +3,8 @@ import uuid from contextlib import contextmanager from typing import Any -import psycopg2.extras -import psycopg2.pool +import psycopg2.extras # type: ignore +import psycopg2.pool # type: ignore from pydantic import BaseModel, model_validator from configs import dify_config @@ -98,16 +98,17 @@ class PGVector(BaseVector): values = [] pks = [] for i, doc in enumerate(documents): - doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) - pks.append(doc_id) - values.append( - ( - doc_id, - doc.page_content, - json.dumps(doc.metadata), - embeddings[i], + if doc.metadata is not None: + doc_id = doc.metadata.get("doc_id", str(uuid.uuid4())) + pks.append(doc_id) + values.append( + ( + doc_id, + doc.page_content, + json.dumps(doc.metadata), + embeddings[i], + ) ) - ) with self._get_cursor() as cur: psycopg2.extras.execute_values( cur, f"INSERT INTO {self.table_name} (id, text, meta, embedding) VALUES %s", values @@ -216,11 +217,11 @@ class PGVectorFactory(AbstractVectorFactory): return PGVector( collection_name=collection_name, config=PGVectorConfig( - host=dify_config.PGVECTOR_HOST, + host=dify_config.PGVECTOR_HOST or "localhost", port=dify_config.PGVECTOR_PORT, - user=dify_config.PGVECTOR_USER, - password=dify_config.PGVECTOR_PASSWORD, - database=dify_config.PGVECTOR_DATABASE, + user=dify_config.PGVECTOR_USER or "postgres", + password=dify_config.PGVECTOR_PASSWORD or "", + database=dify_config.PGVECTOR_DATABASE or "postgres", min_connection=dify_config.PGVECTOR_MIN_CONNECTION, max_connection=dify_config.PGVECTOR_MAX_CONNECTION, ), diff --git a/api/core/rag/datasource/vdb/qdrant/qdrant_vector.py b/api/core/rag/datasource/vdb/qdrant/qdrant_vector.py index 3811458e02..6e94cb69db 100644 --- a/api/core/rag/datasource/vdb/qdrant/qdrant_vector.py +++ b/api/core/rag/datasource/vdb/qdrant/qdrant_vector.py @@ -51,6 +51,8 @@ class QdrantConfig(BaseModel): if self.endpoint and self.endpoint.startswith("path:"): path = self.endpoint.replace("path:", "") if not os.path.isabs(path): + if not self.root_path: + raise ValueError("Root path is not set") path = os.path.join(self.root_path, path) return {"path": path} @@ -149,9 +151,12 @@ class QdrantVector(BaseVector): uuids = self._get_uuids(documents) texts = [d.page_content for d in documents] metadatas = [d.metadata for d in documents] - added_ids = [] - for batch_ids, points in self._generate_rest_batches(texts, embeddings, metadatas, uuids, 64, self._group_id): + # Filter out None values from metadatas list to match expected type + filtered_metadatas = [m for m in metadatas if m is not None] + for batch_ids, points in self._generate_rest_batches( + texts, embeddings, filtered_metadatas, uuids, 64, self._group_id + ): self._client.upsert(collection_name=self._collection_name, points=points) added_ids.extend(batch_ids) @@ -194,7 +199,7 @@ class QdrantVector(BaseVector): batch_metadatas, Field.CONTENT_KEY.value, Field.METADATA_KEY.value, - group_id, + group_id or "", # Ensure group_id is never None Field.GROUP_KEY.value, ), ) @@ -337,18 +342,20 @@ class QdrantVector(BaseVector): ) docs = [] for result in results: + if result.payload is None: + continue metadata = result.payload.get(Field.METADATA_KEY.value) or {} # duplicate check score threshold score_threshold = float(kwargs.get("score_threshold") or 0.0) if result.score > score_threshold: metadata["score"] = result.score doc = Document( - page_content=result.payload.get(Field.CONTENT_KEY.value), + page_content=result.payload.get(Field.CONTENT_KEY.value, ""), metadata=metadata, ) docs.append(doc) # Sort the documents by score in descending order - docs = sorted(docs, key=lambda x: x.metadata["score"], reverse=True) + docs = sorted(docs, key=lambda x: x.metadata["score"] if x.metadata is not None else 0, reverse=True) return docs def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: @@ -432,9 +439,9 @@ class QdrantVectorFactory(AbstractVectorFactory): collection_name=collection_name, group_id=dataset.id, config=QdrantConfig( - endpoint=dify_config.QDRANT_URL, + endpoint=dify_config.QDRANT_URL or "", api_key=dify_config.QDRANT_API_KEY, - root_path=current_app.config.root_path, + root_path=str(current_app.config.root_path), timeout=dify_config.QDRANT_CLIENT_TIMEOUT, grpc_port=dify_config.QDRANT_GRPC_PORT, prefer_grpc=dify_config.QDRANT_GRPC_ENABLED, diff --git a/api/core/rag/datasource/vdb/relyt/relyt_vector.py b/api/core/rag/datasource/vdb/relyt/relyt_vector.py index f373dcfeab..a3a20448ff 100644 --- a/api/core/rag/datasource/vdb/relyt/relyt_vector.py +++ b/api/core/rag/datasource/vdb/relyt/relyt_vector.py @@ -3,7 +3,7 @@ import uuid from typing import Any, Optional from pydantic import BaseModel, model_validator -from sqlalchemy import Column, Sequence, String, Table, create_engine, insert +from sqlalchemy import Column, String, Table, create_engine, insert from sqlalchemy import text as sql_text from sqlalchemy.dialects.postgresql import JSON, TEXT from sqlalchemy.orm import Session @@ -58,14 +58,14 @@ class RelytVector(BaseVector): f"postgresql+psycopg2://{config.user}:{config.password}@{config.host}:{config.port}/{config.database}" ) self.client = create_engine(self._url) - self._fields = [] + self._fields: list[str] = [] self._group_id = group_id def get_type(self) -> str: return VectorType.RELYT - def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs): - index_params = {} + def create(self, texts: list[Document], embeddings: list[list[float]], **kwargs) -> None: + index_params: dict[str, Any] = {} metadatas = [d.metadata for d in texts] self.create_collection(len(embeddings[0])) self.embedding_dimension = len(embeddings[0]) @@ -107,10 +107,10 @@ class RelytVector(BaseVector): redis_client.set(collection_exist_cache_key, 1, ex=3600) def add_texts(self, documents: list[Document], embeddings: list[list[float]], **kwargs): - from pgvecto_rs.sqlalchemy import VECTOR + from pgvecto_rs.sqlalchemy import VECTOR # type: ignore ids = [str(uuid.uuid1()) for _ in documents] - metadatas = [d.metadata for d in documents] + metadatas = [d.metadata for d in documents if d.metadata is not None] for metadata in metadatas: metadata["group_id"] = self._group_id texts = [d.page_content for d in documents] @@ -242,10 +242,6 @@ class RelytVector(BaseVector): filter: Optional[dict] = None, ) -> list[tuple[Document, float]]: # Add the filter if provided - try: - from sqlalchemy.engine import Row - except ImportError: - raise ImportError("Could not import Row from sqlalchemy.engine. Please 'pip install sqlalchemy>=1.4'.") filter_condition = "" if filter is not None: @@ -275,7 +271,7 @@ class RelytVector(BaseVector): # Execute the query and fetch the results with self.client.connect() as conn: - results: Sequence[Row] = conn.execute(sql_text(sql_query), params).fetchall() + results = conn.execute(sql_text(sql_query), params).fetchall() documents_with_scores = [ ( @@ -307,11 +303,11 @@ class RelytVectorFactory(AbstractVectorFactory): return RelytVector( collection_name=collection_name, config=RelytConfig( - host=dify_config.RELYT_HOST, + host=dify_config.RELYT_HOST or "localhost", port=dify_config.RELYT_PORT, - user=dify_config.RELYT_USER, - password=dify_config.RELYT_PASSWORD, - database=dify_config.RELYT_DATABASE, + user=dify_config.RELYT_USER or "", + password=dify_config.RELYT_PASSWORD or "", + database=dify_config.RELYT_DATABASE or "default", ), group_id=dataset.id, ) diff --git a/api/core/rag/datasource/vdb/tencent/tencent_vector.py b/api/core/rag/datasource/vdb/tencent/tencent_vector.py index f971a9c5eb..c15f4b229f 100644 --- a/api/core/rag/datasource/vdb/tencent/tencent_vector.py +++ b/api/core/rag/datasource/vdb/tencent/tencent_vector.py @@ -2,10 +2,10 @@ import json from typing import Any, Optional from pydantic import BaseModel -from tcvectordb import VectorDBClient -from tcvectordb.model import document, enum -from tcvectordb.model import index as vdb_index -from tcvectordb.model.document import Filter +from tcvectordb import VectorDBClient # type: ignore +from tcvectordb.model import document, enum # type: ignore +from tcvectordb.model import index as vdb_index # type: ignore +from tcvectordb.model.document import Filter # type: ignore from configs import dify_config from core.rag.datasource.vdb.vector_base import BaseVector @@ -25,8 +25,8 @@ class TencentConfig(BaseModel): database: Optional[str] index_type: str = "HNSW" metric_type: str = "L2" - shard: int = (1,) - replicas: int = (2,) + shard: int = 1 + replicas: int = 2 def to_tencent_params(self): return {"url": self.url, "username": self.username, "key": self.api_key, "timeout": self.timeout} @@ -120,15 +120,15 @@ class TencentVector(BaseVector): metadatas = [doc.metadata for doc in documents] total_count = len(embeddings) docs = [] - for id in range(0, total_count): + for i in range(0, total_count): if metadatas is None: continue - metadata = json.dumps(metadatas[id]) + metadata = metadatas[i] or {} doc = document.Document( - id=metadatas[id]["doc_id"], - vector=embeddings[id], - text=texts[id], - metadata=metadata, + id=metadata.get("doc_id"), + vector=embeddings[i], + text=texts[i], + metadata=json.dumps(metadata), ) docs.append(doc) self._db.collection(self._collection_name).upsert(docs, self._client_config.timeout) @@ -159,8 +159,8 @@ class TencentVector(BaseVector): def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: return [] - def _get_search_res(self, res, score_threshold): - docs = [] + def _get_search_res(self, res: list | None, score_threshold: float) -> list[Document]: + docs: list[Document] = [] if res is None or len(res) == 0: return docs @@ -193,7 +193,7 @@ class TencentVectorFactory(AbstractVectorFactory): return TencentVector( collection_name=collection_name, config=TencentConfig( - url=dify_config.TENCENT_VECTOR_DB_URL, + url=dify_config.TENCENT_VECTOR_DB_URL or "", api_key=dify_config.TENCENT_VECTOR_DB_API_KEY, timeout=dify_config.TENCENT_VECTOR_DB_TIMEOUT, username=dify_config.TENCENT_VECTOR_DB_USERNAME, diff --git a/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_on_qdrant_vector.py b/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_on_qdrant_vector.py index cfd47aac5b..19c5579a68 100644 --- a/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_on_qdrant_vector.py +++ b/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_on_qdrant_vector.py @@ -54,7 +54,10 @@ class TidbOnQdrantConfig(BaseModel): if self.endpoint and self.endpoint.startswith("path:"): path = self.endpoint.replace("path:", "") if not os.path.isabs(path): - path = os.path.join(self.root_path, path) + if self.root_path: + path = os.path.join(self.root_path, path) + else: + raise ValueError("root_path is required") return {"path": path} else: @@ -157,7 +160,7 @@ class TidbOnQdrantVector(BaseVector): def add_texts(self, documents: list[Document], embeddings: list[list[float]], **kwargs): uuids = self._get_uuids(documents) texts = [d.page_content for d in documents] - metadatas = [d.metadata for d in documents] + metadatas = [d.metadata for d in documents if d.metadata is not None] added_ids = [] for batch_ids, points in self._generate_rest_batches(texts, embeddings, metadatas, uuids, 64, self._group_id): @@ -203,7 +206,7 @@ class TidbOnQdrantVector(BaseVector): batch_metadatas, Field.CONTENT_KEY.value, Field.METADATA_KEY.value, - group_id, + group_id or "", Field.GROUP_KEY.value, ), ) @@ -334,18 +337,20 @@ class TidbOnQdrantVector(BaseVector): ) docs = [] for result in results: + if result.payload is None: + continue metadata = result.payload.get(Field.METADATA_KEY.value) or {} # duplicate check score threshold score_threshold = kwargs.get("score_threshold") or 0.0 if result.score > score_threshold: metadata["score"] = result.score doc = Document( - page_content=result.payload.get(Field.CONTENT_KEY.value), + page_content=result.payload.get(Field.CONTENT_KEY.value, ""), metadata=metadata, ) docs.append(doc) # Sort the documents by score in descending order - docs = sorted(docs, key=lambda x: x.metadata["score"], reverse=True) + docs = sorted(docs, key=lambda x: x.metadata["score"] if x.metadata is not None else 0, reverse=True) return docs def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: @@ -427,12 +432,12 @@ class TidbOnQdrantVectorFactory(AbstractVectorFactory): else: new_cluster = TidbService.create_tidb_serverless_cluster( - dify_config.TIDB_PROJECT_ID, - dify_config.TIDB_API_URL, - dify_config.TIDB_IAM_API_URL, - dify_config.TIDB_PUBLIC_KEY, - dify_config.TIDB_PRIVATE_KEY, - dify_config.TIDB_REGION, + dify_config.TIDB_PROJECT_ID or "", + dify_config.TIDB_API_URL or "", + dify_config.TIDB_IAM_API_URL or "", + dify_config.TIDB_PUBLIC_KEY or "", + dify_config.TIDB_PRIVATE_KEY or "", + dify_config.TIDB_REGION or "", ) new_tidb_auth_binding = TidbAuthBinding( cluster_id=new_cluster["cluster_id"], @@ -464,9 +469,9 @@ class TidbOnQdrantVectorFactory(AbstractVectorFactory): collection_name=collection_name, group_id=dataset.id, config=TidbOnQdrantConfig( - endpoint=dify_config.TIDB_ON_QDRANT_URL, + endpoint=dify_config.TIDB_ON_QDRANT_URL or "", api_key=TIDB_ON_QDRANT_API_KEY, - root_path=config.root_path, + root_path=str(config.root_path), timeout=dify_config.TIDB_ON_QDRANT_CLIENT_TIMEOUT, grpc_port=dify_config.TIDB_ON_QDRANT_GRPC_PORT, prefer_grpc=dify_config.TIDB_ON_QDRANT_GRPC_ENABLED, diff --git a/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_service.py b/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_service.py index 8dd5922ad0..0a48c79511 100644 --- a/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_service.py +++ b/api/core/rag/datasource/vdb/tidb_on_qdrant/tidb_service.py @@ -146,7 +146,7 @@ class TidbService: iam_url: str, public_key: str, private_key: str, - ) -> list[dict]: + ): """ Update the status of a new TiDB Serverless cluster. :param project_id: The project ID of the TiDB Cloud project (required). @@ -159,7 +159,6 @@ class TidbService: :return: The response from the API. """ - clusters = [] tidb_serverless_list_map = {item.cluster_id: item for item in tidb_serverless_list} cluster_ids = [item.cluster_id for item in tidb_serverless_list] params = {"clusterIds": cluster_ids, "view": "BASIC"} @@ -169,7 +168,6 @@ class TidbService: if response.status_code == 200: response_data = response.json() - cluster_infos = [] for item in response_data["clusters"]: state = item["state"] userPrefix = item["userPrefix"] @@ -236,16 +234,17 @@ class TidbService: cluster_infos = [] for item in response_data["clusters"]: cache_key = f"tidb_serverless_cluster_password:{item['displayName']}" - password = redis_client.get(cache_key) - if not password: + cached_password = redis_client.get(cache_key) + if not cached_password: continue cluster_info = { "cluster_id": item["clusterId"], "cluster_name": item["displayName"], "account": "root", - "password": password.decode("utf-8"), + "password": cached_password.decode("utf-8"), } cluster_infos.append(cluster_info) return cluster_infos else: response.raise_for_status() + return [] # FIXME for mypy, This line will not be reached as raise_for_status() will raise an exception diff --git a/api/core/rag/datasource/vdb/tidb_vector/tidb_vector.py b/api/core/rag/datasource/vdb/tidb_vector/tidb_vector.py index 39ab6ea71e..be3a417390 100644 --- a/api/core/rag/datasource/vdb/tidb_vector/tidb_vector.py +++ b/api/core/rag/datasource/vdb/tidb_vector/tidb_vector.py @@ -49,7 +49,7 @@ class TiDBVector(BaseVector): return VectorType.TIDB_VECTOR def _table(self, dim: int) -> Table: - from tidb_vector.sqlalchemy import VectorType + from tidb_vector.sqlalchemy import VectorType # type: ignore return Table( self._collection_name, @@ -241,11 +241,11 @@ class TiDBVectorFactory(AbstractVectorFactory): return TiDBVector( collection_name=collection_name, config=TiDBVectorConfig( - host=dify_config.TIDB_VECTOR_HOST, - port=dify_config.TIDB_VECTOR_PORT, - user=dify_config.TIDB_VECTOR_USER, - password=dify_config.TIDB_VECTOR_PASSWORD, - database=dify_config.TIDB_VECTOR_DATABASE, + host=dify_config.TIDB_VECTOR_HOST or "", + port=dify_config.TIDB_VECTOR_PORT or 0, + user=dify_config.TIDB_VECTOR_USER or "", + password=dify_config.TIDB_VECTOR_PASSWORD or "", + database=dify_config.TIDB_VECTOR_DATABASE or "", program_name=dify_config.APPLICATION_NAME, ), ) diff --git a/api/core/rag/datasource/vdb/vector_base.py b/api/core/rag/datasource/vdb/vector_base.py index 22e191340d..edfce2edd8 100644 --- a/api/core/rag/datasource/vdb/vector_base.py +++ b/api/core/rag/datasource/vdb/vector_base.py @@ -51,15 +51,16 @@ class BaseVector(ABC): def _filter_duplicate_texts(self, texts: list[Document]) -> list[Document]: for text in texts.copy(): - doc_id = text.metadata["doc_id"] - exists_duplicate_node = self.text_exists(doc_id) - if exists_duplicate_node: - texts.remove(text) + if text.metadata and "doc_id" in text.metadata: + doc_id = text.metadata["doc_id"] + exists_duplicate_node = self.text_exists(doc_id) + if exists_duplicate_node: + texts.remove(text) return texts def _get_uuids(self, texts: list[Document]) -> list[str]: - return [text.metadata["doc_id"] for text in texts] + return [text.metadata["doc_id"] for text in texts if text.metadata and "doc_id" in text.metadata] @property def collection_name(self): diff --git a/api/core/rag/datasource/vdb/vector_factory.py b/api/core/rag/datasource/vdb/vector_factory.py index 6d2e04fc02..523fa80f12 100644 --- a/api/core/rag/datasource/vdb/vector_factory.py +++ b/api/core/rag/datasource/vdb/vector_factory.py @@ -193,10 +193,13 @@ class Vector: def _filter_duplicate_texts(self, texts: list[Document]) -> list[Document]: for text in texts.copy(): + if text.metadata is None: + continue doc_id = text.metadata["doc_id"] - exists_duplicate_node = self.text_exists(doc_id) - if exists_duplicate_node: - texts.remove(text) + if doc_id: + exists_duplicate_node = self.text_exists(doc_id) + if exists_duplicate_node: + texts.remove(text) return texts diff --git a/api/core/rag/datasource/vdb/vikingdb/vikingdb_vector.py b/api/core/rag/datasource/vdb/vikingdb/vikingdb_vector.py index 4f927f2899..9de8761a91 100644 --- a/api/core/rag/datasource/vdb/vikingdb/vikingdb_vector.py +++ b/api/core/rag/datasource/vdb/vikingdb/vikingdb_vector.py @@ -2,7 +2,7 @@ import json from typing import Any from pydantic import BaseModel -from volcengine.viking_db import ( +from volcengine.viking_db import ( # type: ignore Data, DistanceType, Field, @@ -121,11 +121,12 @@ class VikingDBVector(BaseVector): for i, page_content in enumerate(page_contents): metadata = {} if metadatas is not None: - for key, val in metadatas[i].items(): + for key, val in (metadatas[i] or {}).items(): metadata[key] = val + # FIXME: fix the type of metadata later doc = Data( { - vdb_Field.PRIMARY_KEY.value: metadatas[i]["doc_id"], + vdb_Field.PRIMARY_KEY.value: metadatas[i]["doc_id"], # type: ignore vdb_Field.VECTOR.value: embeddings[i] if embeddings else None, vdb_Field.CONTENT_KEY.value: page_content, vdb_Field.METADATA_KEY.value: json.dumps(metadata), @@ -178,7 +179,7 @@ class VikingDBVector(BaseVector): score_threshold = float(kwargs.get("score_threshold") or 0.0) return self._get_search_res(results, score_threshold) - def _get_search_res(self, results, score_threshold): + def _get_search_res(self, results, score_threshold) -> list[Document]: if len(results) == 0: return [] @@ -191,7 +192,7 @@ class VikingDBVector(BaseVector): metadata["score"] = result.score doc = Document(page_content=result.fields.get(vdb_Field.CONTENT_KEY.value), metadata=metadata) docs.append(doc) - docs = sorted(docs, key=lambda x: x.metadata["score"], reverse=True) + docs = sorted(docs, key=lambda x: x.metadata.get("score", 0) if x.metadata else 0, reverse=True) return docs def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: diff --git a/api/core/rag/datasource/vdb/weaviate/weaviate_vector.py b/api/core/rag/datasource/vdb/weaviate/weaviate_vector.py index 649cfbfea8..68d043a19f 100644 --- a/api/core/rag/datasource/vdb/weaviate/weaviate_vector.py +++ b/api/core/rag/datasource/vdb/weaviate/weaviate_vector.py @@ -3,7 +3,7 @@ import json from typing import Any, Optional import requests -import weaviate +import weaviate # type: ignore from pydantic import BaseModel, model_validator from configs import dify_config @@ -107,7 +107,8 @@ class WeaviateVector(BaseVector): for i, text in enumerate(texts): data_properties = {Field.TEXT_KEY.value: text} if metadatas is not None: - for key, val in metadatas[i].items(): + # metadata maybe None + for key, val in (metadatas[i] or {}).items(): data_properties[key] = self._json_serializable(val) batch.add_data_object( @@ -208,10 +209,11 @@ class WeaviateVector(BaseVector): score_threshold = float(kwargs.get("score_threshold") or 0.0) # check score threshold if score > score_threshold: - doc.metadata["score"] = score - docs.append(doc) + if doc.metadata is not None: + doc.metadata["score"] = score + docs.append(doc) # Sort the documents by score in descending order - docs = sorted(docs, key=lambda x: x.metadata["score"], reverse=True) + docs = sorted(docs, key=lambda x: x.metadata.get("score", 0) if x.metadata else 0, reverse=True) return docs def search_by_full_text(self, query: str, **kwargs: Any) -> list[Document]: @@ -275,7 +277,7 @@ class WeaviateVectorFactory(AbstractVectorFactory): return WeaviateVector( collection_name=collection_name, config=WeaviateConfig( - endpoint=dify_config.WEAVIATE_ENDPOINT, + endpoint=dify_config.WEAVIATE_ENDPOINT or "", api_key=dify_config.WEAVIATE_API_KEY, batch_size=dify_config.WEAVIATE_BATCH_SIZE, ), diff --git a/api/core/rag/docstore/dataset_docstore.py b/api/core/rag/docstore/dataset_docstore.py index 319a2612c7..35becaa0c7 100644 --- a/api/core/rag/docstore/dataset_docstore.py +++ b/api/core/rag/docstore/dataset_docstore.py @@ -83,6 +83,9 @@ class DatasetDocumentStore: if not isinstance(doc, Document): raise ValueError("doc must be a Document") + if doc.metadata is None: + raise ValueError("doc.metadata must be a dict") + segment_document = self.get_document_segment(doc_id=doc.metadata["doc_id"]) # NOTE: doc could already exist in the store, but we overwrite it @@ -179,10 +182,10 @@ class DatasetDocumentStore: if document_segment is None: return None + data: Optional[str] = document_segment.index_node_hash + return data - return document_segment.index_node_hash - - def get_document_segment(self, doc_id: str) -> DocumentSegment: + def get_document_segment(self, doc_id: str) -> Optional[DocumentSegment]: document_segment = ( db.session.query(DocumentSegment) .filter(DocumentSegment.dataset_id == self._dataset.id, DocumentSegment.index_node_id == doc_id) diff --git a/api/core/rag/embedding/cached_embedding.py b/api/core/rag/embedding/cached_embedding.py index 8ddda7e983..a2c8737da7 100644 --- a/api/core/rag/embedding/cached_embedding.py +++ b/api/core/rag/embedding/cached_embedding.py @@ -1,6 +1,6 @@ import base64 import logging -from typing import Optional, cast +from typing import Any, Optional, cast import numpy as np from sqlalchemy.exc import IntegrityError @@ -27,7 +27,7 @@ class CacheEmbedding(Embeddings): def embed_documents(self, texts: list[str]) -> list[list[float]]: """Embed search docs in batches of 10.""" # use doc embedding cache or store if not exists - text_embeddings = [None for _ in range(len(texts))] + text_embeddings: list[Any] = [None for _ in range(len(texts))] embedding_queue_indices = [] for i, text in enumerate(texts): hash = helper.generate_text_hash(text) @@ -64,7 +64,8 @@ class CacheEmbedding(Embeddings): for vector in embedding_result.embeddings: try: - normalized_embedding = (vector / np.linalg.norm(vector)).tolist() + # FIXME: type ignore for numpy here + normalized_embedding = (vector / np.linalg.norm(vector)).tolist() # type: ignore # stackoverflow best way: https://stackoverflow.com/questions/20319813/how-to-check-list-containing-nan if np.isnan(normalized_embedding).any(): # for issue #11827 float values are not json compliant @@ -77,8 +78,8 @@ class CacheEmbedding(Embeddings): logging.exception("Failed transform embedding") cache_embeddings = [] try: - for i, embedding in zip(embedding_queue_indices, embedding_queue_embeddings): - text_embeddings[i] = embedding + for i, n_embedding in zip(embedding_queue_indices, embedding_queue_embeddings): + text_embeddings[i] = n_embedding hash = helper.generate_text_hash(texts[i]) if hash not in cache_embeddings: embedding_cache = Embedding( @@ -86,7 +87,7 @@ class CacheEmbedding(Embeddings): hash=hash, provider_name=self._model_instance.provider, ) - embedding_cache.set_embedding(embedding) + embedding_cache.set_embedding(n_embedding) db.session.add(embedding_cache) cache_embeddings.append(hash) db.session.commit() @@ -115,7 +116,8 @@ class CacheEmbedding(Embeddings): ) embedding_results = embedding_result.embeddings[0] - embedding_results = (embedding_results / np.linalg.norm(embedding_results)).tolist() + # FIXME: type ignore for numpy here + embedding_results = (embedding_results / np.linalg.norm(embedding_results)).tolist() # type: ignore if np.isnan(embedding_results).any(): raise ValueError("Normalized embedding is nan please try again") except Exception as ex: diff --git a/api/core/rag/extractor/entity/extract_setting.py b/api/core/rag/extractor/entity/extract_setting.py index 3692b5d19d..7c00c668dd 100644 --- a/api/core/rag/extractor/entity/extract_setting.py +++ b/api/core/rag/extractor/entity/extract_setting.py @@ -14,7 +14,7 @@ class NotionInfo(BaseModel): notion_workspace_id: str notion_obj_id: str notion_page_type: str - document: Document = None + document: Optional[Document] = None tenant_id: str model_config = ConfigDict(arbitrary_types_allowed=True) diff --git a/api/core/rag/extractor/excel_extractor.py b/api/core/rag/extractor/excel_extractor.py index fc33165719..c444105bb5 100644 --- a/api/core/rag/extractor/excel_extractor.py +++ b/api/core/rag/extractor/excel_extractor.py @@ -1,7 +1,7 @@ """Abstract interface for document loader implementations.""" import os -from typing import Optional +from typing import Optional, cast import pandas as pd from openpyxl import load_workbook @@ -47,7 +47,7 @@ class ExcelExtractor(BaseExtractor): for col_index, (k, v) in enumerate(row.items()): if pd.notna(v): cell = sheet.cell( - row=index + 2, column=col_index + 1 + row=cast(int, index) + 2, column=col_index + 1 ) # +2 to account for header and 1-based index if cell.hyperlink: value = f"[{v}]({cell.hyperlink.target})" @@ -60,8 +60,8 @@ class ExcelExtractor(BaseExtractor): elif file_extension == ".xls": excel_file = pd.ExcelFile(self._file_path, engine="xlrd") - for sheet_name in excel_file.sheet_names: - df = excel_file.parse(sheet_name=sheet_name) + for excel_sheet_name in excel_file.sheet_names: + df = excel_file.parse(sheet_name=excel_sheet_name) df.dropna(how="all", inplace=True) for _, row in df.iterrows(): diff --git a/api/core/rag/extractor/extract_processor.py b/api/core/rag/extractor/extract_processor.py index 69659e3108..a473b3dfa7 100644 --- a/api/core/rag/extractor/extract_processor.py +++ b/api/core/rag/extractor/extract_processor.py @@ -10,6 +10,7 @@ from core.rag.extractor.csv_extractor import CSVExtractor from core.rag.extractor.entity.datasource_type import DatasourceType from core.rag.extractor.entity.extract_setting import ExtractSetting from core.rag.extractor.excel_extractor import ExcelExtractor +from core.rag.extractor.extractor_base import BaseExtractor from core.rag.extractor.firecrawl.firecrawl_web_extractor import FirecrawlWebExtractor from core.rag.extractor.html_extractor import HtmlExtractor from core.rag.extractor.jina_reader_extractor import JinaReaderWebExtractor @@ -66,9 +67,13 @@ class ExtractProcessor: filename_match = re.search(r'filename="([^"]+)"', content_disposition) if filename_match: filename = unquote(filename_match.group(1)) - suffix = "." + re.search(r"\.(\w+)$", filename).group(1) - - file_path = f"{temp_dir}/{next(tempfile._get_candidate_names())}{suffix}" + match = re.search(r"\.(\w+)$", filename) + if match: + suffix = "." + match.group(1) + else: + suffix = "" + # FIXME mypy: Cannot determine type of 'tempfile._get_candidate_names' better not use it here + file_path = f"{temp_dir}/{next(tempfile._get_candidate_names())}{suffix}" # type: ignore Path(file_path).write_bytes(response.content) extract_setting = ExtractSetting(datasource_type="upload_file", document_model="text_model") if return_text: @@ -89,15 +94,20 @@ class ExtractProcessor: if extract_setting.datasource_type == DatasourceType.FILE.value: with tempfile.TemporaryDirectory() as temp_dir: if not file_path: + assert extract_setting.upload_file is not None, "upload_file is required" upload_file: UploadFile = extract_setting.upload_file suffix = Path(upload_file.key).suffix - file_path = f"{temp_dir}/{next(tempfile._get_candidate_names())}{suffix}" + # FIXME mypy: Cannot determine type of 'tempfile._get_candidate_names' better not use it here + file_path = f"{temp_dir}/{next(tempfile._get_candidate_names())}{suffix}" # type: ignore storage.download(upload_file.key, file_path) input_file = Path(file_path) file_extension = input_file.suffix.lower() etl_type = dify_config.ETL_TYPE unstructured_api_url = dify_config.UNSTRUCTURED_API_URL unstructured_api_key = dify_config.UNSTRUCTURED_API_KEY + assert unstructured_api_url is not None, "unstructured_api_url is required" + assert unstructured_api_key is not None, "unstructured_api_key is required" + extractor: Optional[BaseExtractor] = None if etl_type == "Unstructured": if file_extension in {".xlsx", ".xls"}: extractor = ExcelExtractor(file_path) @@ -156,6 +166,7 @@ class ExtractProcessor: extractor = TextExtractor(file_path, autodetect_encoding=True) return extractor.extract() elif extract_setting.datasource_type == DatasourceType.NOTION.value: + assert extract_setting.notion_info is not None, "notion_info is required" extractor = NotionExtractor( notion_workspace_id=extract_setting.notion_info.notion_workspace_id, notion_obj_id=extract_setting.notion_info.notion_obj_id, @@ -165,6 +176,7 @@ class ExtractProcessor: ) return extractor.extract() elif extract_setting.datasource_type == DatasourceType.WEBSITE.value: + assert extract_setting.website_info is not None, "website_info is required" if extract_setting.website_info.provider == "firecrawl": extractor = FirecrawlWebExtractor( url=extract_setting.website_info.url, diff --git a/api/core/rag/extractor/firecrawl/firecrawl_app.py b/api/core/rag/extractor/firecrawl/firecrawl_app.py index 17c2087a0a..8ae4579c7c 100644 --- a/api/core/rag/extractor/firecrawl/firecrawl_app.py +++ b/api/core/rag/extractor/firecrawl/firecrawl_app.py @@ -1,5 +1,6 @@ import json import time +from typing import cast import requests @@ -20,9 +21,9 @@ class FirecrawlApp: json_data.update(params) response = requests.post(f"{self.base_url}/v0/scrape", headers=headers, json=json_data) if response.status_code == 200: - response = response.json() - if response["success"] == True: - data = response["data"] + response_data = response.json() + if response_data["success"] == True: + data = response_data["data"] return { "title": data.get("metadata").get("title"), "description": data.get("metadata").get("description"), @@ -30,7 +31,7 @@ class FirecrawlApp: "markdown": data.get("markdown"), } else: - raise Exception(f'Failed to scrape URL. Error: {response["error"]}') + raise Exception(f'Failed to scrape URL. Error: {response_data["error"]}') elif response.status_code in {402, 409, 500}: error_message = response.json().get("error", "Unknown error occurred") @@ -46,9 +47,11 @@ class FirecrawlApp: response = self._post_request(f"{self.base_url}/v0/crawl", json_data, headers) if response.status_code == 200: job_id = response.json().get("jobId") - return job_id + return cast(str, job_id) else: self._handle_error(response, "start crawl job") + # FIXME: unreachable code for mypy + return "" # unreachable def check_crawl_status(self, job_id) -> dict: headers = self._prepare_headers() @@ -64,9 +67,9 @@ class FirecrawlApp: for item in data: if isinstance(item, dict) and "metadata" in item and "markdown" in item: url_data = { - "title": item.get("metadata").get("title"), - "description": item.get("metadata").get("description"), - "source_url": item.get("metadata").get("sourceURL"), + "title": item.get("metadata", {}).get("title"), + "description": item.get("metadata", {}).get("description"), + "source_url": item.get("metadata", {}).get("sourceURL"), "markdown": item.get("markdown"), } url_data_list.append(url_data) @@ -92,6 +95,8 @@ class FirecrawlApp: else: self._handle_error(response, "check crawl status") + # FIXME: unreachable code for mypy + return {} # unreachable def _prepare_headers(self): return {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} diff --git a/api/core/rag/extractor/html_extractor.py b/api/core/rag/extractor/html_extractor.py index 560c2d1d84..350b522347 100644 --- a/api/core/rag/extractor/html_extractor.py +++ b/api/core/rag/extractor/html_extractor.py @@ -1,6 +1,6 @@ """Abstract interface for document loader implementations.""" -from bs4 import BeautifulSoup +from bs4 import BeautifulSoup # type: ignore from core.rag.extractor.extractor_base import BaseExtractor from core.rag.models.document import Document @@ -23,6 +23,7 @@ class HtmlExtractor(BaseExtractor): return [Document(page_content=self._load_as_text())] def _load_as_text(self) -> str: + text: str = "" with open(self._file_path, "rb") as fp: soup = BeautifulSoup(fp, "html.parser") text = soup.get_text() diff --git a/api/core/rag/extractor/notion_extractor.py b/api/core/rag/extractor/notion_extractor.py index 87a4ce08bf..fdc2e46d14 100644 --- a/api/core/rag/extractor/notion_extractor.py +++ b/api/core/rag/extractor/notion_extractor.py @@ -1,6 +1,6 @@ import json import logging -from typing import Any, Optional +from typing import Any, Optional, cast import requests @@ -78,6 +78,7 @@ class NotionExtractor(BaseExtractor): def _get_notion_database_data(self, database_id: str, query_dict: dict[str, Any] = {}) -> list[Document]: """Get all the pages from a Notion database.""" + assert self._notion_access_token is not None, "Notion access token is required" res = requests.post( DATABASE_URL_TMPL.format(database_id=database_id), headers={ @@ -96,6 +97,7 @@ class NotionExtractor(BaseExtractor): for result in data["results"]: properties = result["properties"] data = {} + value: Any for property_name, property_value in properties.items(): type = property_value["type"] if type == "multi_select": @@ -130,6 +132,7 @@ class NotionExtractor(BaseExtractor): return [Document(page_content="\n".join(database_content))] def _get_notion_block_data(self, page_id: str) -> list[str]: + assert self._notion_access_token is not None, "Notion access token is required" result_lines_arr = [] start_cursor = None block_url = BLOCK_CHILD_URL_TMPL.format(block_id=page_id) @@ -184,6 +187,7 @@ class NotionExtractor(BaseExtractor): def _read_block(self, block_id: str, num_tabs: int = 0) -> str: """Read a block.""" + assert self._notion_access_token is not None, "Notion access token is required" result_lines_arr = [] start_cursor = None block_url = BLOCK_CHILD_URL_TMPL.format(block_id=block_id) @@ -242,6 +246,7 @@ class NotionExtractor(BaseExtractor): def _read_table_rows(self, block_id: str) -> str: """Read table rows.""" + assert self._notion_access_token is not None, "Notion access token is required" done = False result_lines_arr = [] start_cursor = None @@ -296,7 +301,7 @@ class NotionExtractor(BaseExtractor): result_lines = "\n".join(result_lines_arr) return result_lines - def update_last_edited_time(self, document_model: DocumentModel): + def update_last_edited_time(self, document_model: Optional[DocumentModel]): if not document_model: return @@ -309,6 +314,7 @@ class NotionExtractor(BaseExtractor): db.session.commit() def get_notion_last_edited_time(self) -> str: + assert self._notion_access_token is not None, "Notion access token is required" obj_id = self._notion_obj_id page_type = self._notion_page_type if page_type == "database": @@ -330,7 +336,7 @@ class NotionExtractor(BaseExtractor): ) data = res.json() - return data["last_edited_time"] + return cast(str, data["last_edited_time"]) @classmethod def _get_access_token(cls, tenant_id: str, notion_workspace_id: str) -> str: @@ -349,4 +355,4 @@ class NotionExtractor(BaseExtractor): f"and notion workspace {notion_workspace_id}" ) - return data_source_binding.access_token + return cast(str, data_source_binding.access_token) diff --git a/api/core/rag/extractor/pdf_extractor.py b/api/core/rag/extractor/pdf_extractor.py index 57cb9610ba..89a7061c26 100644 --- a/api/core/rag/extractor/pdf_extractor.py +++ b/api/core/rag/extractor/pdf_extractor.py @@ -1,7 +1,7 @@ """Abstract interface for document loader implementations.""" from collections.abc import Iterator -from typing import Optional +from typing import Optional, cast from core.rag.extractor.blob.blob import Blob from core.rag.extractor.extractor_base import BaseExtractor @@ -27,7 +27,7 @@ class PdfExtractor(BaseExtractor): plaintext_file_exists = False if self._file_cache_key: try: - text = storage.load(self._file_cache_key).decode("utf-8") + text = cast(bytes, storage.load(self._file_cache_key)).decode("utf-8") plaintext_file_exists = True return [Document(page_content=text)] except FileNotFoundError: @@ -53,7 +53,7 @@ class PdfExtractor(BaseExtractor): def parse(self, blob: Blob) -> Iterator[Document]: """Lazily parse the blob.""" - import pypdfium2 + import pypdfium2 # type: ignore with blob.as_bytes_io() as file_path: pdf_reader = pypdfium2.PdfDocument(file_path, autoclose=True) diff --git a/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py b/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py index bd669bbad3..9647dedfff 100644 --- a/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py +++ b/api/core/rag/extractor/unstructured/unstructured_eml_extractor.py @@ -1,7 +1,7 @@ import base64 import logging -from bs4 import BeautifulSoup +from bs4 import BeautifulSoup # type: ignore from core.rag.extractor.extractor_base import BaseExtractor from core.rag.models.document import Document diff --git a/api/core/rag/extractor/unstructured/unstructured_epub_extractor.py b/api/core/rag/extractor/unstructured/unstructured_epub_extractor.py index 35220b558a..80c29157aa 100644 --- a/api/core/rag/extractor/unstructured/unstructured_epub_extractor.py +++ b/api/core/rag/extractor/unstructured/unstructured_epub_extractor.py @@ -30,6 +30,9 @@ class UnstructuredEpubExtractor(BaseExtractor): if self._api_url: from unstructured.partition.api import partition_via_api + if self._api_key is None: + raise ValueError("api_key is required") + elements = partition_via_api(filename=self._file_path, api_url=self._api_url, api_key=self._api_key) else: from unstructured.partition.epub import partition_epub diff --git a/api/core/rag/extractor/unstructured/unstructured_ppt_extractor.py b/api/core/rag/extractor/unstructured/unstructured_ppt_extractor.py index 0fdcd58b2e..e504d4bc23 100644 --- a/api/core/rag/extractor/unstructured/unstructured_ppt_extractor.py +++ b/api/core/rag/extractor/unstructured/unstructured_ppt_extractor.py @@ -27,9 +27,11 @@ class UnstructuredPPTExtractor(BaseExtractor): elements = partition_via_api(filename=self._file_path, api_url=self._api_url, api_key=self._api_key) else: raise NotImplementedError("Unstructured API Url is not configured") - text_by_page = {} + text_by_page: dict[int, str] = {} for element in elements: page = element.metadata.page_number + if page is None: + continue text = element.text if page in text_by_page: text_by_page[page] += "\n" + text diff --git a/api/core/rag/extractor/unstructured/unstructured_pptx_extractor.py b/api/core/rag/extractor/unstructured/unstructured_pptx_extractor.py index ab41290fbc..cefe72b290 100644 --- a/api/core/rag/extractor/unstructured/unstructured_pptx_extractor.py +++ b/api/core/rag/extractor/unstructured/unstructured_pptx_extractor.py @@ -29,14 +29,15 @@ class UnstructuredPPTXExtractor(BaseExtractor): from unstructured.partition.pptx import partition_pptx elements = partition_pptx(filename=self._file_path) - text_by_page = {} + text_by_page: dict[int, str] = {} for element in elements: page = element.metadata.page_number text = element.text - if page in text_by_page: - text_by_page[page] += "\n" + text - else: - text_by_page[page] = text + if page is not None: + if page in text_by_page: + text_by_page[page] += "\n" + text + else: + text_by_page[page] = text combined_texts = list(text_by_page.values()) documents = [] diff --git a/api/core/rag/extractor/word_extractor.py b/api/core/rag/extractor/word_extractor.py index 0c38a9c076..c3161bc812 100644 --- a/api/core/rag/extractor/word_extractor.py +++ b/api/core/rag/extractor/word_extractor.py @@ -89,6 +89,8 @@ class WordExtractor(BaseExtractor): response = ssrf_proxy.get(url) if response.status_code == 200: image_ext = mimetypes.guess_extension(response.headers["Content-Type"]) + if image_ext is None: + continue file_uuid = str(uuid.uuid4()) file_key = "image_files/" + self.tenant_id + "/" + file_uuid + "." + image_ext mime_type, _ = mimetypes.guess_type(file_key) @@ -97,6 +99,8 @@ class WordExtractor(BaseExtractor): continue else: image_ext = rel.target_ref.split(".")[-1] + if image_ext is None: + continue # user uuid as file name file_uuid = str(uuid.uuid4()) file_key = "image_files/" + self.tenant_id + "/" + file_uuid + "." + image_ext @@ -226,6 +230,8 @@ class WordExtractor(BaseExtractor): if x_child is None: continue if x.tag.endswith("instrText"): + if x.text is None: + continue for i in url_pattern.findall(x.text): hyperlinks_url = str(i) except Exception as e: diff --git a/api/core/rag/index_processor/index_processor_base.py b/api/core/rag/index_processor/index_processor_base.py index be857bd122..7e5efdc66e 100644 --- a/api/core/rag/index_processor/index_processor_base.py +++ b/api/core/rag/index_processor/index_processor_base.py @@ -49,6 +49,7 @@ class BaseIndexProcessor(ABC): """ Get the NodeParser object according to the processing rule. """ + character_splitter: TextSplitter if processing_rule["mode"] == "custom": # The user-defined segmentation rule rules = processing_rule["rules"] diff --git a/api/core/rag/index_processor/index_processor_factory.py b/api/core/rag/index_processor/index_processor_factory.py index 9b855ece2c..c5ba6295f3 100644 --- a/api/core/rag/index_processor/index_processor_factory.py +++ b/api/core/rag/index_processor/index_processor_factory.py @@ -9,7 +9,7 @@ from core.rag.index_processor.processor.qa_index_processor import QAIndexProcess class IndexProcessorFactory: """IndexProcessorInit.""" - def __init__(self, index_type: str): + def __init__(self, index_type: str | None): self._index_type = index_type def init_index_processor(self) -> BaseIndexProcessor: diff --git a/api/core/rag/index_processor/processor/paragraph_index_processor.py b/api/core/rag/index_processor/processor/paragraph_index_processor.py index a631f953ce..c66fa54d50 100644 --- a/api/core/rag/index_processor/processor/paragraph_index_processor.py +++ b/api/core/rag/index_processor/processor/paragraph_index_processor.py @@ -27,12 +27,13 @@ class ParagraphIndexProcessor(BaseIndexProcessor): def transform(self, documents: list[Document], **kwargs) -> list[Document]: # Split the text documents into nodes. splitter = self._get_splitter( - processing_rule=kwargs.get("process_rule"), embedding_model_instance=kwargs.get("embedding_model_instance") + processing_rule=kwargs.get("process_rule", {}), + embedding_model_instance=kwargs.get("embedding_model_instance"), ) all_documents = [] for document in documents: # document clean - document_text = CleanProcessor.clean(document.page_content, kwargs.get("process_rule")) + document_text = CleanProcessor.clean(document.page_content, kwargs.get("process_rule", {})) document.page_content = document_text # parse document to nodes document_nodes = splitter.split_documents([document]) @@ -41,8 +42,9 @@ class ParagraphIndexProcessor(BaseIndexProcessor): if document_node.page_content.strip(): doc_id = str(uuid.uuid4()) hash = helper.generate_text_hash(document_node.page_content) - document_node.metadata["doc_id"] = doc_id - document_node.metadata["doc_hash"] = hash + if document_node.metadata is not None: + document_node.metadata["doc_id"] = doc_id + document_node.metadata["doc_hash"] = hash # delete Splitter character page_content = remove_leading_symbols(document_node.page_content).strip() if len(page_content) > 0: diff --git a/api/core/rag/index_processor/processor/qa_index_processor.py b/api/core/rag/index_processor/processor/qa_index_processor.py index 320f0157a1..20fd16e8f3 100644 --- a/api/core/rag/index_processor/processor/qa_index_processor.py +++ b/api/core/rag/index_processor/processor/qa_index_processor.py @@ -32,15 +32,16 @@ class QAIndexProcessor(BaseIndexProcessor): def transform(self, documents: list[Document], **kwargs) -> list[Document]: splitter = self._get_splitter( - processing_rule=kwargs.get("process_rule"), embedding_model_instance=kwargs.get("embedding_model_instance") + processing_rule=kwargs.get("process_rule") or {}, + embedding_model_instance=kwargs.get("embedding_model_instance"), ) # Split the text documents into nodes. - all_documents = [] - all_qa_documents = [] + all_documents: list[Document] = [] + all_qa_documents: list[Document] = [] for document in documents: # document clean - document_text = CleanProcessor.clean(document.page_content, kwargs.get("process_rule")) + document_text = CleanProcessor.clean(document.page_content, kwargs.get("process_rule") or {}) document.page_content = document_text # parse document to nodes @@ -50,8 +51,9 @@ class QAIndexProcessor(BaseIndexProcessor): if document_node.page_content.strip(): doc_id = str(uuid.uuid4()) hash = helper.generate_text_hash(document_node.page_content) - document_node.metadata["doc_id"] = doc_id - document_node.metadata["doc_hash"] = hash + if document_node.metadata is not None: + document_node.metadata["doc_id"] = doc_id + document_node.metadata["doc_hash"] = hash # delete Splitter character page_content = document_node.page_content document_node.page_content = remove_leading_symbols(page_content) @@ -64,7 +66,7 @@ class QAIndexProcessor(BaseIndexProcessor): document_format_thread = threading.Thread( target=self._format_qa_document, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "tenant_id": kwargs.get("tenant_id"), "document_node": doc, "all_qa_documents": all_qa_documents, @@ -148,11 +150,12 @@ class QAIndexProcessor(BaseIndexProcessor): qa_documents = [] for result in document_qa_list: qa_document = Document(page_content=result["question"], metadata=document_node.metadata.copy()) - doc_id = str(uuid.uuid4()) - hash = helper.generate_text_hash(result["question"]) - qa_document.metadata["answer"] = result["answer"] - qa_document.metadata["doc_id"] = doc_id - qa_document.metadata["doc_hash"] = hash + if qa_document.metadata is not None: + doc_id = str(uuid.uuid4()) + hash = helper.generate_text_hash(result["question"]) + qa_document.metadata["answer"] = result["answer"] + qa_document.metadata["doc_id"] = doc_id + qa_document.metadata["doc_hash"] = hash qa_documents.append(qa_document) format_documents.extend(qa_documents) except Exception as e: diff --git a/api/core/rag/rerank/rerank_model.py b/api/core/rag/rerank/rerank_model.py index 6ae432a526..ac7a3f8bb8 100644 --- a/api/core/rag/rerank/rerank_model.py +++ b/api/core/rag/rerank/rerank_model.py @@ -30,7 +30,11 @@ class RerankModelRunner(BaseRerankRunner): doc_ids = set() unique_documents = [] for document in documents: - if document.provider == "dify" and document.metadata["doc_id"] not in doc_ids: + if ( + document.provider == "dify" + and document.metadata is not None + and document.metadata["doc_id"] not in doc_ids + ): doc_ids.add(document.metadata["doc_id"]) docs.append(document.page_content) unique_documents.append(document) @@ -54,7 +58,8 @@ class RerankModelRunner(BaseRerankRunner): metadata=documents[result.index].metadata, provider=documents[result.index].provider, ) - rerank_document.metadata["score"] = result.score - rerank_documents.append(rerank_document) + if rerank_document.metadata is not None: + rerank_document.metadata["score"] = result.score + rerank_documents.append(rerank_document) return rerank_documents diff --git a/api/core/rag/rerank/weight_rerank.py b/api/core/rag/rerank/weight_rerank.py index 4719be012f..cbc96037bf 100644 --- a/api/core/rag/rerank/weight_rerank.py +++ b/api/core/rag/rerank/weight_rerank.py @@ -39,7 +39,7 @@ class WeightRerankRunner(BaseRerankRunner): unique_documents = [] doc_ids = set() for document in documents: - if document.metadata["doc_id"] not in doc_ids: + if document.metadata is not None and document.metadata["doc_id"] not in doc_ids: doc_ids.add(document.metadata["doc_id"]) unique_documents.append(document) @@ -56,10 +56,11 @@ class WeightRerankRunner(BaseRerankRunner): ) if score_threshold and score < score_threshold: continue - document.metadata["score"] = score - rerank_documents.append(document) + if document.metadata is not None: + document.metadata["score"] = score + rerank_documents.append(document) - rerank_documents.sort(key=lambda x: x.metadata["score"], reverse=True) + rerank_documents.sort(key=lambda x: x.metadata["score"] if x.metadata else 0, reverse=True) return rerank_documents[:top_n] if top_n else rerank_documents def _calculate_keyword_score(self, query: str, documents: list[Document]) -> list[float]: @@ -76,8 +77,9 @@ class WeightRerankRunner(BaseRerankRunner): for document in documents: # get the document keywords document_keywords = keyword_table_handler.extract_keywords(document.page_content, None) - document.metadata["keywords"] = document_keywords - documents_keywords.append(document_keywords) + if document.metadata is not None: + document.metadata["keywords"] = document_keywords + documents_keywords.append(document_keywords) # Counter query keywords(TF) query_keyword_counts = Counter(query_keywords) @@ -162,7 +164,7 @@ class WeightRerankRunner(BaseRerankRunner): query_vector = cache_embedding.embed_query(query) for document in documents: # calculate cosine similarity - if "score" in document.metadata: + if document.metadata and "score" in document.metadata: query_vector_scores.append(document.metadata["score"]) else: # transform to NumPy diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index 7a5bf39fa6..a265f36671 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -1,7 +1,7 @@ import math import threading from collections import Counter -from typing import Optional, cast +from typing import Any, Optional, cast from flask import Flask, current_app @@ -34,7 +34,7 @@ from models.dataset import Dataset, DatasetQuery, DocumentSegment from models.dataset import Document as DatasetDocument from services.external_knowledge_service import ExternalDatasetService -default_retrieval_model = { +default_retrieval_model: dict[str, Any] = { "search_method": RetrievalMethod.SEMANTIC_SEARCH.value, "reranking_enable": False, "reranking_model": {"reranking_provider_name": "", "reranking_model_name": ""}, @@ -140,12 +140,12 @@ class DatasetRetrieval: user_from, available_datasets, query, - retrieve_config.top_k, - retrieve_config.score_threshold, - retrieve_config.rerank_mode, + retrieve_config.top_k or 0, + retrieve_config.score_threshold or 0, + retrieve_config.rerank_mode or "reranking_model", retrieve_config.reranking_model, retrieve_config.weights, - retrieve_config.reranking_enabled, + retrieve_config.reranking_enabled or True, message_id, ) @@ -300,10 +300,11 @@ class DatasetRetrieval: metadata=external_document.get("metadata"), provider="external", ) - document.metadata["score"] = external_document.get("score") - document.metadata["title"] = external_document.get("title") - document.metadata["dataset_id"] = dataset_id - document.metadata["dataset_name"] = dataset.name + if document.metadata is not None: + document.metadata["score"] = external_document.get("score") + document.metadata["title"] = external_document.get("title") + document.metadata["dataset_id"] = dataset_id + document.metadata["dataset_name"] = dataset.name results.append(document) else: retrieval_model_config = dataset.retrieval_model or default_retrieval_model @@ -325,7 +326,7 @@ class DatasetRetrieval: score_threshold = 0.0 score_threshold_enabled = retrieval_model_config.get("score_threshold_enabled") if score_threshold_enabled: - score_threshold = retrieval_model_config.get("score_threshold") + score_threshold = retrieval_model_config.get("score_threshold", 0.0) with measure_time() as timer: results = RetrievalService.retrieve( @@ -358,14 +359,14 @@ class DatasetRetrieval: score_threshold: float, reranking_mode: str, reranking_model: Optional[dict] = None, - weights: Optional[dict] = None, + weights: Optional[dict[str, Any]] = None, reranking_enable: bool = True, message_id: Optional[str] = None, ): if not available_datasets: return [] threads = [] - all_documents = [] + all_documents: list[Document] = [] dataset_ids = [dataset.id for dataset in available_datasets] index_type_check = all( item.indexing_technique == available_datasets[0].indexing_technique for item in available_datasets @@ -392,15 +393,18 @@ class DatasetRetrieval: "The configured knowledge base list have different embedding model, please set reranking model." ) if reranking_enable and reranking_mode == RerankMode.WEIGHTED_SCORE: - weights["vector_setting"]["embedding_provider_name"] = available_datasets[0].embedding_model_provider - weights["vector_setting"]["embedding_model_name"] = available_datasets[0].embedding_model + if weights is not None: + weights["vector_setting"]["embedding_provider_name"] = available_datasets[ + 0 + ].embedding_model_provider + weights["vector_setting"]["embedding_model_name"] = available_datasets[0].embedding_model for dataset in available_datasets: index_type = dataset.indexing_technique retrieval_thread = threading.Thread( target=self._retriever, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "dataset_id": dataset.id, "query": query, "top_k": top_k, @@ -439,21 +443,22 @@ class DatasetRetrieval: """Handle retrieval end.""" dify_documents = [document for document in documents if document.provider == "dify"] for document in dify_documents: - query = db.session.query(DocumentSegment).filter( - DocumentSegment.index_node_id == document.metadata["doc_id"] - ) + if document.metadata is not None: + query = db.session.query(DocumentSegment).filter( + DocumentSegment.index_node_id == document.metadata["doc_id"] + ) - # if 'dataset_id' in document.metadata: - if "dataset_id" in document.metadata: - query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) + # if 'dataset_id' in document.metadata: + if "dataset_id" in document.metadata: + query = query.filter(DocumentSegment.dataset_id == document.metadata["dataset_id"]) - # add hit count to document segment - query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) + # add hit count to document segment + query.update({DocumentSegment.hit_count: DocumentSegment.hit_count + 1}, synchronize_session=False) - db.session.commit() + db.session.commit() # get tracing instance - trace_manager: TraceQueueManager = ( + trace_manager: Optional[TraceQueueManager] = ( self.application_generate_entity.trace_manager if self.application_generate_entity else None ) if trace_manager: @@ -504,10 +509,11 @@ class DatasetRetrieval: metadata=external_document.get("metadata"), provider="external", ) - document.metadata["score"] = external_document.get("score") - document.metadata["title"] = external_document.get("title") - document.metadata["dataset_id"] = dataset_id - document.metadata["dataset_name"] = dataset.name + if document.metadata is not None: + document.metadata["score"] = external_document.get("score") + document.metadata["title"] = external_document.get("title") + document.metadata["dataset_id"] = dataset_id + document.metadata["dataset_name"] = dataset.name all_documents.append(document) else: # get retrieval model , if the model is not setting , using default @@ -607,19 +613,20 @@ class DatasetRetrieval: tools.append(tool) elif retrieve_config.retrieve_strategy == DatasetRetrieveConfigEntity.RetrieveStrategy.MULTIPLE: - tool = DatasetMultiRetrieverTool.from_dataset( - dataset_ids=[dataset.id for dataset in available_datasets], - tenant_id=tenant_id, - top_k=retrieve_config.top_k or 2, - score_threshold=retrieve_config.score_threshold, - hit_callbacks=[hit_callback], - return_resource=return_resource, - retriever_from=invoke_from.to_source(), - reranking_provider_name=retrieve_config.reranking_model.get("reranking_provider_name"), - reranking_model_name=retrieve_config.reranking_model.get("reranking_model_name"), - ) + if retrieve_config.reranking_model is not None: + tool = DatasetMultiRetrieverTool.from_dataset( + dataset_ids=[dataset.id for dataset in available_datasets], + tenant_id=tenant_id, + top_k=retrieve_config.top_k or 2, + score_threshold=retrieve_config.score_threshold, + hit_callbacks=[hit_callback], + return_resource=return_resource, + retriever_from=invoke_from.to_source(), + reranking_provider_name=retrieve_config.reranking_model.get("reranking_provider_name"), + reranking_model_name=retrieve_config.reranking_model.get("reranking_model_name"), + ) - tools.append(tool) + tools.append(tool) return tools @@ -635,10 +642,11 @@ class DatasetRetrieval: query_keywords = keyword_table_handler.extract_keywords(query, None) documents_keywords = [] for document in documents: - # get the document keywords - document_keywords = keyword_table_handler.extract_keywords(document.page_content, None) - document.metadata["keywords"] = document_keywords - documents_keywords.append(document_keywords) + if document.metadata is not None: + # get the document keywords + document_keywords = keyword_table_handler.extract_keywords(document.page_content, None) + document.metadata["keywords"] = document_keywords + documents_keywords.append(document_keywords) # Counter query keywords(TF) query_keyword_counts = Counter(query_keywords) @@ -696,8 +704,9 @@ class DatasetRetrieval: for document, score in zip(documents, similarities): # format document - document.metadata["score"] = score - documents = sorted(documents, key=lambda x: x.metadata["score"], reverse=True) + if document.metadata is not None: + document.metadata["score"] = score + documents = sorted(documents, key=lambda x: x.metadata.get("score", 0) if x.metadata else 0, reverse=True) return documents[:top_k] if top_k else documents def calculate_vector_score( @@ -705,10 +714,12 @@ class DatasetRetrieval: ) -> list[Document]: filter_documents = [] for document in all_documents: - if score_threshold is None or document.metadata["score"] >= score_threshold: + if score_threshold is None or (document.metadata and document.metadata.get("score", 0) >= score_threshold): filter_documents.append(document) if not filter_documents: return [] - filter_documents = sorted(filter_documents, key=lambda x: x.metadata["score"], reverse=True) + filter_documents = sorted( + filter_documents, key=lambda x: x.metadata.get("score", 0) if x.metadata else 0, reverse=True + ) return filter_documents[:top_k] if top_k else filter_documents diff --git a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py index 06147fe7b5..b008d0df9c 100644 --- a/api/core/rag/retrieval/router/multi_dataset_function_call_router.py +++ b/api/core/rag/retrieval/router/multi_dataset_function_call_router.py @@ -1,7 +1,8 @@ -from typing import Union +from typing import Union, cast from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.model_manager import ModelInstance +from core.model_runtime.entities.llm_entities import LLMResult from core.model_runtime.entities.message_entities import PromptMessageTool, SystemPromptMessage, UserPromptMessage @@ -27,11 +28,14 @@ class FunctionCallMultiDatasetRouter: SystemPromptMessage(content="You are a helpful AI assistant."), UserPromptMessage(content=query), ] - result = model_instance.invoke_llm( - prompt_messages=prompt_messages, - tools=dataset_tools, - stream=False, - model_parameters={"temperature": 0.2, "top_p": 0.3, "max_tokens": 1500}, + result = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, + tools=dataset_tools, + stream=False, + model_parameters={"temperature": 0.2, "top_p": 0.3, "max_tokens": 1500}, + ), ) if result.message.tool_calls: # get retrieval model config diff --git a/api/core/rag/retrieval/router/multi_dataset_react_route.py b/api/core/rag/retrieval/router/multi_dataset_react_route.py index 68fab0c127..05e8d043df 100644 --- a/api/core/rag/retrieval/router/multi_dataset_react_route.py +++ b/api/core/rag/retrieval/router/multi_dataset_react_route.py @@ -1,9 +1,9 @@ from collections.abc import Generator, Sequence -from typing import Union +from typing import Union, cast from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.model_manager import ModelInstance -from core.model_runtime.entities.llm_entities import LLMUsage +from core.model_runtime.entities.llm_entities import LLMResult, LLMUsage from core.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool from core.prompt.advanced_prompt_transform import AdvancedPromptTransform from core.prompt.entities.advanced_prompt_entities import ChatModelMessage, CompletionModelPromptTemplate @@ -92,6 +92,7 @@ class ReactMultiDatasetRouter: suffix: str = SUFFIX, format_instructions: str = FORMAT_INSTRUCTIONS, ) -> Union[str, None]: + prompt: Union[list[ChatModelMessage], CompletionModelPromptTemplate] if model_config.mode == "chat": prompt = self.create_chat_prompt( query=query, @@ -149,12 +150,15 @@ class ReactMultiDatasetRouter: :param stop: stop :return: """ - invoke_result = model_instance.invoke_llm( - prompt_messages=prompt_messages, - model_parameters=completion_param, - stop=stop, - stream=True, - user=user_id, + invoke_result = cast( + Generator[LLMResult, None, None], + model_instance.invoke_llm( + prompt_messages=prompt_messages, + model_parameters=completion_param, + stop=stop, + stream=True, + user=user_id, + ), ) # handle invoke result @@ -172,7 +176,7 @@ class ReactMultiDatasetRouter: :return: """ model = None - prompt_messages = [] + prompt_messages: list[PromptMessage] = [] full_text = "" usage = None for result in invoke_result: diff --git a/api/core/rag/splitter/fixed_text_splitter.py b/api/core/rag/splitter/fixed_text_splitter.py index 53032b34d5..3376bd7f75 100644 --- a/api/core/rag/splitter/fixed_text_splitter.py +++ b/api/core/rag/splitter/fixed_text_splitter.py @@ -26,8 +26,8 @@ class EnhanceRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter): def from_encoder( cls: type[TS], embedding_model_instance: Optional[ModelInstance], - allowed_special: Union[Literal[all], Set[str]] = set(), - disallowed_special: Union[Literal[all], Collection[str]] = "all", + allowed_special: Union[Literal["all"], Set[str]] = set(), # noqa: UP037 + disallowed_special: Union[Literal["all"], Collection[str]] = "all", # noqa: UP037 **kwargs: Any, ): def _token_encoder(text: str) -> int: diff --git a/api/core/rag/splitter/text_splitter.py b/api/core/rag/splitter/text_splitter.py index 7dd62f8de1..4bfa541fd4 100644 --- a/api/core/rag/splitter/text_splitter.py +++ b/api/core/rag/splitter/text_splitter.py @@ -92,7 +92,7 @@ class TextSplitter(BaseDocumentTransformer, ABC): texts, metadatas = [], [] for doc in documents: texts.append(doc.page_content) - metadatas.append(doc.metadata) + metadatas.append(doc.metadata or {}) return self.create_documents(texts, metadatas=metadatas) def _join_docs(self, docs: list[str], separator: str) -> Optional[str]: @@ -143,7 +143,7 @@ class TextSplitter(BaseDocumentTransformer, ABC): def from_huggingface_tokenizer(cls, tokenizer: Any, **kwargs: Any) -> TextSplitter: """Text splitter that uses HuggingFace tokenizer to count length.""" try: - from transformers import PreTrainedTokenizerBase + from transformers import PreTrainedTokenizerBase # type: ignore if not isinstance(tokenizer, PreTrainedTokenizerBase): raise ValueError("Tokenizer received was not an instance of PreTrainedTokenizerBase") diff --git a/api/core/tools/entities/api_entities.py b/api/core/tools/entities/api_entities.py index ddb1481276..975c374cae 100644 --- a/api/core/tools/entities/api_entities.py +++ b/api/core/tools/entities/api_entities.py @@ -14,7 +14,7 @@ class UserTool(BaseModel): label: I18nObject # label description: I18nObject parameters: Optional[list[ToolParameter]] = None - labels: list[str] = None + labels: list[str] | None = None UserToolProviderTypeLiteral = Optional[Literal["builtin", "api", "workflow"]] diff --git a/api/core/tools/entities/tool_bundle.py b/api/core/tools/entities/tool_bundle.py index 0c15b2a371..7c365dc69d 100644 --- a/api/core/tools/entities/tool_bundle.py +++ b/api/core/tools/entities/tool_bundle.py @@ -18,7 +18,7 @@ class ApiToolBundle(BaseModel): # summary summary: Optional[str] = None # operation_id - operation_id: str = None + operation_id: str | None = None # parameters parameters: Optional[list[ToolParameter]] = None # author diff --git a/api/core/tools/entities/tool_entities.py b/api/core/tools/entities/tool_entities.py index 4fc383f91b..260e4e457f 100644 --- a/api/core/tools/entities/tool_entities.py +++ b/api/core/tools/entities/tool_entities.py @@ -244,18 +244,19 @@ class ToolParameter(BaseModel): """ # convert options to ToolParameterOption if options: - options = [ + options_tool_parametor = [ ToolParameterOption(value=option, label=I18nObject(en_US=option, zh_Hans=option)) for option in options ] return cls( name=name, label=I18nObject(en_US="", zh_Hans=""), human_description=I18nObject(en_US="", zh_Hans=""), + placeholder=None, type=type, form=cls.ToolParameterForm.LLM, llm_description=llm_description, required=required, - options=options, + options=options_tool_parametor, ) @@ -331,7 +332,7 @@ class ToolProviderCredentials(BaseModel): "default": self.default, "options": self.options, "help": self.help.to_dict() if self.help else None, - "label": self.label.to_dict(), + "label": self.label.to_dict() if self.label else None, "url": self.url, "placeholder": self.placeholder.to_dict() if self.placeholder else None, } @@ -374,7 +375,10 @@ class ToolRuntimeVariablePool(BaseModel): pool[index] = ToolRuntimeImageVariable(**variable) super().__init__(**data) - def dict(self) -> dict: + def dict(self) -> dict: # type: ignore + """ + FIXME: just ignore the type check for now + """ return { "conversation_id": self.conversation_id, "user_id": self.user_id, diff --git a/api/core/tools/provider/api_tool_provider.py b/api/core/tools/provider/api_tool_provider.py index d99314e33a..f451edbf2e 100644 --- a/api/core/tools/provider/api_tool_provider.py +++ b/api/core/tools/provider/api_tool_provider.py @@ -1,9 +1,14 @@ +from typing import Optional + from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_bundle import ApiToolBundle from core.tools.entities.tool_entities import ( ApiProviderAuthType, ToolCredentialsOption, + ToolDescription, + ToolIdentity, ToolProviderCredentials, + ToolProviderIdentity, ToolProviderType, ) from core.tools.provider.tool_provider import ToolProviderController @@ -64,21 +69,18 @@ class ApiToolProviderController(ToolProviderController): pass else: raise ValueError(f"invalid auth type {auth_type}") - - user_name = db_provider.user.name if db_provider.user_id else "" - + user_name = db_provider.user.name if db_provider.user_id and db_provider.user is not None else "" return ApiToolProviderController( - **{ - "identity": { - "author": user_name, - "name": db_provider.name, - "label": {"en_US": db_provider.name, "zh_Hans": db_provider.name}, - "description": {"en_US": db_provider.description, "zh_Hans": db_provider.description}, - "icon": db_provider.icon, - }, - "credentials_schema": credentials_schema, - "provider_id": db_provider.id or "", - } + identity=ToolProviderIdentity( + author=user_name, + name=db_provider.name, + label=I18nObject(en_US=db_provider.name, zh_Hans=db_provider.name), + description=I18nObject(en_US=db_provider.description, zh_Hans=db_provider.description), + icon=db_provider.icon, + ), + credentials_schema=credentials_schema, + provider_id=db_provider.id or "", + tools=None, ) @property @@ -93,24 +95,22 @@ class ApiToolProviderController(ToolProviderController): :return: the tool """ return ApiTool( - **{ - "api_bundle": tool_bundle, - "identity": { - "author": tool_bundle.author, - "name": tool_bundle.operation_id, - "label": {"en_US": tool_bundle.operation_id, "zh_Hans": tool_bundle.operation_id}, - "icon": self.identity.icon, - "provider": self.provider_id, - }, - "description": { - "human": {"en_US": tool_bundle.summary or "", "zh_Hans": tool_bundle.summary or ""}, - "llm": tool_bundle.summary or "", - }, - "parameters": tool_bundle.parameters or [], - } + api_bundle=tool_bundle, + identity=ToolIdentity( + author=tool_bundle.author, + name=tool_bundle.operation_id or "", + label=I18nObject(en_US=tool_bundle.operation_id, zh_Hans=tool_bundle.operation_id), + icon=self.identity.icon if self.identity else None, + provider=self.provider_id, + ), + description=ToolDescription( + human=I18nObject(en_US=tool_bundle.summary or "", zh_Hans=tool_bundle.summary or ""), + llm=tool_bundle.summary or "", + ), + parameters=tool_bundle.parameters or [], ) - def load_bundled_tools(self, tools: list[ApiToolBundle]) -> list[ApiTool]: + def load_bundled_tools(self, tools: list[ApiToolBundle]) -> list[Tool]: """ load bundled tools @@ -121,7 +121,7 @@ class ApiToolProviderController(ToolProviderController): return self.tools - def get_tools(self, user_id: str, tenant_id: str) -> list[ApiTool]: + def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]: """ fetch tools from database @@ -131,6 +131,8 @@ class ApiToolProviderController(ToolProviderController): """ if self.tools is not None: return self.tools + if self.identity is None: + return None tools: list[Tool] = [] @@ -151,7 +153,7 @@ class ApiToolProviderController(ToolProviderController): self.tools = tools return tools - def get_tool(self, tool_name: str) -> ApiTool: + def get_tool(self, tool_name: str) -> Tool: """ get tool by name @@ -161,7 +163,9 @@ class ApiToolProviderController(ToolProviderController): if self.tools is None: self.get_tools() - for tool in self.tools: + for tool in self.tools or []: + if tool.identity is None: + continue if tool.identity.name == tool_name: return tool diff --git a/api/core/tools/provider/app_tool_provider.py b/api/core/tools/provider/app_tool_provider.py index 582ad636b1..fc29920acd 100644 --- a/api/core/tools/provider/app_tool_provider.py +++ b/api/core/tools/provider/app_tool_provider.py @@ -1,9 +1,10 @@ import logging -from typing import Any +from typing import Any, Optional from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ToolParameter, ToolParameterOption, ToolProviderType from core.tools.provider.tool_provider import ToolProviderController +from core.tools.tool.api_tool import ApiTool from core.tools.tool.tool import Tool from extensions.ext_database import db from models.model import App, AppModelConfig @@ -20,10 +21,10 @@ class AppToolProviderEntity(ToolProviderController): def _validate_credentials(self, tool_name: str, credentials: dict[str, Any]) -> None: pass - def validate_parameters(self, tool_name: str, tool_parameters: dict[str, Any]) -> None: + def validate_parameters(self, tool_id: int, tool_name: str, tool_parameters: dict[str, Any]) -> None: pass - def get_tools(self, user_id: str) -> list[Tool]: + def get_tools(self, user_id: str = "", tenant_id: str = "") -> list[Tool]: db_tools: list[PublishedAppTool] = ( db.session.query(PublishedAppTool) .filter( @@ -38,7 +39,7 @@ class AppToolProviderEntity(ToolProviderController): tools: list[Tool] = [] for db_tool in db_tools: - tool = { + tool: dict[str, Any] = { "identity": { "author": db_tool.author, "name": db_tool.tool_name, @@ -52,7 +53,7 @@ class AppToolProviderEntity(ToolProviderController): "parameters": [], } # get app from db - app: App = db_tool.app + app: Optional[App] = db_tool.app if not app: logger.error(f"app {db_tool.app_id} not found") @@ -79,6 +80,7 @@ class AppToolProviderEntity(ToolProviderController): type=ToolParameter.ToolParameterType.STRING, required=required, default=default, + placeholder=I18nObject(en_US="", zh_Hans=""), ) ) elif form_type == "select": @@ -92,6 +94,7 @@ class AppToolProviderEntity(ToolProviderController): type=ToolParameter.ToolParameterType.SELECT, required=required, default=default, + placeholder=I18nObject(en_US="", zh_Hans=""), options=[ ToolParameterOption(value=option, label=I18nObject(en_US=option, zh_Hans=option)) for option in options @@ -99,5 +102,5 @@ class AppToolProviderEntity(ToolProviderController): ) ) - tools.append(Tool(**tool)) + tools.append(ApiTool(**tool)) return tools diff --git a/api/core/tools/provider/builtin/_positions.py b/api/core/tools/provider/builtin/_positions.py index 5c10f72fda..99a062f8c3 100644 --- a/api/core/tools/provider/builtin/_positions.py +++ b/api/core/tools/provider/builtin/_positions.py @@ -5,7 +5,7 @@ from core.tools.entities.api_entities import UserToolProvider class BuiltinToolProviderSort: - _position = {} + _position: dict[str, int] = {} @classmethod def sort(cls, providers: list[UserToolProvider]) -> list[UserToolProvider]: diff --git a/api/core/tools/provider/builtin/aippt/tools/aippt.py b/api/core/tools/provider/builtin/aippt/tools/aippt.py index 38123f125a..cf10f5d255 100644 --- a/api/core/tools/provider/builtin/aippt/tools/aippt.py +++ b/api/core/tools/provider/builtin/aippt/tools/aippt.py @@ -4,7 +4,7 @@ from hmac import new as hmac_new from json import loads as json_loads from threading import Lock from time import sleep, time -from typing import Any +from typing import Any, Union from httpx import get, post from requests import get as requests_get @@ -21,23 +21,25 @@ class AIPPTGenerateToolAdapter: """ _api_base_url = URL("https://co.aippt.cn/api") - _api_token_cache = {} - _style_cache = {} + _api_token_cache: dict[str, dict[str, Union[str, float]]] = {} + _style_cache: dict[str, dict[str, Union[list[dict[str, Any]], float]]] = {} - _api_token_cache_lock = Lock() - _style_cache_lock = Lock() + _api_token_cache_lock: Lock = Lock() + _style_cache_lock: Lock = Lock() - _task = {} + _task: dict[str, Any] = {} _task_type_map = { "auto": 1, "markdown": 7, } - _tool: BuiltinTool + _tool: BuiltinTool | None - def __init__(self, tool: BuiltinTool = None): + def __init__(self, tool: BuiltinTool | None = None): self._tool = tool - def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]: + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: """ Invokes the AIPPT generate tool with the given user ID and tool parameters. @@ -68,8 +70,8 @@ class AIPPTGenerateToolAdapter: ) # get suit - color: str = tool_parameters.get("color") - style: str = tool_parameters.get("style") + color: str = tool_parameters.get("color", "") + style: str = tool_parameters.get("style", "") if color == "__default__": color_id = "" @@ -226,7 +228,7 @@ class AIPPTGenerateToolAdapter: return "" - def _generate_ppt(self, task_id: str, suit_id: int, user_id) -> tuple[str, str]: + def _generate_ppt(self, task_id: str, suit_id: int, user_id: str) -> tuple[str, str]: """ Generate a ppt @@ -362,7 +364,9 @@ class AIPPTGenerateToolAdapter: ).decode("utf-8") @classmethod - def _get_styles(cls, credentials: dict[str, str], user_id: str) -> tuple[list[dict], list[dict]]: + def _get_styles( + cls, credentials: dict[str, str], user_id: str + ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: """ Get styles """ @@ -415,7 +419,7 @@ class AIPPTGenerateToolAdapter: return colors, styles - def get_styles(self, user_id: str) -> tuple[list[dict], list[dict]]: + def get_styles(self, user_id: str) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: """ Get styles @@ -507,7 +511,9 @@ class AIPPTGenerateTool(BuiltinTool): def __init__(self, **kwargs: Any): super().__init__(**kwargs) - def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]: + def _invoke( + self, user_id: str, tool_parameters: dict[str, Any] + ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: return AIPPTGenerateToolAdapter(self)._invoke(user_id, tool_parameters) def get_runtime_parameters(self) -> list[ToolParameter]: diff --git a/api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py b/api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py index 2d65ba2d6f..8bd16050ec 100644 --- a/api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py +++ b/api/core/tools/provider/builtin/arxiv/tools/arxiv_search.py @@ -1,7 +1,7 @@ import logging from typing import Any, Optional -import arxiv +import arxiv # type: ignore from pydantic import BaseModel, Field from core.tools.entities.tool_entities import ToolInvokeMessage diff --git a/api/core/tools/provider/builtin/audio/tools/tts.py b/api/core/tools/provider/builtin/audio/tools/tts.py index f83a64d041..8a33ac405b 100644 --- a/api/core/tools/provider/builtin/audio/tools/tts.py +++ b/api/core/tools/provider/builtin/audio/tools/tts.py @@ -11,19 +11,21 @@ from services.model_provider_service import ModelProviderService class TTSTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> list[ToolInvokeMessage]: - provider, model = tool_parameters.get("model").split("#") - voice = tool_parameters.get(f"voice#{provider}#{model}") + provider, model = tool_parameters.get("model", "").split("#") + voice = tool_parameters.get(f"voice#{provider}#{model}", "") model_manager = ModelManager() + if not self.runtime: + raise ValueError("Runtime is required") model_instance = model_manager.get_model_instance( - tenant_id=self.runtime.tenant_id, + tenant_id=self.runtime.tenant_id or "", provider=provider, model_type=ModelType.TTS, model=model, ) tts = model_instance.invoke_tts( - content_text=tool_parameters.get("text"), + content_text=tool_parameters.get("text", ""), user=user_id, - tenant_id=self.runtime.tenant_id, + tenant_id=self.runtime.tenant_id or "", voice=voice, ) buffer = io.BytesIO() @@ -41,8 +43,11 @@ class TTSTool(BuiltinTool): ] def get_available_models(self) -> list[tuple[str, str, list[Any]]]: + if not self.runtime: + raise ValueError("Runtime is required") model_provider_service = ModelProviderService() - models = model_provider_service.get_models_by_model_type(tenant_id=self.runtime.tenant_id, model_type="tts") + tid: str = self.runtime.tenant_id or "" + models = model_provider_service.get_models_by_model_type(tenant_id=tid, model_type="tts") items = [] for provider_model in models: provider = provider_model.provider @@ -62,6 +67,8 @@ class TTSTool(BuiltinTool): ToolParameter( name=f"voice#{provider}#{model}", label=I18nObject(en_US=f"Voice of {model}({provider})"), + human_description=I18nObject(en_US=f"Select a voice for {model} model"), + placeholder=I18nObject(en_US="Select a voice"), type=ToolParameter.ToolParameterType.SELECT, form=ToolParameter.ToolParameterForm.FORM, options=[ @@ -83,6 +90,7 @@ class TTSTool(BuiltinTool): type=ToolParameter.ToolParameterType.SELECT, form=ToolParameter.ToolParameterForm.FORM, required=True, + placeholder=I18nObject(en_US="Select a model", zh_Hans="选择模型"), options=options, ), ) diff --git a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py b/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py index a04f5c0fe9..b224ff5258 100644 --- a/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py +++ b/api/core/tools/provider/builtin/aws/tools/apply_guardrail.py @@ -2,8 +2,8 @@ import json import logging from typing import Any, Union -import boto3 -from botocore.exceptions import BotoCoreError +import boto3 # type: ignore +from botocore.exceptions import BotoCoreError # type: ignore from pydantic import BaseModel, Field from core.tools.entities.tool_entities import ToolInvokeMessage diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py b/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py index 9896081221..b6d16d2759 100644 --- a/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py +++ b/api/core/tools/provider/builtin/aws/tools/lambda_translate_utils.py @@ -1,7 +1,7 @@ import json from typing import Any, Union -import boto3 +import boto3 # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py b/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py index f43f3b6fe0..01bc596346 100644 --- a/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py +++ b/api/core/tools/provider/builtin/aws/tools/lambda_yaml_to_json.py @@ -2,7 +2,7 @@ import json import logging from typing import Any, Union -import boto3 +import boto3 # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py b/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py index bffcd058b5..715b1ddedd 100644 --- a/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py +++ b/api/core/tools/provider/builtin/aws/tools/sagemaker_text_rerank.py @@ -2,7 +2,7 @@ import json import operator from typing import Any, Union -import boto3 +import boto3 # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool @@ -10,8 +10,8 @@ from core.tools.tool.builtin_tool import BuiltinTool class SageMakerReRankTool(BuiltinTool): sagemaker_client: Any = None - sagemaker_endpoint: str = None - topk: int = None + sagemaker_endpoint: str | None = None + topk: int | None = None def _sagemaker_rerank(self, query_input: str, docs: list[str], rerank_endpoint: str): inputs = [query_input] * len(docs) diff --git a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py b/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py index 1fafe09b4d..55cff89798 100644 --- a/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py +++ b/api/core/tools/provider/builtin/aws/tools/sagemaker_tts.py @@ -2,7 +2,7 @@ import json from enum import Enum from typing import Any, Optional, Union -import boto3 +import boto3 # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool @@ -17,7 +17,7 @@ class TTSModelType(Enum): class SageMakerTTSTool(BuiltinTool): sagemaker_client: Any = None - sagemaker_endpoint: str = None + sagemaker_endpoint: str | None = None s3_client: Any = None comprehend_client: Any = None diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo.py b/api/core/tools/provider/builtin/cogview/tools/cogvideo.py index 7f69e833cb..a60062ca66 100644 --- a/api/core/tools/provider/builtin/cogview/tools/cogvideo.py +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo.py @@ -1,6 +1,6 @@ from typing import Any, Union -from zhipuai import ZhipuAI +from zhipuai import ZhipuAI # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py index a521f1c28a..3e24b74d25 100644 --- a/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py +++ b/api/core/tools/provider/builtin/cogview/tools/cogvideo_job.py @@ -1,7 +1,7 @@ from typing import Any, Union import httpx -from zhipuai import ZhipuAI +from zhipuai import ZhipuAI # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/cogview/tools/cogview3.py b/api/core/tools/provider/builtin/cogview/tools/cogview3.py index 12b4173fa4..9aa781709a 100644 --- a/api/core/tools/provider/builtin/cogview/tools/cogview3.py +++ b/api/core/tools/provider/builtin/cogview/tools/cogview3.py @@ -1,7 +1,7 @@ import random from typing import Any, Union -from zhipuai import ZhipuAI +from zhipuai import ZhipuAI # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/feishu_base/tools/search_records.py b/api/core/tools/provider/builtin/feishu_base/tools/search_records.py index c959496735..d58b42b820 100644 --- a/api/core/tools/provider/builtin/feishu_base/tools/search_records.py +++ b/api/core/tools/provider/builtin/feishu_base/tools/search_records.py @@ -7,18 +7,22 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class SearchRecordsTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - app_token = tool_parameters.get("app_token") - table_id = tool_parameters.get("table_id") - table_name = tool_parameters.get("table_name") - view_id = tool_parameters.get("view_id") - field_names = tool_parameters.get("field_names") - sort = tool_parameters.get("sort") - filters = tool_parameters.get("filter") - page_token = tool_parameters.get("page_token") + app_token = tool_parameters.get("app_token", "") + table_id = tool_parameters.get("table_id", "") + table_name = tool_parameters.get("table_name", "") + view_id = tool_parameters.get("view_id", "") + field_names = tool_parameters.get("field_names", "") + sort = tool_parameters.get("sort", "") + filters = tool_parameters.get("filter", "") + page_token = tool_parameters.get("page_token", "") automatic_fields = tool_parameters.get("automatic_fields", False) user_id_type = tool_parameters.get("user_id_type", "open_id") page_size = tool_parameters.get("page_size", 20) diff --git a/api/core/tools/provider/builtin/feishu_base/tools/update_records.py b/api/core/tools/provider/builtin/feishu_base/tools/update_records.py index a7b0363875..31cf8e18d8 100644 --- a/api/core/tools/provider/builtin/feishu_base/tools/update_records.py +++ b/api/core/tools/provider/builtin/feishu_base/tools/update_records.py @@ -7,14 +7,18 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class UpdateRecordsTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - app_token = tool_parameters.get("app_token") - table_id = tool_parameters.get("table_id") - table_name = tool_parameters.get("table_name") - records = tool_parameters.get("records") + app_token = tool_parameters.get("app_token", "") + table_id = tool_parameters.get("table_id", "") + table_name = tool_parameters.get("table_name", "") + records = tool_parameters.get("records", "") user_id_type = tool_parameters.get("user_id_type", "open_id") res = client.update_records(app_token, table_id, table_name, records, user_id_type) diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py b/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py index 8f83aea5ab..80287feca1 100644 --- a/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py +++ b/api/core/tools/provider/builtin/feishu_calendar/tools/add_event_attendees.py @@ -7,12 +7,16 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class AddEventAttendeesTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - event_id = tool_parameters.get("event_id") - attendee_phone_or_email = tool_parameters.get("attendee_phone_or_email") + event_id = tool_parameters.get("event_id", "") + attendee_phone_or_email = tool_parameters.get("attendee_phone_or_email", "") need_notification = tool_parameters.get("need_notification", True) res = client.add_event_attendees(event_id, attendee_phone_or_email, need_notification) diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py b/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py index 144889692f..02e9b44521 100644 --- a/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py +++ b/api/core/tools/provider/builtin/feishu_calendar/tools/delete_event.py @@ -7,11 +7,15 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class DeleteEventTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - event_id = tool_parameters.get("event_id") + event_id = tool_parameters.get("event_id", "") need_notification = tool_parameters.get("need_notification", True) res = client.delete_event(event_id, need_notification) diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py b/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py index a2cd5a8b17..4dafe4b3ba 100644 --- a/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py +++ b/api/core/tools/provider/builtin/feishu_calendar/tools/get_primary_calendar.py @@ -7,8 +7,12 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class GetPrimaryCalendarTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) user_id_type = tool_parameters.get("user_id_type", "open_id") diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py b/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py index 8815b4c9c8..2e8ca968b3 100644 --- a/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py +++ b/api/core/tools/provider/builtin/feishu_calendar/tools/list_events.py @@ -7,14 +7,18 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class ListEventsTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - start_time = tool_parameters.get("start_time") - end_time = tool_parameters.get("end_time") - page_token = tool_parameters.get("page_token") - page_size = tool_parameters.get("page_size") + start_time = tool_parameters.get("start_time", "") + end_time = tool_parameters.get("end_time", "") + page_token = tool_parameters.get("page_token", "") + page_size = tool_parameters.get("page_size", 50) res = client.list_events(start_time, end_time, page_token, page_size) diff --git a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py b/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py index 85bcb1d3f6..b20eb6c318 100644 --- a/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py +++ b/api/core/tools/provider/builtin/feishu_calendar/tools/update_event.py @@ -7,16 +7,20 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class UpdateEventTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - event_id = tool_parameters.get("event_id") - summary = tool_parameters.get("summary") - description = tool_parameters.get("description") + event_id = tool_parameters.get("event_id", "") + summary = tool_parameters.get("summary", "") + description = tool_parameters.get("description", "") need_notification = tool_parameters.get("need_notification", True) - start_time = tool_parameters.get("start_time") - end_time = tool_parameters.get("end_time") + start_time = tool_parameters.get("start_time", "") + end_time = tool_parameters.get("end_time", "") auto_record = tool_parameters.get("auto_record", False) res = client.update_event(event_id, summary, description, need_notification, start_time, end_time, auto_record) diff --git a/api/core/tools/provider/builtin/feishu_document/tools/create_document.py b/api/core/tools/provider/builtin/feishu_document/tools/create_document.py index 090a0828e8..1533f59417 100644 --- a/api/core/tools/provider/builtin/feishu_document/tools/create_document.py +++ b/api/core/tools/provider/builtin/feishu_document/tools/create_document.py @@ -7,13 +7,17 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class CreateDocumentTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - title = tool_parameters.get("title") - content = tool_parameters.get("content") - folder_token = tool_parameters.get("folder_token") + title = tool_parameters.get("title", "") + content = tool_parameters.get("content", "") + folder_token = tool_parameters.get("folder_token", "") res = client.create_document(title, content, folder_token) return self.create_json_message(res) diff --git a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py b/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py index dd57c6870d..8ea68a2ed8 100644 --- a/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py +++ b/api/core/tools/provider/builtin/feishu_document/tools/list_document_blocks.py @@ -7,11 +7,15 @@ from core.tools.utils.feishu_api_utils import FeishuRequest class ListDocumentBlockTool(BuiltinTool): def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage: + if not self.runtime or not self.runtime.credentials: + raise ValueError("Runtime is not set") app_id = self.runtime.credentials.get("app_id") app_secret = self.runtime.credentials.get("app_secret") + if not app_id or not app_secret: + raise ValueError("app_id and app_secret are required") client = FeishuRequest(app_id, app_secret) - document_id = tool_parameters.get("document_id") + document_id = tool_parameters.get("document_id", "") page_token = tool_parameters.get("page_token", "") user_id_type = tool_parameters.get("user_id_type", "open_id") page_size = tool_parameters.get("page_size", 500) diff --git a/api/core/tools/provider/builtin/json_process/tools/delete.py b/api/core/tools/provider/builtin/json_process/tools/delete.py index fcab3d71a9..06f6cacd5d 100644 --- a/api/core/tools/provider/builtin/json_process/tools/delete.py +++ b/api/core/tools/provider/builtin/json_process/tools/delete.py @@ -1,7 +1,7 @@ import json from typing import Any, Union -from jsonpath_ng import parse +from jsonpath_ng import parse # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/json_process/tools/insert.py b/api/core/tools/provider/builtin/json_process/tools/insert.py index 793c74e5f9..e825329a6d 100644 --- a/api/core/tools/provider/builtin/json_process/tools/insert.py +++ b/api/core/tools/provider/builtin/json_process/tools/insert.py @@ -1,7 +1,7 @@ import json from typing import Any, Union -from jsonpath_ng import parse +from jsonpath_ng import parse # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/json_process/tools/parse.py b/api/core/tools/provider/builtin/json_process/tools/parse.py index f91432ee77..193017ba9a 100644 --- a/api/core/tools/provider/builtin/json_process/tools/parse.py +++ b/api/core/tools/provider/builtin/json_process/tools/parse.py @@ -1,7 +1,7 @@ import json from typing import Any, Union -from jsonpath_ng import parse +from jsonpath_ng import parse # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/json_process/tools/replace.py b/api/core/tools/provider/builtin/json_process/tools/replace.py index 383825c2d0..feca0d8a7c 100644 --- a/api/core/tools/provider/builtin/json_process/tools/replace.py +++ b/api/core/tools/provider/builtin/json_process/tools/replace.py @@ -1,7 +1,7 @@ import json from typing import Any, Union -from jsonpath_ng import parse +from jsonpath_ng import parse # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/maths/tools/eval_expression.py b/api/core/tools/provider/builtin/maths/tools/eval_expression.py index 0c5b5e41cb..d3a497d1cd 100644 --- a/api/core/tools/provider/builtin/maths/tools/eval_expression.py +++ b/api/core/tools/provider/builtin/maths/tools/eval_expression.py @@ -1,7 +1,7 @@ import logging from typing import Any, Union -import numexpr as ne +import numexpr as ne # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py b/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py index db4adfd4ad..6473c509e1 100644 --- a/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py +++ b/api/core/tools/provider/builtin/novitaai/_novita_tool_base.py @@ -1,4 +1,4 @@ -from novita_client import ( +from novita_client import ( # type: ignore Txt2ImgV3Embedding, Txt2ImgV3HiresFix, Txt2ImgV3LoRA, diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py b/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py index 0b4f2edff3..097b234bd5 100644 --- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py +++ b/api/core/tools/provider/builtin/novitaai/tools/novitaai_createtile.py @@ -2,7 +2,7 @@ from base64 import b64decode from copy import deepcopy from typing import Any, Union -from novita_client import ( +from novita_client import ( # type: ignore NovitaClient, ) diff --git a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py b/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py index 9c61eab9f9..297a27abba 100644 --- a/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py +++ b/api/core/tools/provider/builtin/novitaai/tools/novitaai_txt2img.py @@ -2,7 +2,7 @@ from base64 import b64decode from copy import deepcopy from typing import Any, Union -from novita_client import ( +from novita_client import ( # type: ignore NovitaClient, ) diff --git a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py b/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py index 165e93956e..704e0015d9 100644 --- a/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py +++ b/api/core/tools/provider/builtin/podcast_generator/tools/podcast_audio_generator.py @@ -13,7 +13,7 @@ from core.tools.tool.builtin_tool import BuiltinTool with warnings.catch_warnings(): warnings.simplefilter("ignore") - from pydub import AudioSegment + from pydub import AudioSegment # type: ignore class PodcastAudioGeneratorTool(BuiltinTool): diff --git a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py b/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py index d8ca20bde6..4a47c4211f 100644 --- a/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py +++ b/api/core/tools/provider/builtin/qrcode/tools/qrcode_generator.py @@ -2,10 +2,10 @@ import io import logging from typing import Any, Union -from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q -from qrcode.image.base import BaseImage -from qrcode.image.pure import PyPNGImage -from qrcode.main import QRCode +from qrcode.constants import ERROR_CORRECT_H, ERROR_CORRECT_L, ERROR_CORRECT_M, ERROR_CORRECT_Q # type: ignore +from qrcode.image.base import BaseImage # type: ignore +from qrcode.image.pure import PyPNGImage # type: ignore +from qrcode.main import QRCode # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/transcript/tools/transcript.py b/api/core/tools/provider/builtin/transcript/tools/transcript.py index 27f700efbd..ac7565d9ee 100644 --- a/api/core/tools/provider/builtin/transcript/tools/transcript.py +++ b/api/core/tools/provider/builtin/transcript/tools/transcript.py @@ -1,7 +1,7 @@ from typing import Any, Union from urllib.parse import parse_qs, urlparse -from youtube_transcript_api import YouTubeTranscriptApi +from youtube_transcript_api import YouTubeTranscriptApi # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/twilio/tools/send_message.py b/api/core/tools/provider/builtin/twilio/tools/send_message.py index 5ee839baa5..98a108f4ec 100644 --- a/api/core/tools/provider/builtin/twilio/tools/send_message.py +++ b/api/core/tools/provider/builtin/twilio/tools/send_message.py @@ -37,7 +37,7 @@ class TwilioAPIWrapper(BaseModel): def set_validator(cls, values: dict) -> dict: """Validate that api key and python package exists in environment.""" try: - from twilio.rest import Client + from twilio.rest import Client # type: ignore except ImportError: raise ImportError("Could not import twilio python package. Please install it with `pip install twilio`.") account_sid = values.get("account_sid") diff --git a/api/core/tools/provider/builtin/twilio/twilio.py b/api/core/tools/provider/builtin/twilio/twilio.py index b1d100aad9..649e03d185 100644 --- a/api/core/tools/provider/builtin/twilio/twilio.py +++ b/api/core/tools/provider/builtin/twilio/twilio.py @@ -1,7 +1,7 @@ from typing import Any -from twilio.base.exceptions import TwilioRestException -from twilio.rest import Client +from twilio.base.exceptions import TwilioRestException # type: ignore +from twilio.rest import Client # type: ignore from core.tools.errors import ToolProviderCredentialValidationError from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController diff --git a/api/core/tools/provider/builtin/vanna/tools/vanna.py b/api/core/tools/provider/builtin/vanna/tools/vanna.py index 1c7cb39c92..a6afd2dddf 100644 --- a/api/core/tools/provider/builtin/vanna/tools/vanna.py +++ b/api/core/tools/provider/builtin/vanna/tools/vanna.py @@ -1,6 +1,6 @@ from typing import Any, Union -from vanna.remote import VannaDefault +from vanna.remote import VannaDefault # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.errors import ToolProviderCredentialValidationError @@ -14,6 +14,9 @@ class VannaTool(BuiltinTool): """ invoke tools """ + # Ensure runtime and credentials + if not self.runtime or not self.runtime.credentials: + raise ToolProviderCredentialValidationError("Tool runtime or credentials are missing") api_key = self.runtime.credentials.get("api_key", None) if not api_key: raise ToolProviderCredentialValidationError("Please input api key") diff --git a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py b/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py index cb88e9519a..edb96e722f 100644 --- a/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py +++ b/api/core/tools/provider/builtin/wikipedia/tools/wikipedia_search.py @@ -1,6 +1,6 @@ from typing import Any, Optional, Union -import wikipedia +import wikipedia # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/yahoo/tools/analytics.py b/api/core/tools/provider/builtin/yahoo/tools/analytics.py index f044fbe540..95a65ba22f 100644 --- a/api/core/tools/provider/builtin/yahoo/tools/analytics.py +++ b/api/core/tools/provider/builtin/yahoo/tools/analytics.py @@ -3,7 +3,7 @@ from typing import Any, Union import pandas as pd from requests.exceptions import HTTPError, ReadTimeout -from yfinance import download +from yfinance import download # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/yahoo/tools/news.py b/api/core/tools/provider/builtin/yahoo/tools/news.py index ff820430f9..c9ae0c4ca7 100644 --- a/api/core/tools/provider/builtin/yahoo/tools/news.py +++ b/api/core/tools/provider/builtin/yahoo/tools/news.py @@ -1,6 +1,6 @@ from typing import Any, Union -import yfinance +import yfinance # type: ignore from requests.exceptions import HTTPError, ReadTimeout from core.tools.entities.tool_entities import ToolInvokeMessage diff --git a/api/core/tools/provider/builtin/yahoo/tools/ticker.py b/api/core/tools/provider/builtin/yahoo/tools/ticker.py index dfc7e46047..74d0d25add 100644 --- a/api/core/tools/provider/builtin/yahoo/tools/ticker.py +++ b/api/core/tools/provider/builtin/yahoo/tools/ticker.py @@ -1,7 +1,7 @@ from typing import Any, Union from requests.exceptions import HTTPError, ReadTimeout -from yfinance import Ticker +from yfinance import Ticker # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin/youtube/tools/videos.py b/api/core/tools/provider/builtin/youtube/tools/videos.py index 95dec2eac9..a24fe89679 100644 --- a/api/core/tools/provider/builtin/youtube/tools/videos.py +++ b/api/core/tools/provider/builtin/youtube/tools/videos.py @@ -1,7 +1,7 @@ from datetime import datetime from typing import Any, Union -from googleapiclient.discovery import build +from googleapiclient.discovery import build # type: ignore from core.tools.entities.tool_entities import ToolInvokeMessage from core.tools.tool.builtin_tool import BuiltinTool diff --git a/api/core/tools/provider/builtin_tool_provider.py b/api/core/tools/provider/builtin_tool_provider.py index 955a0add3b..61de75ac5e 100644 --- a/api/core/tools/provider/builtin_tool_provider.py +++ b/api/core/tools/provider/builtin_tool_provider.py @@ -1,6 +1,6 @@ from abc import abstractmethod from os import listdir, path -from typing import Any +from typing import Any, Optional from core.helper.module_import_helper import load_single_subclass_from_source from core.tools.entities.tool_entities import ToolParameter, ToolProviderCredentials, ToolProviderType @@ -50,6 +50,8 @@ class BuiltinToolProviderController(ToolProviderController): """ if self.tools: return self.tools + if not self.identity: + return [] provider = self.identity.name tool_path = path.join(path.dirname(path.realpath(__file__)), "builtin", provider, "tools") @@ -86,7 +88,7 @@ class BuiltinToolProviderController(ToolProviderController): return self.credentials_schema.copy() - def get_tools(self) -> list[Tool]: + def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]: """ returns a list of tools that the provider can provide @@ -94,11 +96,14 @@ class BuiltinToolProviderController(ToolProviderController): """ return self._get_builtin_tools() - def get_tool(self, tool_name: str) -> Tool: + def get_tool(self, tool_name: str) -> Optional[Tool]: """ returns the tool that the provider can provide """ - return next(filter(lambda x: x.identity.name == tool_name, self.get_tools()), None) + tools = self.get_tools() + if tools is None: + raise ValueError("tools not found") + return next((t for t in tools if t.identity and t.identity.name == tool_name), None) def get_parameters(self, tool_name: str) -> list[ToolParameter]: """ @@ -107,10 +112,13 @@ class BuiltinToolProviderController(ToolProviderController): :param tool_name: the name of the tool, defined in `get_tools` :return: list of parameters """ - tool = next(filter(lambda x: x.identity.name == tool_name, self.get_tools()), None) + tools = self.get_tools() + if tools is None: + raise ToolNotFoundError(f"tool {tool_name} not found") + tool = next((t for t in tools if t.identity and t.identity.name == tool_name), None) if tool is None: raise ToolNotFoundError(f"tool {tool_name} not found") - return tool.parameters + return tool.parameters or [] @property def need_credentials(self) -> bool: @@ -144,6 +152,8 @@ class BuiltinToolProviderController(ToolProviderController): """ returns the labels of the provider """ + if self.identity is None: + return [] return self.identity.tags or [] def validate_parameters(self, tool_id: int, tool_name: str, tool_parameters: dict[str, Any]) -> None: @@ -159,56 +169,56 @@ class BuiltinToolProviderController(ToolProviderController): for parameter in tool_parameters_schema: tool_parameters_need_to_validate[parameter.name] = parameter - for parameter in tool_parameters: - if parameter not in tool_parameters_need_to_validate: - raise ToolParameterValidationError(f"parameter {parameter} not found in tool {tool_name}") + for parameter_name in tool_parameters: + if parameter_name not in tool_parameters_need_to_validate: + raise ToolParameterValidationError(f"parameter {parameter_name} not found in tool {tool_name}") # check type - parameter_schema = tool_parameters_need_to_validate[parameter] + parameter_schema = tool_parameters_need_to_validate[parameter_name] if parameter_schema.type == ToolParameter.ToolParameterType.STRING: - if not isinstance(tool_parameters[parameter], str): - raise ToolParameterValidationError(f"parameter {parameter} should be string") + if not isinstance(tool_parameters[parameter_name], str): + raise ToolParameterValidationError(f"parameter {parameter_name} should be string") elif parameter_schema.type == ToolParameter.ToolParameterType.NUMBER: - if not isinstance(tool_parameters[parameter], int | float): - raise ToolParameterValidationError(f"parameter {parameter} should be number") + if not isinstance(tool_parameters[parameter_name], int | float): + raise ToolParameterValidationError(f"parameter {parameter_name} should be number") - if parameter_schema.min is not None and tool_parameters[parameter] < parameter_schema.min: + if parameter_schema.min is not None and tool_parameters[parameter_name] < parameter_schema.min: raise ToolParameterValidationError( - f"parameter {parameter} should be greater than {parameter_schema.min}" + f"parameter {parameter_name} should be greater than {parameter_schema.min}" ) - if parameter_schema.max is not None and tool_parameters[parameter] > parameter_schema.max: + if parameter_schema.max is not None and tool_parameters[parameter_name] > parameter_schema.max: raise ToolParameterValidationError( - f"parameter {parameter} should be less than {parameter_schema.max}" + f"parameter {parameter_name} should be less than {parameter_schema.max}" ) elif parameter_schema.type == ToolParameter.ToolParameterType.BOOLEAN: - if not isinstance(tool_parameters[parameter], bool): - raise ToolParameterValidationError(f"parameter {parameter} should be boolean") + if not isinstance(tool_parameters[parameter_name], bool): + raise ToolParameterValidationError(f"parameter {parameter_name} should be boolean") elif parameter_schema.type == ToolParameter.ToolParameterType.SELECT: - if not isinstance(tool_parameters[parameter], str): - raise ToolParameterValidationError(f"parameter {parameter} should be string") + if not isinstance(tool_parameters[parameter_name], str): + raise ToolParameterValidationError(f"parameter {parameter_name} should be string") options = parameter_schema.options if not isinstance(options, list): - raise ToolParameterValidationError(f"parameter {parameter} options should be list") + raise ToolParameterValidationError(f"parameter {parameter_name} options should be list") - if tool_parameters[parameter] not in [x.value for x in options]: - raise ToolParameterValidationError(f"parameter {parameter} should be one of {options}") + if tool_parameters[parameter_name] not in [x.value for x in options]: + raise ToolParameterValidationError(f"parameter {parameter_name} should be one of {options}") - tool_parameters_need_to_validate.pop(parameter) + tool_parameters_need_to_validate.pop(parameter_name) - for parameter in tool_parameters_need_to_validate: - parameter_schema = tool_parameters_need_to_validate[parameter] + for parameter_name in tool_parameters_need_to_validate: + parameter_schema = tool_parameters_need_to_validate[parameter_name] if parameter_schema.required: - raise ToolParameterValidationError(f"parameter {parameter} is required") + raise ToolParameterValidationError(f"parameter {parameter_name} is required") # the parameter is not set currently, set the default value if needed if parameter_schema.default is not None: default_value = parameter_schema.type.cast_value(parameter_schema.default) - tool_parameters[parameter] = default_value + tool_parameters[parameter_name] = default_value def validate_credentials(self, credentials: dict[str, Any]) -> None: """ diff --git a/api/core/tools/provider/tool_provider.py b/api/core/tools/provider/tool_provider.py index bc05a11562..e35207e4f0 100644 --- a/api/core/tools/provider/tool_provider.py +++ b/api/core/tools/provider/tool_provider.py @@ -24,10 +24,12 @@ class ToolProviderController(BaseModel, ABC): :return: the credentials schema """ + if self.credentials_schema is None: + return {} return self.credentials_schema.copy() @abstractmethod - def get_tools(self) -> list[Tool]: + def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]: """ returns a list of tools that the provider can provide @@ -36,7 +38,7 @@ class ToolProviderController(BaseModel, ABC): pass @abstractmethod - def get_tool(self, tool_name: str) -> Tool: + def get_tool(self, tool_name: str) -> Optional[Tool]: """ returns a tool that the provider can provide @@ -51,10 +53,13 @@ class ToolProviderController(BaseModel, ABC): :param tool_name: the name of the tool, defined in `get_tools` :return: list of parameters """ - tool = next(filter(lambda x: x.identity.name == tool_name, self.get_tools()), None) + tools = self.get_tools() + if tools is None: + raise ToolNotFoundError(f"tool {tool_name} not found") + tool = next((t for t in tools if t.identity and t.identity.name == tool_name), None) if tool is None: raise ToolNotFoundError(f"tool {tool_name} not found") - return tool.parameters + return tool.parameters or [] @property def provider_type(self) -> ToolProviderType: @@ -78,55 +83,55 @@ class ToolProviderController(BaseModel, ABC): for parameter in tool_parameters_schema: tool_parameters_need_to_validate[parameter.name] = parameter - for parameter in tool_parameters: - if parameter not in tool_parameters_need_to_validate: - raise ToolParameterValidationError(f"parameter {parameter} not found in tool {tool_name}") + for tool_parameter in tool_parameters: + if tool_parameter not in tool_parameters_need_to_validate: + raise ToolParameterValidationError(f"parameter {tool_parameter} not found in tool {tool_name}") # check type - parameter_schema = tool_parameters_need_to_validate[parameter] + parameter_schema = tool_parameters_need_to_validate[tool_parameter] if parameter_schema.type == ToolParameter.ToolParameterType.STRING: - if not isinstance(tool_parameters[parameter], str): - raise ToolParameterValidationError(f"parameter {parameter} should be string") + if not isinstance(tool_parameters[tool_parameter], str): + raise ToolParameterValidationError(f"parameter {tool_parameter} should be string") elif parameter_schema.type == ToolParameter.ToolParameterType.NUMBER: - if not isinstance(tool_parameters[parameter], int | float): - raise ToolParameterValidationError(f"parameter {parameter} should be number") + if not isinstance(tool_parameters[tool_parameter], int | float): + raise ToolParameterValidationError(f"parameter {tool_parameter} should be number") - if parameter_schema.min is not None and tool_parameters[parameter] < parameter_schema.min: + if parameter_schema.min is not None and tool_parameters[tool_parameter] < parameter_schema.min: raise ToolParameterValidationError( - f"parameter {parameter} should be greater than {parameter_schema.min}" + f"parameter {tool_parameter} should be greater than {parameter_schema.min}" ) - if parameter_schema.max is not None and tool_parameters[parameter] > parameter_schema.max: + if parameter_schema.max is not None and tool_parameters[tool_parameter] > parameter_schema.max: raise ToolParameterValidationError( - f"parameter {parameter} should be less than {parameter_schema.max}" + f"parameter {tool_parameter} should be less than {parameter_schema.max}" ) elif parameter_schema.type == ToolParameter.ToolParameterType.BOOLEAN: - if not isinstance(tool_parameters[parameter], bool): - raise ToolParameterValidationError(f"parameter {parameter} should be boolean") + if not isinstance(tool_parameters[tool_parameter], bool): + raise ToolParameterValidationError(f"parameter {tool_parameter} should be boolean") elif parameter_schema.type == ToolParameter.ToolParameterType.SELECT: - if not isinstance(tool_parameters[parameter], str): - raise ToolParameterValidationError(f"parameter {parameter} should be string") + if not isinstance(tool_parameters[tool_parameter], str): + raise ToolParameterValidationError(f"parameter {tool_parameter} should be string") options = parameter_schema.options if not isinstance(options, list): - raise ToolParameterValidationError(f"parameter {parameter} options should be list") + raise ToolParameterValidationError(f"parameter {tool_parameter} options should be list") - if tool_parameters[parameter] not in [x.value for x in options]: - raise ToolParameterValidationError(f"parameter {parameter} should be one of {options}") + if tool_parameters[tool_parameter] not in [x.value for x in options]: + raise ToolParameterValidationError(f"parameter {tool_parameter} should be one of {options}") - tool_parameters_need_to_validate.pop(parameter) + tool_parameters_need_to_validate.pop(tool_parameter) - for parameter in tool_parameters_need_to_validate: - parameter_schema = tool_parameters_need_to_validate[parameter] + for tool_parameter_validate in tool_parameters_need_to_validate: + parameter_schema = tool_parameters_need_to_validate[tool_parameter_validate] if parameter_schema.required: - raise ToolParameterValidationError(f"parameter {parameter} is required") + raise ToolParameterValidationError(f"parameter {tool_parameter_validate} is required") # the parameter is not set currently, set the default value if needed if parameter_schema.default is not None: - tool_parameters[parameter] = parameter_schema.type.cast_value(parameter_schema.default) + tool_parameters[tool_parameter_validate] = parameter_schema.type.cast_value(parameter_schema.default) def validate_credentials_format(self, credentials: dict[str, Any]) -> None: """ @@ -144,6 +149,8 @@ class ToolProviderController(BaseModel, ABC): for credential_name in credentials: if credential_name not in credentials_need_to_validate: + if self.identity is None: + raise ValueError("identity is not set") raise ToolProviderCredentialValidationError( f"credential {credential_name} not found in provider {self.identity.name}" ) diff --git a/api/core/tools/provider/workflow_tool_provider.py b/api/core/tools/provider/workflow_tool_provider.py index 5656dd09ab..17fe2e20cf 100644 --- a/api/core/tools/provider/workflow_tool_provider.py +++ b/api/core/tools/provider/workflow_tool_provider.py @@ -11,6 +11,7 @@ from core.tools.entities.tool_entities import ( ToolProviderType, ) from core.tools.provider.tool_provider import ToolProviderController +from core.tools.tool.tool import Tool from core.tools.tool.workflow_tool import WorkflowTool from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils from extensions.ext_database import db @@ -116,6 +117,7 @@ class WorkflowToolProviderController(ToolProviderController): llm_description=parameter.description, required=variable.required, options=options, + placeholder=I18nObject(en_US="", zh_Hans=""), ) ) elif features.file_upload: @@ -128,6 +130,7 @@ class WorkflowToolProviderController(ToolProviderController): llm_description=parameter.description, required=False, form=parameter.form, + placeholder=I18nObject(en_US="", zh_Hans=""), ) ) else: @@ -157,7 +160,7 @@ class WorkflowToolProviderController(ToolProviderController): label=db_provider.label, ) - def get_tools(self, user_id: str, tenant_id: str) -> list[WorkflowTool]: + def get_tools(self, user_id: str = "", tenant_id: str = "") -> Optional[list[Tool]]: """ fetch tools from database @@ -168,7 +171,7 @@ class WorkflowToolProviderController(ToolProviderController): if self.tools is not None: return self.tools - db_providers: WorkflowToolProvider = ( + db_providers: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter( WorkflowToolProvider.tenant_id == tenant_id, @@ -179,12 +182,14 @@ class WorkflowToolProviderController(ToolProviderController): if not db_providers: return [] + if not db_providers.app: + raise ValueError("app not found") self.tools = [self._get_db_provider_tool(db_providers, db_providers.app)] return self.tools - def get_tool(self, tool_name: str) -> Optional[WorkflowTool]: + def get_tool(self, tool_name: str) -> Optional[Tool]: """ get tool by name @@ -195,6 +200,8 @@ class WorkflowToolProviderController(ToolProviderController): return None for tool in self.tools: + if tool.identity is None: + continue if tool.identity.name == tool_name: return tool diff --git a/api/core/tools/tool/api_tool.py b/api/core/tools/tool/api_tool.py index 48aac75dbb..9a00450290 100644 --- a/api/core/tools/tool/api_tool.py +++ b/api/core/tools/tool/api_tool.py @@ -32,11 +32,13 @@ class ApiTool(Tool): :param meta: the meta data of a tool call processing, tenant_id is required :return: the new tool """ + if self.api_bundle is None: + raise ValueError("api_bundle is required") return self.__class__( identity=self.identity.model_copy() if self.identity else None, parameters=self.parameters.copy() if self.parameters else None, description=self.description.model_copy() if self.description else None, - api_bundle=self.api_bundle.model_copy() if self.api_bundle else None, + api_bundle=self.api_bundle.model_copy(), runtime=Tool.Runtime(**runtime), ) @@ -61,6 +63,8 @@ class ApiTool(Tool): def assembling_request(self, parameters: dict[str, Any]) -> dict[str, Any]: headers = {} + if self.runtime is None: + raise ValueError("runtime is required") credentials = self.runtime.credentials or {} if "auth_type" not in credentials: @@ -88,7 +92,7 @@ class ApiTool(Tool): headers[api_key_header] = credentials["api_key_value"] - needed_parameters = [parameter for parameter in self.api_bundle.parameters if parameter.required] + needed_parameters = [parameter for parameter in (self.api_bundle.parameters or []) if parameter.required] for parameter in needed_parameters: if parameter.required and parameter.name not in parameters: raise ToolParameterValidationError(f"Missing required parameter {parameter.name}") @@ -137,7 +141,8 @@ class ApiTool(Tool): params = {} path_params = {} - body = {} + # FIXME: body should be a dict[str, Any] but it changed a lot in this function + body: Any = {} cookies = {} files = [] @@ -198,7 +203,7 @@ class ApiTool(Tool): body = body if method in {"get", "head", "post", "put", "delete", "patch"}: - response = getattr(ssrf_proxy, method)( + response: httpx.Response = getattr(ssrf_proxy, method)( url, params=params, headers=headers, @@ -288,6 +293,7 @@ class ApiTool(Tool): """ invoke http request """ + response: httpx.Response | str = "" # assemble request headers = self.assembling_request(tool_parameters) diff --git a/api/core/tools/tool/builtin_tool.py b/api/core/tools/tool/builtin_tool.py index e2a81ed0a3..adda4297f3 100644 --- a/api/core/tools/tool/builtin_tool.py +++ b/api/core/tools/tool/builtin_tool.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, cast from core.model_runtime.entities.llm_entities import LLMResult from core.model_runtime.entities.message_entities import PromptMessage, SystemPromptMessage, UserPromptMessage @@ -32,9 +32,12 @@ class BuiltinTool(Tool): :return: the model result """ # invoke model + if self.runtime is None or self.identity is None: + raise ValueError("runtime and identity are required") + return ModelInvocationUtils.invoke( user_id=user_id, - tenant_id=self.runtime.tenant_id, + tenant_id=self.runtime.tenant_id or "", tool_type="builtin", tool_name=self.identity.name, prompt_messages=prompt_messages, @@ -50,8 +53,11 @@ class BuiltinTool(Tool): :param model_config: the model config :return: the max tokens """ + if self.runtime is None: + raise ValueError("runtime is required") + return ModelInvocationUtils.get_max_llm_context_tokens( - tenant_id=self.runtime.tenant_id, + tenant_id=self.runtime.tenant_id or "", ) def get_prompt_tokens(self, prompt_messages: list[PromptMessage]) -> int: @@ -61,7 +67,12 @@ class BuiltinTool(Tool): :param prompt_messages: the prompt messages :return: the tokens """ - return ModelInvocationUtils.calculate_tokens(tenant_id=self.runtime.tenant_id, prompt_messages=prompt_messages) + if self.runtime is None: + raise ValueError("runtime is required") + + return ModelInvocationUtils.calculate_tokens( + tenant_id=self.runtime.tenant_id or "", prompt_messages=prompt_messages + ) def summary(self, user_id: str, content: str) -> str: max_tokens = self.get_max_tokens() @@ -81,7 +92,7 @@ class BuiltinTool(Tool): stop=[], ) - return summary.message.content + return cast(str, summary.message.content) lines = content.split("\n") new_lines = [] @@ -102,16 +113,16 @@ class BuiltinTool(Tool): # merge lines into messages with max tokens messages: list[str] = [] - for i in new_lines: + for j in new_lines: if len(messages) == 0: - messages.append(i) + messages.append(j) else: - if len(messages[-1]) + len(i) < max_tokens * 0.5: - messages[-1] += i - if get_prompt_tokens(messages[-1] + i) > max_tokens * 0.7: - messages.append(i) + if len(messages[-1]) + len(j) < max_tokens * 0.5: + messages[-1] += j + if get_prompt_tokens(messages[-1] + j) > max_tokens * 0.7: + messages.append(j) else: - messages[-1] += i + messages[-1] += j summaries = [] for i in range(len(messages)): diff --git a/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py b/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py index ab7b40a253..a4afea4b9d 100644 --- a/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py +++ b/api/core/tools/tool/dataset_retriever/dataset_multi_retriever_tool.py @@ -1,4 +1,5 @@ import threading +from typing import Any from flask import Flask, current_app from pydantic import BaseModel, Field @@ -7,13 +8,14 @@ from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCa from core.model_manager import ModelManager from core.model_runtime.entities.model_entities import ModelType from core.rag.datasource.retrieval_service import RetrievalService +from core.rag.models.document import Document as RagDocument from core.rag.rerank.rerank_model import RerankModelRunner from core.rag.retrieval.retrieval_methods import RetrievalMethod from core.tools.tool.dataset_retriever.dataset_retriever_base_tool import DatasetRetrieverBaseTool from extensions.ext_database import db from models.dataset import Dataset, Document, DocumentSegment -default_retrieval_model = { +default_retrieval_model: dict[str, Any] = { "search_method": RetrievalMethod.SEMANTIC_SEARCH.value, "reranking_enable": False, "reranking_model": {"reranking_provider_name": "", "reranking_model_name": ""}, @@ -44,12 +46,12 @@ class DatasetMultiRetrieverTool(DatasetRetrieverBaseTool): def _run(self, query: str) -> str: threads = [] - all_documents = [] + all_documents: list[RagDocument] = [] for dataset_id in self.dataset_ids: retrieval_thread = threading.Thread( target=self._retriever, kwargs={ - "flask_app": current_app._get_current_object(), + "flask_app": current_app._get_current_object(), # type: ignore "dataset_id": dataset_id, "query": query, "all_documents": all_documents, @@ -77,11 +79,11 @@ class DatasetMultiRetrieverTool(DatasetRetrieverBaseTool): document_score_list = {} for item in all_documents: - if item.metadata.get("score"): + if item.metadata and item.metadata.get("score"): document_score_list[item.metadata["doc_id"]] = item.metadata["score"] document_context_list = [] - index_node_ids = [document.metadata["doc_id"] for document in all_documents] + index_node_ids = [document.metadata["doc_id"] for document in all_documents if document.metadata] segments = DocumentSegment.query.filter( DocumentSegment.dataset_id.in_(self.dataset_ids), DocumentSegment.completed_at.isnot(None), @@ -139,6 +141,7 @@ class DatasetMultiRetrieverTool(DatasetRetrieverBaseTool): hit_callback.return_retriever_resource_info(context_list) return str("\n".join(document_context_list)) + return "" def _retriever( self, diff --git a/api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py b/api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py index dad8c77357..a4d2de3b1c 100644 --- a/api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py +++ b/api/core/tools/tool/dataset_retriever/dataset_retriever_base_tool.py @@ -1,7 +1,7 @@ from abc import abstractmethod from typing import Any, Optional -from msal_extensions.persistence import ABC +from msal_extensions.persistence import ABC # type: ignore from pydantic import BaseModel, ConfigDict from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCallbackHandler diff --git a/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py b/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py index 987f94a350..b382016473 100644 --- a/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py +++ b/api/core/tools/tool/dataset_retriever/dataset_retriever_tool.py @@ -1,3 +1,5 @@ +from typing import Any + from pydantic import BaseModel, Field from core.rag.datasource.retrieval_service import RetrievalService @@ -69,25 +71,27 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool): metadata=external_document.get("metadata"), provider="external", ) - document.metadata["score"] = external_document.get("score") - document.metadata["title"] = external_document.get("title") - document.metadata["dataset_id"] = dataset.id - document.metadata["dataset_name"] = dataset.name - results.append(document) + if document.metadata is not None: + document.metadata["score"] = external_document.get("score") + document.metadata["title"] = external_document.get("title") + document.metadata["dataset_id"] = dataset.id + document.metadata["dataset_name"] = dataset.name + results.append(document) # deal with external documents context_list = [] for position, item in enumerate(results, start=1): - source = { - "position": position, - "dataset_id": item.metadata.get("dataset_id"), - "dataset_name": item.metadata.get("dataset_name"), - "document_name": item.metadata.get("title"), - "data_source_type": "external", - "retriever_from": self.retriever_from, - "score": item.metadata.get("score"), - "title": item.metadata.get("title"), - "content": item.page_content, - } + if item.metadata is not None: + source = { + "position": position, + "dataset_id": item.metadata.get("dataset_id"), + "dataset_name": item.metadata.get("dataset_name"), + "document_name": item.metadata.get("title"), + "data_source_type": "external", + "retriever_from": self.retriever_from, + "score": item.metadata.get("score"), + "title": item.metadata.get("title"), + "content": item.page_content, + } context_list.append(source) for hit_callback in self.hit_callbacks: hit_callback.return_retriever_resource_info(context_list) @@ -95,7 +99,7 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool): return str("\n".join([item.page_content for item in results])) else: # get retrieval model , if the model is not setting , using default - retrieval_model = dataset.retrieval_model or default_retrieval_model + retrieval_model: dict[str, Any] = dataset.retrieval_model or default_retrieval_model if dataset.indexing_technique == "economy": # use keyword table query documents = RetrievalService.retrieve( @@ -113,11 +117,11 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool): score_threshold=retrieval_model.get("score_threshold", 0.0) if retrieval_model["score_threshold_enabled"] else 0.0, - reranking_model=retrieval_model.get("reranking_model", None) + reranking_model=retrieval_model.get("reranking_model") if retrieval_model["reranking_enable"] else None, reranking_mode=retrieval_model.get("reranking_mode") or "reranking_model", - weights=retrieval_model.get("weights", None), + weights=retrieval_model.get("weights"), ) else: documents = [] @@ -127,7 +131,7 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool): document_score_list = {} if dataset.indexing_technique != "economy": for item in documents: - if item.metadata.get("score"): + if item.metadata is not None and item.metadata.get("score"): document_score_list[item.metadata["doc_id"]] = item.metadata["score"] document_context_list = [] index_node_ids = [document.metadata["doc_id"] for document in documents] @@ -155,20 +159,21 @@ class DatasetRetrieverTool(DatasetRetrieverBaseTool): context_list = [] resource_number = 1 for segment in sorted_segments: - context = {} - document = Document.query.filter( + document_segment = Document.query.filter( Document.id == segment.document_id, Document.enabled == True, Document.archived == False, ).first() - if dataset and document: + if not document_segment: + continue + if dataset and document_segment: source = { "position": resource_number, "dataset_id": dataset.id, "dataset_name": dataset.name, - "document_id": document.id, - "document_name": document.name, - "data_source_type": document.data_source_type, + "document_id": document_segment.id, + "document_name": document_segment.name, + "data_source_type": document_segment.data_source_type, "segment_id": segment.id, "retriever_from": self.retriever_from, "score": document_score_list.get(segment.index_node_id, None), diff --git a/api/core/tools/tool/dataset_retriever_tool.py b/api/core/tools/tool/dataset_retriever_tool.py index 3c9295c493..2d7e193e15 100644 --- a/api/core/tools/tool/dataset_retriever_tool.py +++ b/api/core/tools/tool/dataset_retriever_tool.py @@ -1,4 +1,4 @@ -from typing import Any +from typing import Any, Optional from core.app.app_config.entities import DatasetRetrieveConfigEntity from core.app.entities.app_invoke_entities import InvokeFrom @@ -23,7 +23,7 @@ class DatasetRetrieverTool(Tool): def get_dataset_tools( tenant_id: str, dataset_ids: list[str], - retrieve_config: DatasetRetrieveConfigEntity, + retrieve_config: Optional[DatasetRetrieveConfigEntity], return_resource: bool, invoke_from: InvokeFrom, hit_callback: DatasetIndexToolCallbackHandler, @@ -51,6 +51,8 @@ class DatasetRetrieverTool(Tool): invoke_from=invoke_from, hit_callback=hit_callback, ) + if retrieval_tools is None: + return [] # restore retrieve strategy retrieve_config.retrieve_strategy = original_retriever_mode @@ -83,6 +85,7 @@ class DatasetRetrieverTool(Tool): llm_description="Query for the dataset to be used to retrieve the dataset.", required=True, default="", + placeholder=I18nObject(en_US="", zh_Hans=""), ), ] @@ -102,7 +105,9 @@ class DatasetRetrieverTool(Tool): return self.create_text_message(text=result) - def validate_credentials(self, credentials: dict[str, Any], parameters: dict[str, Any]) -> None: + def validate_credentials( + self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False + ) -> str | None: """ validate the credentials for dataset retriever tool """ diff --git a/api/core/tools/tool/tool.py b/api/core/tools/tool/tool.py index 8d40450381..55f94d7619 100644 --- a/api/core/tools/tool/tool.py +++ b/api/core/tools/tool/tool.py @@ -91,7 +91,7 @@ class Tool(BaseModel, ABC): :return: the tool provider type """ - def load_variables(self, variables: ToolRuntimeVariablePool): + def load_variables(self, variables: ToolRuntimeVariablePool | None) -> None: """ load variables from database @@ -105,6 +105,8 @@ class Tool(BaseModel, ABC): """ if not self.variables: return + if self.identity is None: + return self.variables.set_file(self.identity.name, variable_name, image_key) @@ -114,6 +116,8 @@ class Tool(BaseModel, ABC): """ if not self.variables: return + if self.identity is None: + return self.variables.set_text(self.identity.name, variable_name, text) @@ -200,7 +204,11 @@ class Tool(BaseModel, ABC): def invoke(self, user_id: str, tool_parameters: Mapping[str, Any]) -> list[ToolInvokeMessage]: # update tool_parameters # TODO: Fix type error. + if self.runtime is None: + return [] if self.runtime.runtime_parameters: + # Convert Mapping to dict before updating + tool_parameters = dict(tool_parameters) tool_parameters.update(self.runtime.runtime_parameters) # try parse tool parameters into the correct type @@ -221,7 +229,7 @@ class Tool(BaseModel, ABC): Transform tool parameters type """ # Temp fix for the issue that the tool parameters will be converted to empty while validating the credentials - result = deepcopy(tool_parameters) + result: dict[str, Any] = deepcopy(dict(tool_parameters)) for parameter in self.parameters or []: if parameter.name in tool_parameters: result[parameter.name] = parameter.type.cast_value(tool_parameters[parameter.name]) @@ -234,12 +242,15 @@ class Tool(BaseModel, ABC): ) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: pass - def validate_credentials(self, credentials: dict[str, Any], parameters: dict[str, Any]) -> None: + def validate_credentials( + self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False + ) -> str | None: """ validate the credentials :param credentials: the credentials :param parameters: the parameters + :param format_only: only return the formatted """ pass diff --git a/api/core/tools/tool/workflow_tool.py b/api/core/tools/tool/workflow_tool.py index 33b4ad021a..edff4a2d07 100644 --- a/api/core/tools/tool/workflow_tool.py +++ b/api/core/tools/tool/workflow_tool.py @@ -68,20 +68,20 @@ class WorkflowTool(Tool): if data.get("error"): raise Exception(data.get("error")) - result = [] + r = [] outputs = data.get("outputs") if outputs == None: outputs = {} else: - outputs, files = self._extract_files(outputs) - for file in files: - result.append(self.create_file_message(file)) + outputs, extracted_files = self._extract_files(outputs) + for f in extracted_files: + r.append(self.create_file_message(f)) - result.append(self.create_text_message(json.dumps(outputs, ensure_ascii=False))) - result.append(self.create_json_message(outputs)) + r.append(self.create_text_message(json.dumps(outputs, ensure_ascii=False))) + r.append(self.create_json_message(outputs)) - return result + return r def _get_user(self, user_id: str) -> Union[EndUser, Account]: """ diff --git a/api/core/tools/tool_engine.py b/api/core/tools/tool_engine.py index f92b43608e..425a892527 100644 --- a/api/core/tools/tool_engine.py +++ b/api/core/tools/tool_engine.py @@ -3,7 +3,7 @@ from collections.abc import Mapping from copy import deepcopy from datetime import UTC, datetime from mimetypes import guess_type -from typing import Any, Optional, Union +from typing import Any, Optional, Union, cast from yarl import URL @@ -46,7 +46,7 @@ class ToolEngine: invoke_from: InvokeFrom, agent_tool_callback: DifyAgentCallbackHandler, trace_manager: Optional[TraceQueueManager] = None, - ) -> tuple[str, list[tuple[MessageFile, bool]], ToolInvokeMeta]: + ) -> tuple[str, list[tuple[MessageFile, str]], ToolInvokeMeta]: """ Agent invokes the tool with the given arguments. """ @@ -69,6 +69,8 @@ class ToolEngine: raise ValueError(f"tool_parameters should be a dict, but got a string: {tool_parameters}") # invoke the tool + if tool.identity is None: + raise ValueError("tool identity is not set") try: # hit the callback handler agent_tool_callback.on_tool_start(tool_name=tool.identity.name, tool_inputs=tool_parameters) @@ -163,6 +165,8 @@ class ToolEngine: """ Invoke the tool with the given arguments. """ + if tool.identity is None: + raise ValueError("tool identity is not set") started_at = datetime.now(UTC) meta = ToolInvokeMeta( time_cost=0.0, @@ -171,7 +175,7 @@ class ToolEngine: "tool_name": tool.identity.name, "tool_provider": tool.identity.provider, "tool_provider_type": tool.tool_provider_type().value, - "tool_parameters": deepcopy(tool.runtime.runtime_parameters), + "tool_parameters": deepcopy(tool.runtime.runtime_parameters) if tool.runtime else {}, "tool_icon": tool.identity.icon, }, ) @@ -194,9 +198,9 @@ class ToolEngine: result = "" for response in tool_response: if response.type == ToolInvokeMessage.MessageType.TEXT: - result += response.message + result += str(response.message) if response.message is not None else "" elif response.type == ToolInvokeMessage.MessageType.LINK: - result += f"result link: {response.message}. please tell user to check it." + result += f"result link: {response.message!r}. please tell user to check it." elif response.type in {ToolInvokeMessage.MessageType.IMAGE_LINK, ToolInvokeMessage.MessageType.IMAGE}: result += ( "image has been created and sent to user already, you do not need to create it," @@ -205,7 +209,7 @@ class ToolEngine: elif response.type == ToolInvokeMessage.MessageType.JSON: result += f"tool response: {json.dumps(response.message, ensure_ascii=False)}." else: - result += f"tool response: {response.message}." + result += f"tool response: {response.message!r}." return result @@ -223,7 +227,7 @@ class ToolEngine: mimetype = response.meta.get("mime_type") else: try: - url = URL(response.message) + url = URL(cast(str, response.message)) extension = url.suffix guess_type_result, _ = guess_type(f"a{extension}") if guess_type_result: @@ -237,7 +241,7 @@ class ToolEngine: result.append( ToolInvokeMessageBinary( mimetype=response.meta.get("mime_type", "image/jpeg"), - url=response.message, + url=cast(str, response.message), save_as=response.save_as, ) ) @@ -245,7 +249,7 @@ class ToolEngine: result.append( ToolInvokeMessageBinary( mimetype=response.meta.get("mime_type", "octet/stream"), - url=response.message, + url=cast(str, response.message), save_as=response.save_as, ) ) @@ -257,7 +261,7 @@ class ToolEngine: mimetype=response.meta.get("mime_type", "octet/stream") if response.meta else "octet/stream", - url=response.message, + url=cast(str, response.message), save_as=response.save_as, ) ) diff --git a/api/core/tools/tool_label_manager.py b/api/core/tools/tool_label_manager.py index 2a5a2944ef..e53985951b 100644 --- a/api/core/tools/tool_label_manager.py +++ b/api/core/tools/tool_label_manager.py @@ -84,13 +84,17 @@ class ToolLabelManager: if not isinstance(controller, ApiToolProviderController | WorkflowToolProviderController): raise ValueError("Unsupported tool type") - provider_ids = [controller.provider_id for controller in tool_providers] + provider_ids = [ + controller.provider_id + for controller in tool_providers + if isinstance(controller, (ApiToolProviderController, WorkflowToolProviderController)) + ] labels: list[ToolLabelBinding] = ( db.session.query(ToolLabelBinding).filter(ToolLabelBinding.tool_id.in_(provider_ids)).all() ) - tool_labels = {label.tool_id: [] for label in labels} + tool_labels: dict[str, list[str]] = {label.tool_id: [] for label in labels} for label in labels: tool_labels[label.tool_id].append(label.label_name) diff --git a/api/core/tools/tool_manager.py b/api/core/tools/tool_manager.py index ac333162b6..5b2173a4d0 100644 --- a/api/core/tools/tool_manager.py +++ b/api/core/tools/tool_manager.py @@ -4,7 +4,7 @@ import mimetypes from collections.abc import Generator from os import listdir, path from threading import Lock, Thread -from typing import Any, Optional, Union +from typing import Any, Optional, Union, cast from configs import dify_config from core.agent.entities import AgentToolEntity @@ -15,15 +15,18 @@ from core.model_runtime.utils.encoders import jsonable_encoder from core.tools.entities.api_entities import UserToolProvider, UserToolProviderTypeLiteral from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_entities import ApiProviderAuthType, ToolInvokeFrom, ToolParameter -from core.tools.errors import ToolProviderNotFoundError +from core.tools.errors import ToolNotFoundError, ToolProviderNotFoundError from core.tools.provider.api_tool_provider import ApiToolProviderController from core.tools.provider.builtin._positions import BuiltinToolProviderSort from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController +from core.tools.provider.tool_provider import ToolProviderController +from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController from core.tools.tool.api_tool import ApiTool from core.tools.tool.builtin_tool import BuiltinTool from core.tools.tool.tool import Tool from core.tools.tool_label_manager import ToolLabelManager from core.tools.utils.configuration import ToolConfigurationManager, ToolParameterConfigurationManager +from core.workflow.nodes.tool.entities import ToolEntity from extensions.ext_database import db from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider from services.tools.tools_transform_service import ToolTransformService @@ -33,9 +36,9 @@ logger = logging.getLogger(__name__) class ToolManager: _builtin_provider_lock = Lock() - _builtin_providers = {} + _builtin_providers: dict[str, BuiltinToolProviderController] = {} _builtin_providers_loaded = False - _builtin_tools_labels = {} + _builtin_tools_labels: dict[str, Union[I18nObject, None]] = {} @classmethod def get_builtin_provider(cls, provider: str) -> BuiltinToolProviderController: @@ -55,7 +58,7 @@ class ToolManager: return cls._builtin_providers[provider] @classmethod - def get_builtin_tool(cls, provider: str, tool_name: str) -> BuiltinTool: + def get_builtin_tool(cls, provider: str, tool_name: str) -> Union[BuiltinTool, Tool]: """ get the builtin tool @@ -66,13 +69,15 @@ class ToolManager: """ provider_controller = cls.get_builtin_provider(provider) tool = provider_controller.get_tool(tool_name) + if tool is None: + raise ToolNotFoundError(f"tool {tool_name} not found") return tool @classmethod def get_tool( cls, provider_type: str, provider_id: str, tool_name: str, tenant_id: Optional[str] = None - ) -> Union[BuiltinTool, ApiTool]: + ) -> Union[BuiltinTool, ApiTool, Tool]: """ get the tool @@ -103,7 +108,7 @@ class ToolManager: tenant_id: str, invoke_from: InvokeFrom = InvokeFrom.DEBUGGER, tool_invoke_from: ToolInvokeFrom = ToolInvokeFrom.AGENT, - ) -> Union[BuiltinTool, ApiTool]: + ) -> Union[BuiltinTool, ApiTool, Tool]: """ get the tool runtime @@ -113,6 +118,7 @@ class ToolManager: :return: the tool """ + controller: Union[BuiltinToolProviderController, ApiToolProviderController, WorkflowToolProviderController] if provider_type == "builtin": builtin_tool = cls.get_builtin_tool(provider_id, tool_name) @@ -129,7 +135,7 @@ class ToolManager: ) # get credentials - builtin_provider: BuiltinToolProvider = ( + builtin_provider: Optional[BuiltinToolProvider] = ( db.session.query(BuiltinToolProvider) .filter( BuiltinToolProvider.tenant_id == tenant_id, @@ -177,7 +183,7 @@ class ToolManager: } ) elif provider_type == "workflow": - workflow_provider = ( + workflow_provider: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id) .first() @@ -187,8 +193,13 @@ class ToolManager: raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found") controller = ToolTransformService.workflow_provider_to_controller(db_provider=workflow_provider) + controller_tools: Optional[list[Tool]] = controller.get_tools( + user_id="", tenant_id=workflow_provider.tenant_id + ) + if controller_tools is None or len(controller_tools) == 0: + raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found") - return controller.get_tools(user_id=None, tenant_id=workflow_provider.tenant_id)[0].fork_tool_runtime( + return controller_tools[0].fork_tool_runtime( runtime={ "tenant_id": tenant_id, "credentials": {}, @@ -215,7 +226,7 @@ class ToolManager: if parameter_rule.type == ToolParameter.ToolParameterType.SELECT: # check if tool_parameter_config in options - options = [x.value for x in parameter_rule.options] + options = [x.value for x in parameter_rule.options or []] if parameter_value is not None and parameter_value not in options: raise ValueError( f"tool parameter {parameter_rule.name} value {parameter_value} not in options {options}" @@ -267,6 +278,8 @@ class ToolManager: identity_id=f"AGENT.{app_id}", ) runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters) + if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None: + raise ValueError("runtime not found or runtime parameters not found") tool_entity.runtime.runtime_parameters.update(runtime_parameters) return tool_entity @@ -312,6 +325,9 @@ class ToolManager: if runtime_parameters: runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters) + if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None: + raise ValueError("runtime not found or runtime parameters not found") + tool_entity.runtime.runtime_parameters.update(runtime_parameters) return tool_entity @@ -326,6 +342,8 @@ class ToolManager: """ # get provider provider_controller = cls.get_builtin_provider(provider) + if provider_controller.identity is None: + raise ToolProviderNotFoundError(f"builtin provider {provider} not found") absolute_path = path.join( path.dirname(path.realpath(__file__)), @@ -381,11 +399,15 @@ class ToolManager: ), parent_type=BuiltinToolProviderController, ) - provider: BuiltinToolProviderController = provider_class() - cls._builtin_providers[provider.identity.name] = provider - for tool in provider.get_tools(): + provider_controller: BuiltinToolProviderController = provider_class() + if provider_controller.identity is None: + continue + cls._builtin_providers[provider_controller.identity.name] = provider_controller + for tool in provider_controller.get_tools() or []: + if tool.identity is None: + continue cls._builtin_tools_labels[tool.identity.name] = tool.identity.label - yield provider + yield provider_controller except Exception as e: logger.exception(f"load builtin provider {provider}") @@ -449,9 +471,11 @@ class ToolManager: # append builtin providers for provider in builtin_providers: # handle include, exclude + if provider.identity is None: + continue if is_filtered( - include_set=dify_config.POSITION_TOOL_INCLUDES_SET, - exclude_set=dify_config.POSITION_TOOL_EXCLUDES_SET, + include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET), + exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET), data=provider, name_func=lambda x: x.identity.name, ): @@ -472,7 +496,7 @@ class ToolManager: db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all() ) - api_provider_controllers = [ + api_provider_controllers: list[dict[str, Any]] = [ {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)} for provider in db_api_providers ] @@ -495,7 +519,7 @@ class ToolManager: db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all() ) - workflow_provider_controllers = [] + workflow_provider_controllers: list[WorkflowToolProviderController] = [] for provider in workflow_providers: try: workflow_provider_controllers.append( @@ -505,7 +529,9 @@ class ToolManager: # app has been deleted pass - labels = ToolLabelManager.get_tools_labels(workflow_provider_controllers) + labels = ToolLabelManager.get_tools_labels( + [cast(ToolProviderController, controller) for controller in workflow_provider_controllers] + ) for provider_controller in workflow_provider_controllers: user_provider = ToolTransformService.workflow_provider_to_user_provider( @@ -527,7 +553,7 @@ class ToolManager: :return: the provider controller, the credentials """ - provider: ApiToolProvider = ( + provider: Optional[ApiToolProvider] = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.id == provider_id, @@ -556,7 +582,7 @@ class ToolManager: get tool provider """ provider_name = provider - provider: ApiToolProvider = ( + provider_tool: Optional[ApiToolProvider] = ( db.session.query(ApiToolProvider) .filter( ApiToolProvider.tenant_id == tenant_id, @@ -565,17 +591,18 @@ class ToolManager: .first() ) - if provider is None: + if provider_tool is None: raise ValueError(f"you have not added provider {provider_name}") try: - credentials = json.loads(provider.credentials_str) or {} + credentials = json.loads(provider_tool.credentials_str) or {} except: credentials = {} # package tool provider controller controller = ApiToolProviderController.from_db( - provider, ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE + provider_tool, + ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE, ) # init tool configuration tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller) @@ -584,25 +611,28 @@ class ToolManager: masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials) try: - icon = json.loads(provider.icon) + icon = json.loads(provider_tool.icon) except: icon = {"background": "#252525", "content": "\ud83d\ude01"} # add tool labels labels = ToolLabelManager.get_tool_labels(controller) - return jsonable_encoder( - { - "schema_type": provider.schema_type, - "schema": provider.schema, - "tools": provider.tools, - "icon": icon, - "description": provider.description, - "credentials": masked_credentials, - "privacy_policy": provider.privacy_policy, - "custom_disclaimer": provider.custom_disclaimer, - "labels": labels, - } + return cast( + dict, + jsonable_encoder( + { + "schema_type": provider_tool.schema_type, + "schema": provider_tool.schema, + "tools": provider_tool.tools, + "icon": icon, + "description": provider_tool.description, + "credentials": masked_credentials, + "privacy_policy": provider_tool.privacy_policy, + "custom_disclaimer": provider_tool.custom_disclaimer, + "labels": labels, + } + ), ) @classmethod @@ -617,6 +647,7 @@ class ToolManager: """ provider_type = provider_type provider_id = provider_id + provider: Optional[Union[BuiltinToolProvider, ApiToolProvider, WorkflowToolProvider]] = None if provider_type == "builtin": return ( dify_config.CONSOLE_API_URL @@ -626,16 +657,21 @@ class ToolManager: ) elif provider_type == "api": try: - provider: ApiToolProvider = ( + provider = ( db.session.query(ApiToolProvider) .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id) .first() ) - return json.loads(provider.icon) + if provider is None: + raise ToolProviderNotFoundError(f"api provider {provider_id} not found") + icon = json.loads(provider.icon) + if isinstance(icon, (str, dict)): + return icon + return {"background": "#252525", "content": "\ud83d\ude01"} except: return {"background": "#252525", "content": "\ud83d\ude01"} elif provider_type == "workflow": - provider: WorkflowToolProvider = ( + provider = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id) .first() @@ -643,7 +679,13 @@ class ToolManager: if provider is None: raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found") - return json.loads(provider.icon) + try: + icon = json.loads(provider.icon) + if isinstance(icon, (str, dict)): + return icon + return {"background": "#252525", "content": "\ud83d\ude01"} + except: + return {"background": "#252525", "content": "\ud83d\ude01"} else: raise ValueError(f"provider type {provider_type} not found") diff --git a/api/core/tools/utils/configuration.py b/api/core/tools/utils/configuration.py index 8b5e27f538..d772092864 100644 --- a/api/core/tools/utils/configuration.py +++ b/api/core/tools/utils/configuration.py @@ -72,9 +72,13 @@ class ToolConfigurationManager(BaseModel): return a deep copy of credentials with decrypted values """ + identity_id = "" + if self.provider_controller.identity: + identity_id = f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}" + cache = ToolProviderCredentialsCache( tenant_id=self.tenant_id, - identity_id=f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}", + identity_id=identity_id, cache_type=ToolProviderCredentialsCacheType.PROVIDER, ) cached_credentials = cache.get() @@ -95,9 +99,13 @@ class ToolConfigurationManager(BaseModel): return credentials def delete_tool_credentials_cache(self): + identity_id = "" + if self.provider_controller.identity: + identity_id = f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}" + cache = ToolProviderCredentialsCache( tenant_id=self.tenant_id, - identity_id=f"{self.provider_controller.provider_type.value}.{self.provider_controller.identity.name}", + identity_id=identity_id, cache_type=ToolProviderCredentialsCacheType.PROVIDER, ) cache.delete() @@ -199,6 +207,9 @@ class ToolParameterConfigurationManager(BaseModel): return a deep copy of parameters with decrypted values """ + if self.tool_runtime is None or self.tool_runtime.identity is None: + raise ValueError("tool_runtime is required") + cache = ToolParameterCache( tenant_id=self.tenant_id, provider=f"{self.provider_type}.{self.provider_name}", @@ -232,6 +243,9 @@ class ToolParameterConfigurationManager(BaseModel): return parameters def delete_tool_parameters_cache(self): + if self.tool_runtime is None or self.tool_runtime.identity is None: + raise ValueError("tool_runtime is required") + cache = ToolParameterCache( tenant_id=self.tenant_id, provider=f"{self.provider_type}.{self.provider_name}", diff --git a/api/core/tools/utils/feishu_api_utils.py b/api/core/tools/utils/feishu_api_utils.py index ea28037df0..ecf60045aa 100644 --- a/api/core/tools/utils/feishu_api_utils.py +++ b/api/core/tools/utils/feishu_api_utils.py @@ -1,5 +1,5 @@ import json -from typing import Optional +from typing import Any, Optional, cast import httpx @@ -101,7 +101,7 @@ class FeishuRequest: """ url = f"{self.API_BASE_URL}/access_token/get_tenant_access_token" payload = {"app_id": app_id, "app_secret": app_secret} - res = self._send_request(url, require_token=False, payload=payload) + res: dict = self._send_request(url, require_token=False, payload=payload) return res def create_document(self, title: str, content: str, folder_token: str) -> dict: @@ -126,15 +126,16 @@ class FeishuRequest: "content": content, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def write_document(self, document_id: str, content: str, position: str = "end") -> dict: url = f"{self.API_BASE_URL}/document/write_document" payload = {"document_id": document_id, "content": content, "position": position} - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) return res def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> str: @@ -155,9 +156,9 @@ class FeishuRequest: "lang": lang, } url = f"{self.API_BASE_URL}/document/get_document_content" - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data").get("content") + return cast(str, res.get("data", {}).get("content")) return "" def list_document_blocks( @@ -173,9 +174,10 @@ class FeishuRequest: "page_token": page_token, } url = f"{self.API_BASE_URL}/document/list_document_blocks" - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict: @@ -191,9 +193,10 @@ class FeishuRequest: "msg_type": msg_type, "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict: @@ -203,7 +206,7 @@ class FeishuRequest: "msg_type": msg_type, "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"), } - res = self._send_request(url, require_token=False, payload=payload) + res: dict = self._send_request(url, require_token=False, payload=payload) return res def get_chat_messages( @@ -227,9 +230,10 @@ class FeishuRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_thread_messages( @@ -245,9 +249,10 @@ class FeishuRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict: @@ -260,9 +265,10 @@ class FeishuRequest: "completed_at": completed_time, "description": description, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_task( @@ -278,9 +284,10 @@ class FeishuRequest: "completed_time": completed_time, "description": description, } - res = self._send_request(url, method="PATCH", payload=payload) + res: dict = self._send_request(url, method="PATCH", payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_task(self, task_guid: str) -> dict: @@ -289,7 +296,7 @@ class FeishuRequest: payload = { "task_guid": task_guid, } - res = self._send_request(url, method="DELETE", payload=payload) + res: dict = self._send_request(url, method="DELETE", payload=payload) return res def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict: @@ -300,7 +307,7 @@ class FeishuRequest: "member_phone_or_email": member_phone_or_email, "member_role": member_role, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) return res def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict: @@ -312,9 +319,10 @@ class FeishuRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_primary_calendar(self, user_id_type: str = "open_id") -> dict: @@ -322,9 +330,10 @@ class FeishuRequest: params = { "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_event( @@ -347,9 +356,10 @@ class FeishuRequest: "auto_record": auto_record, "attendee_ability": attendee_ability, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_event( @@ -363,7 +373,7 @@ class FeishuRequest: auto_record: bool, ) -> dict: url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}" - payload = {} + payload: dict[str, Any] = {} if summary: payload["summary"] = summary if description: @@ -376,7 +386,7 @@ class FeishuRequest: payload["need_notification"] = need_notification if auto_record: payload["auto_record"] = auto_record - res = self._send_request(url, method="PATCH", payload=payload) + res: dict = self._send_request(url, method="PATCH", payload=payload) return res def delete_event(self, event_id: str, need_notification: bool = True) -> dict: @@ -384,7 +394,7 @@ class FeishuRequest: params = { "need_notification": need_notification, } - res = self._send_request(url, method="DELETE", params=params) + res: dict = self._send_request(url, method="DELETE", params=params) return res def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict: @@ -395,9 +405,10 @@ class FeishuRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def search_events( @@ -418,9 +429,10 @@ class FeishuRequest: "user_id_type": user_id_type, "page_size": page_size, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict: @@ -431,9 +443,10 @@ class FeishuRequest: "attendee_phone_or_email": attendee_phone_or_email, "need_notification": need_notification, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_spreadsheet( @@ -447,9 +460,10 @@ class FeishuRequest: "title": title, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_spreadsheet( @@ -463,9 +477,10 @@ class FeishuRequest: "spreadsheet_token": spreadsheet_token, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def list_spreadsheet_sheets( @@ -477,9 +492,10 @@ class FeishuRequest: params = { "spreadsheet_token": spreadsheet_token, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_rows( @@ -499,9 +515,10 @@ class FeishuRequest: "length": length, "values": values, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_cols( @@ -521,9 +538,10 @@ class FeishuRequest: "length": length, "values": values, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_rows( @@ -545,9 +563,10 @@ class FeishuRequest: "num_rows": num_rows, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_cols( @@ -569,9 +588,10 @@ class FeishuRequest: "num_cols": num_cols, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_table( @@ -593,9 +613,10 @@ class FeishuRequest: "query": query, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_base( @@ -609,9 +630,10 @@ class FeishuRequest: "name": name, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_records( @@ -633,9 +655,10 @@ class FeishuRequest: payload = { "records": convert_add_records(records), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_records( @@ -657,9 +680,10 @@ class FeishuRequest: payload = { "records": convert_update_records(records), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_records( @@ -686,9 +710,10 @@ class FeishuRequest: payload = { "records": record_id_list, } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def search_record( @@ -740,7 +765,7 @@ class FeishuRequest: except json.JSONDecodeError: raise ValueError("The input string is not valid JSON") - payload = {} + payload: dict[str, Any] = {} if view_id: payload["view_id"] = view_id @@ -752,10 +777,11 @@ class FeishuRequest: payload["filter"] = filter_dict if automatic_fields: payload["automatic_fields"] = automatic_fields - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_base_info( @@ -767,9 +793,10 @@ class FeishuRequest: params = { "app_token": app_token, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_table( @@ -797,9 +824,10 @@ class FeishuRequest: } if default_view_name: payload["default_view_name"] = default_view_name - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_tables( @@ -834,9 +862,10 @@ class FeishuRequest: "table_names": table_name_list, } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def list_tables( @@ -852,9 +881,10 @@ class FeishuRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_records( @@ -882,7 +912,8 @@ class FeishuRequest: "record_ids": record_id_list, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params, payload=payload) + res: dict = self._send_request(url, method="GET", params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res diff --git a/api/core/tools/utils/lark_api_utils.py b/api/core/tools/utils/lark_api_utils.py index 30cb0cb141..de394a39bf 100644 --- a/api/core/tools/utils/lark_api_utils.py +++ b/api/core/tools/utils/lark_api_utils.py @@ -1,5 +1,5 @@ import json -from typing import Optional +from typing import Any, Optional, cast import httpx @@ -62,12 +62,10 @@ class LarkRequest: def tenant_access_token(self) -> str: feishu_tenant_access_token = f"tools:{self.app_id}:feishu_tenant_access_token" if redis_client.exists(feishu_tenant_access_token): - return redis_client.get(feishu_tenant_access_token).decode() - res = self.get_tenant_access_token(self.app_id, self.app_secret) + return str(redis_client.get(feishu_tenant_access_token).decode()) + res: dict[str, str] = self.get_tenant_access_token(self.app_id, self.app_secret) redis_client.setex(feishu_tenant_access_token, res.get("expire"), res.get("tenant_access_token")) - if "tenant_access_token" in res: - return res.get("tenant_access_token") - return "" + return res.get("tenant_access_token", "") def _send_request( self, @@ -91,7 +89,7 @@ class LarkRequest: def get_tenant_access_token(self, app_id: str, app_secret: str) -> dict: url = f"{self.API_BASE_URL}/access_token/get_tenant_access_token" payload = {"app_id": app_id, "app_secret": app_secret} - res = self._send_request(url, require_token=False, payload=payload) + res: dict = self._send_request(url, require_token=False, payload=payload) return res def create_document(self, title: str, content: str, folder_token: str) -> dict: @@ -101,15 +99,16 @@ class LarkRequest: "content": content, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def write_document(self, document_id: str, content: str, position: str = "end") -> dict: url = f"{self.API_BASE_URL}/document/write_document" payload = {"document_id": document_id, "content": content, "position": position} - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) return res def get_document_content(self, document_id: str, mode: str = "markdown", lang: str = "0") -> str | dict: @@ -119,9 +118,9 @@ class LarkRequest: "lang": lang, } url = f"{self.API_BASE_URL}/document/get_document_content" - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data").get("content") + return cast(dict, res.get("data", {}).get("content")) return "" def list_document_blocks( @@ -134,9 +133,10 @@ class LarkRequest: "page_token": page_token, } url = f"{self.API_BASE_URL}/document/list_document_blocks" - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def send_bot_message(self, receive_id_type: str, receive_id: str, msg_type: str, content: str) -> dict: @@ -149,9 +149,10 @@ class LarkRequest: "msg_type": msg_type, "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def send_webhook_message(self, webhook: str, msg_type: str, content: str) -> dict: @@ -161,7 +162,7 @@ class LarkRequest: "msg_type": msg_type, "content": content.strip('"').replace(r"\"", '"').replace(r"\\", "\\"), } - res = self._send_request(url, require_token=False, payload=payload) + res: dict = self._send_request(url, require_token=False, payload=payload) return res def get_chat_messages( @@ -182,9 +183,10 @@ class LarkRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_thread_messages( @@ -197,9 +199,10 @@ class LarkRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_task(self, summary: str, start_time: str, end_time: str, completed_time: str, description: str) -> dict: @@ -211,9 +214,10 @@ class LarkRequest: "completed_at": completed_time, "description": description, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_task( @@ -228,9 +232,10 @@ class LarkRequest: "completed_time": completed_time, "description": description, } - res = self._send_request(url, method="PATCH", payload=payload) + res: dict = self._send_request(url, method="PATCH", payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_task(self, task_guid: str) -> dict: @@ -238,9 +243,10 @@ class LarkRequest: payload = { "task_guid": task_guid, } - res = self._send_request(url, method="DELETE", payload=payload) + res: dict = self._send_request(url, method="DELETE", payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_members(self, task_guid: str, member_phone_or_email: str, member_role: str) -> dict: @@ -250,9 +256,10 @@ class LarkRequest: "member_phone_or_email": member_phone_or_email, "member_role": member_role, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_wiki_nodes(self, space_id: str, parent_node_token: str, page_token: str, page_size: int = 20) -> dict: @@ -263,9 +270,10 @@ class LarkRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_primary_calendar(self, user_id_type: str = "open_id") -> dict: @@ -273,9 +281,10 @@ class LarkRequest: params = { "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_event( @@ -298,9 +307,10 @@ class LarkRequest: "auto_record": auto_record, "attendee_ability": attendee_ability, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_event( @@ -314,7 +324,7 @@ class LarkRequest: auto_record: bool, ) -> dict: url = f"{self.API_BASE_URL}/calendar/update_event/{event_id}" - payload = {} + payload: dict[str, Any] = {} if summary: payload["summary"] = summary if description: @@ -327,7 +337,7 @@ class LarkRequest: payload["need_notification"] = need_notification if auto_record: payload["auto_record"] = auto_record - res = self._send_request(url, method="PATCH", payload=payload) + res: dict = self._send_request(url, method="PATCH", payload=payload) return res def delete_event(self, event_id: str, need_notification: bool = True) -> dict: @@ -335,7 +345,7 @@ class LarkRequest: params = { "need_notification": need_notification, } - res = self._send_request(url, method="DELETE", params=params) + res: dict = self._send_request(url, method="DELETE", params=params) return res def list_events(self, start_time: str, end_time: str, page_token: str, page_size: int = 50) -> dict: @@ -346,9 +356,10 @@ class LarkRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def search_events( @@ -369,9 +380,10 @@ class LarkRequest: "user_id_type": user_id_type, "page_size": page_size, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_event_attendees(self, event_id: str, attendee_phone_or_email: str, need_notification: bool = True) -> dict: @@ -381,9 +393,10 @@ class LarkRequest: "attendee_phone_or_email": attendee_phone_or_email, "need_notification": need_notification, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_spreadsheet( @@ -396,9 +409,10 @@ class LarkRequest: "title": title, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_spreadsheet( @@ -411,9 +425,10 @@ class LarkRequest: "spreadsheet_token": spreadsheet_token, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def list_spreadsheet_sheets( @@ -424,9 +439,10 @@ class LarkRequest: params = { "spreadsheet_token": spreadsheet_token, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_rows( @@ -445,9 +461,10 @@ class LarkRequest: "length": length, "values": values, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_cols( @@ -466,9 +483,10 @@ class LarkRequest: "length": length, "values": values, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_rows( @@ -489,9 +507,10 @@ class LarkRequest: "num_rows": num_rows, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_cols( @@ -512,9 +531,10 @@ class LarkRequest: "num_cols": num_cols, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_table( @@ -535,9 +555,10 @@ class LarkRequest: "query": query, "user_id_type": user_id_type, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_base( @@ -550,9 +571,10 @@ class LarkRequest: "name": name, "folder_token": folder_token, } - res = self._send_request(url, payload=payload) + res: dict = self._send_request(url, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def add_records( @@ -573,9 +595,10 @@ class LarkRequest: payload = { "records": self.convert_add_records(records), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def update_records( @@ -596,9 +619,10 @@ class LarkRequest: payload = { "records": self.convert_update_records(records), } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_records( @@ -624,9 +648,10 @@ class LarkRequest: payload = { "records": record_id_list, } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def search_record( @@ -678,7 +703,7 @@ class LarkRequest: except json.JSONDecodeError: raise ValueError("The input string is not valid JSON") - payload = {} + payload: dict[str, Any] = {} if view_id: payload["view_id"] = view_id @@ -690,9 +715,10 @@ class LarkRequest: payload["filter"] = filter_dict if automatic_fields: payload["automatic_fields"] = automatic_fields - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def get_base_info( @@ -703,9 +729,10 @@ class LarkRequest: params = { "app_token": app_token, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def create_table( @@ -732,9 +759,10 @@ class LarkRequest: } if default_view_name: payload["default_view_name"] = default_view_name - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def delete_tables( @@ -767,9 +795,10 @@ class LarkRequest: "table_ids": table_id_list, "table_names": table_name_list, } - res = self._send_request(url, params=params, payload=payload) + res: dict = self._send_request(url, params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def list_tables( @@ -784,9 +813,10 @@ class LarkRequest: "page_token": page_token, "page_size": page_size, } - res = self._send_request(url, method="GET", params=params) + res: dict = self._send_request(url, method="GET", params=params) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res def read_records( @@ -814,7 +844,8 @@ class LarkRequest: "record_ids": record_id_list, "user_id_type": user_id_type, } - res = self._send_request(url, method="POST", params=params, payload=payload) + res: dict = self._send_request(url, method="POST", params=params, payload=payload) if "data" in res: - return res.get("data") + data: dict = res.get("data", {}) + return data return res diff --git a/api/core/tools/utils/message_transformer.py b/api/core/tools/utils/message_transformer.py index e30c903a4b..3509f1e6e5 100644 --- a/api/core/tools/utils/message_transformer.py +++ b/api/core/tools/utils/message_transformer.py @@ -90,12 +90,12 @@ class ToolFileMessageTransformer: ) elif message.type == ToolInvokeMessage.MessageType.FILE: assert message.meta is not None - file = message.meta.get("file") - if isinstance(file, File): - if file.transfer_method == FileTransferMethod.TOOL_FILE: - assert file.related_id is not None - url = cls.get_tool_file_url(tool_file_id=file.related_id, extension=file.extension) - if file.type == FileType.IMAGE: + file_mata = message.meta.get("file") + if isinstance(file_mata, File): + if file_mata.transfer_method == FileTransferMethod.TOOL_FILE: + assert file_mata.related_id is not None + url = cls.get_tool_file_url(tool_file_id=file_mata.related_id, extension=file_mata.extension) + if file_mata.type == FileType.IMAGE: result.append( ToolInvokeMessage( type=ToolInvokeMessage.MessageType.IMAGE_LINK, diff --git a/api/core/tools/utils/model_invocation_utils.py b/api/core/tools/utils/model_invocation_utils.py index 4e226810d6..3689dcc9e5 100644 --- a/api/core/tools/utils/model_invocation_utils.py +++ b/api/core/tools/utils/model_invocation_utils.py @@ -5,7 +5,7 @@ Therefore, a model manager is needed to list/invoke/validate models. """ import json -from typing import cast +from typing import Optional, cast from core.model_manager import ModelManager from core.model_runtime.entities.llm_entities import LLMResult @@ -51,7 +51,7 @@ class ModelInvocationUtils: if not schema: raise InvokeModelError("No model schema found") - max_tokens = schema.model_properties.get(ModelPropertyKey.CONTEXT_SIZE, None) + max_tokens: Optional[int] = schema.model_properties.get(ModelPropertyKey.CONTEXT_SIZE, None) if max_tokens is None: return 2048 @@ -133,14 +133,17 @@ class ModelInvocationUtils: db.session.commit() try: - response: LLMResult = model_instance.invoke_llm( - prompt_messages=prompt_messages, - model_parameters=model_parameters, - tools=[], - stop=[], - stream=False, - user=user_id, - callbacks=[], + response: LLMResult = cast( + LLMResult, + model_instance.invoke_llm( + prompt_messages=prompt_messages, + model_parameters=model_parameters, + tools=[], + stop=[], + stream=False, + user=user_id, + callbacks=[], + ), ) except InvokeRateLimitError as e: raise InvokeModelError(f"Invoke rate limit error: {e}") diff --git a/api/core/tools/utils/parser.py b/api/core/tools/utils/parser.py index ae44b1b99d..f1dc1123b9 100644 --- a/api/core/tools/utils/parser.py +++ b/api/core/tools/utils/parser.py @@ -6,7 +6,7 @@ from json.decoder import JSONDecodeError from typing import Optional from requests import get -from yaml import YAMLError, safe_load +from yaml import YAMLError, safe_load # type: ignore from core.tools.entities.common_entities import I18nObject from core.tools.entities.tool_bundle import ApiToolBundle @@ -64,6 +64,9 @@ class ApiBasedToolSchemaParser: default=parameter["schema"]["default"] if "schema" in parameter and "default" in parameter["schema"] else None, + placeholder=I18nObject( + en_US=parameter.get("description", ""), zh_Hans=parameter.get("description", "") + ), ) # check if there is a type @@ -108,6 +111,9 @@ class ApiBasedToolSchemaParser: form=ToolParameter.ToolParameterForm.LLM, llm_description=property.get("description", ""), default=property.get("default", None), + placeholder=I18nObject( + en_US=parameter.get("description", ""), zh_Hans=parameter.get("description", "") + ), ) # check if there is a type @@ -158,9 +164,9 @@ class ApiBasedToolSchemaParser: return bundles @staticmethod - def _get_tool_parameter_type(parameter: dict) -> ToolParameter.ToolParameterType: + def _get_tool_parameter_type(parameter: dict) -> Optional[ToolParameter.ToolParameterType]: parameter = parameter or {} - typ = None + typ: Optional[str] = None if parameter.get("format") == "binary": return ToolParameter.ToolParameterType.FILE @@ -175,6 +181,8 @@ class ApiBasedToolSchemaParser: return ToolParameter.ToolParameterType.BOOLEAN elif typ == "string": return ToolParameter.ToolParameterType.STRING + else: + return None @staticmethod def parse_openapi_yaml_to_tool_bundle( @@ -236,7 +244,8 @@ class ApiBasedToolSchemaParser: if ("summary" not in operation or len(operation["summary"]) == 0) and ( "description" not in operation or len(operation["description"]) == 0 ): - warning["missing_summary"] = f"No summary or description found in operation {method} {path}." + if warning is not None: + warning["missing_summary"] = f"No summary or description found in operation {method} {path}." openapi["paths"][path][method] = { "operationId": operation["operationId"], diff --git a/api/core/tools/utils/web_reader_tool.py b/api/core/tools/utils/web_reader_tool.py index 3aae31e93a..d42fd99fce 100644 --- a/api/core/tools/utils/web_reader_tool.py +++ b/api/core/tools/utils/web_reader_tool.py @@ -9,13 +9,13 @@ import tempfile import unicodedata from contextlib import contextmanager from pathlib import Path -from typing import Optional +from typing import Any, Literal, Optional, cast from urllib.parse import unquote import chardet -import cloudscraper -from bs4 import BeautifulSoup, CData, Comment, NavigableString -from regex import regex +import cloudscraper # type: ignore +from bs4 import BeautifulSoup, CData, Comment, NavigableString # type: ignore +from regex import regex # type: ignore from core.helper import ssrf_proxy from core.rag.extractor import extract_processor @@ -68,7 +68,7 @@ def get_url(url: str, user_agent: Optional[str] = None) -> str: return "Unsupported content-type [{}] of URL.".format(main_content_type) if main_content_type in extract_processor.SUPPORT_URL_CONTENT_TYPES: - return ExtractProcessor.load_from_url(url, return_text=True) + return cast(str, ExtractProcessor.load_from_url(url, return_text=True)) response = ssrf_proxy.get(url, headers=headers, follow_redirects=True, timeout=(120, 300)) elif response.status_code == 403: @@ -125,7 +125,7 @@ def extract_using_readabilipy(html): os.unlink(article_json_path) os.unlink(html_path) - article_json = { + article_json: dict[str, Any] = { "title": None, "byline": None, "date": None, @@ -300,7 +300,7 @@ def strip_control_characters(text): def normalize_unicode(text): """Normalize unicode such that things that are visually equivalent map to the same unicode string where possible.""" - normal_form = "NFKC" + normal_form: Literal["NFC", "NFD", "NFKC", "NFKD"] = "NFKC" text = unicodedata.normalize(normal_form, text) return text @@ -332,6 +332,7 @@ def add_content_digest(element): def content_digest(element): + digest: Any if is_text(element): # Hash trimmed_string = element.string.strip() diff --git a/api/core/tools/utils/workflow_configuration_sync.py b/api/core/tools/utils/workflow_configuration_sync.py index d92bfb9b90..08a112cfdb 100644 --- a/api/core/tools/utils/workflow_configuration_sync.py +++ b/api/core/tools/utils/workflow_configuration_sync.py @@ -7,7 +7,7 @@ from core.tools.entities.tool_entities import WorkflowToolParameterConfiguration class WorkflowToolConfigurationUtils: @classmethod - def check_parameter_configurations(cls, configurations: Mapping[str, Any]): + def check_parameter_configurations(cls, configurations: list[Mapping[str, Any]]): for configuration in configurations: WorkflowToolParameterConfiguration.model_validate(configuration) @@ -27,7 +27,7 @@ class WorkflowToolConfigurationUtils: @classmethod def check_is_synced( cls, variables: list[VariableEntity], tool_configurations: list[WorkflowToolParameterConfiguration] - ) -> None: + ) -> bool: """ check is synced diff --git a/api/core/tools/utils/yaml_utils.py b/api/core/tools/utils/yaml_utils.py index 42c7f85bc6..ee7ca11e05 100644 --- a/api/core/tools/utils/yaml_utils.py +++ b/api/core/tools/utils/yaml_utils.py @@ -2,7 +2,7 @@ import logging from pathlib import Path from typing import Any -import yaml +import yaml # type: ignore from yaml import YAMLError logger = logging.getLogger(__name__) diff --git a/api/core/variables/variables.py b/api/core/variables/variables.py index 973e420961..c32815b24d 100644 --- a/api/core/variables/variables.py +++ b/api/core/variables/variables.py @@ -1,4 +1,5 @@ from collections.abc import Sequence +from typing import cast from uuid import uuid4 from pydantic import Field @@ -78,7 +79,7 @@ class SecretVariable(StringVariable): @property def log(self) -> str: - return encrypter.obfuscated_token(self.value) + return cast(str, encrypter.obfuscated_token(self.value)) class NoneVariable(NoneSegment, Variable): diff --git a/api/core/workflow/callbacks/workflow_logging_callback.py b/api/core/workflow/callbacks/workflow_logging_callback.py index ed737e7316..b9c6b35ad3 100644 --- a/api/core/workflow/callbacks/workflow_logging_callback.py +++ b/api/core/workflow/callbacks/workflow_logging_callback.py @@ -33,7 +33,7 @@ _TEXT_COLOR_MAPPING = { class WorkflowLoggingCallback(WorkflowCallback): def __init__(self) -> None: - self.current_node_id = None + self.current_node_id: Optional[str] = None def on_event(self, event: GraphEngineEvent) -> None: if isinstance(event, GraphRunStartedEvent): diff --git a/api/core/workflow/entities/node_entities.py b/api/core/workflow/entities/node_entities.py index ca01dcd7d8..ae5f117bf9 100644 --- a/api/core/workflow/entities/node_entities.py +++ b/api/core/workflow/entities/node_entities.py @@ -36,9 +36,9 @@ class NodeRunResult(BaseModel): status: WorkflowNodeExecutionStatus = WorkflowNodeExecutionStatus.RUNNING inputs: Optional[Mapping[str, Any]] = None # node inputs - process_data: Optional[dict[str, Any]] = None # process data + process_data: Optional[Mapping[str, Any]] = None # process data outputs: Optional[Mapping[str, Any]] = None # node outputs - metadata: Optional[dict[NodeRunMetadataKey, Any]] = None # node metadata + metadata: Optional[Mapping[NodeRunMetadataKey, Any]] = None # node metadata llm_usage: Optional[LLMUsage] = None # llm usage edge_source_handle: Optional[str] = None # source handle id of node with multiple branches diff --git a/api/core/workflow/graph_engine/condition_handlers/condition_handler.py b/api/core/workflow/graph_engine/condition_handlers/condition_handler.py index bc3a15bd00..b8470aecbd 100644 --- a/api/core/workflow/graph_engine/condition_handlers/condition_handler.py +++ b/api/core/workflow/graph_engine/condition_handlers/condition_handler.py @@ -5,7 +5,7 @@ from core.workflow.utils.condition.processor import ConditionProcessor class ConditionRunConditionHandlerHandler(RunConditionHandler): - def check(self, graph_runtime_state: GraphRuntimeState, previous_route_node_state: RouteNodeState) -> bool: + def check(self, graph_runtime_state: GraphRuntimeState, previous_route_node_state: RouteNodeState): """ Check if the condition can be executed diff --git a/api/core/workflow/graph_engine/entities/graph.py b/api/core/workflow/graph_engine/entities/graph.py index 800dd136af..b3bcc3b2cc 100644 --- a/api/core/workflow/graph_engine/entities/graph.py +++ b/api/core/workflow/graph_engine/entities/graph.py @@ -1,4 +1,5 @@ import uuid +from collections import defaultdict from collections.abc import Mapping from typing import Any, Optional, cast @@ -310,26 +311,17 @@ class Graph(BaseModel): parallel = None if len(target_node_edges) > 1: # fetch all node ids in current parallels - parallel_branch_node_ids = {} - condition_edge_mappings = {} + parallel_branch_node_ids = defaultdict(list) + condition_edge_mappings = defaultdict(list) for graph_edge in target_node_edges: if graph_edge.run_condition is None: - if "default" not in parallel_branch_node_ids: - parallel_branch_node_ids["default"] = [] - parallel_branch_node_ids["default"].append(graph_edge.target_node_id) else: condition_hash = graph_edge.run_condition.hash - if condition_hash not in condition_edge_mappings: - condition_edge_mappings[condition_hash] = [] - condition_edge_mappings[condition_hash].append(graph_edge) for condition_hash, graph_edges in condition_edge_mappings.items(): if len(graph_edges) > 1: - if condition_hash not in parallel_branch_node_ids: - parallel_branch_node_ids[condition_hash] = [] - for graph_edge in graph_edges: parallel_branch_node_ids[condition_hash].append(graph_edge.target_node_id) @@ -418,7 +410,7 @@ class Graph(BaseModel): if condition_edge_mappings: for condition_hash, graph_edges in condition_edge_mappings.items(): for graph_edge in graph_edges: - current_parallel: GraphParallel | None = cls._get_current_parallel( + current_parallel = cls._get_current_parallel( parallel_mapping=parallel_mapping, graph_edge=graph_edge, parallel=condition_parallels.get(condition_hash), diff --git a/api/core/workflow/graph_engine/graph_engine.py b/api/core/workflow/graph_engine/graph_engine.py index 854036b2c1..db1e01f14f 100644 --- a/api/core/workflow/graph_engine/graph_engine.py +++ b/api/core/workflow/graph_engine/graph_engine.py @@ -40,6 +40,7 @@ from core.workflow.graph_engine.entities.graph_runtime_state import GraphRuntime from core.workflow.graph_engine.entities.runtime_route_state import RouteNodeState from core.workflow.nodes import NodeType from core.workflow.nodes.answer.answer_stream_processor import AnswerStreamProcessor +from core.workflow.nodes.answer.base_stream_processor import StreamProcessor from core.workflow.nodes.base import BaseNode from core.workflow.nodes.base.entities import BaseNodeData from core.workflow.nodes.end.end_stream_processor import EndStreamProcessor @@ -66,7 +67,7 @@ class GraphEngineThreadPool(ThreadPoolExecutor): self.max_submit_count = max_submit_count self.submit_count = 0 - def submit(self, fn, *args, **kwargs): + def submit(self, fn, /, *args, **kwargs): self.submit_count += 1 self.check_is_full() @@ -140,7 +141,8 @@ class GraphEngine: def run(self) -> Generator[GraphEngineEvent, None, None]: # trigger graph run start event yield GraphRunStartedEvent() - handle_exceptions = [] + handle_exceptions: list[str] = [] + stream_processor: StreamProcessor try: if self.init_params.workflow_type == WorkflowType.CHAT: @@ -168,7 +170,7 @@ class GraphEngine: elif isinstance(item, NodeRunSucceededEvent): if item.node_type == NodeType.END: self.graph_runtime_state.outputs = ( - item.route_node_state.node_run_result.outputs + dict(item.route_node_state.node_run_result.outputs) if item.route_node_state.node_run_result and item.route_node_state.node_run_result.outputs else {} @@ -350,7 +352,7 @@ class GraphEngine: if any(edge.run_condition for edge in edge_mappings): # if nodes has run conditions, get node id which branch to take based on the run condition results - condition_edge_mappings = {} + condition_edge_mappings: dict[str, list[GraphEdge]] = {} for edge in edge_mappings: if edge.run_condition: run_condition_hash = edge.run_condition.hash @@ -364,6 +366,9 @@ class GraphEngine: continue edge = cast(GraphEdge, sub_edge_mappings[0]) + if edge.run_condition is None: + logger.warning(f"Edge {edge.target_node_id} run condition is None") + continue result = ConditionManager.get_condition_handler( init_params=self.init_params, @@ -387,11 +392,11 @@ class GraphEngine: handle_exceptions=handle_exceptions, ) - for item in parallel_generator: - if isinstance(item, str): - final_node_id = item + for parallel_result in parallel_generator: + if isinstance(parallel_result, str): + final_node_id = parallel_result else: - yield item + yield parallel_result break @@ -413,11 +418,11 @@ class GraphEngine: handle_exceptions=handle_exceptions, ) - for item in parallel_generator: - if isinstance(item, str): - final_node_id = item + for generated_item in parallel_generator: + if isinstance(generated_item, str): + final_node_id = generated_item else: - yield item + yield generated_item if not final_node_id: break @@ -653,7 +658,7 @@ class GraphEngine: parallel_start_node_id=parallel_start_node_id, parent_parallel_id=parent_parallel_id, parent_parallel_start_node_id=parent_parallel_start_node_id, - error=run_result.error, + error=run_result.error or "Unknown error", retry_index=retries, start_at=retry_start_at, ) @@ -732,20 +737,20 @@ class GraphEngine: variable_value=variable_value, ) - # add parallel info to run result metadata - if parallel_id and parallel_start_node_id: - if not run_result.metadata: - run_result.metadata = {} + # When setting metadata, convert to dict first + if not run_result.metadata: + run_result.metadata = {} - run_result.metadata[NodeRunMetadataKey.PARALLEL_ID] = parallel_id - run_result.metadata[NodeRunMetadataKey.PARALLEL_START_NODE_ID] = ( - parallel_start_node_id - ) + if parallel_id and parallel_start_node_id: + metadata_dict = dict(run_result.metadata) + metadata_dict[NodeRunMetadataKey.PARALLEL_ID] = parallel_id + metadata_dict[NodeRunMetadataKey.PARALLEL_START_NODE_ID] = parallel_start_node_id if parent_parallel_id and parent_parallel_start_node_id: - run_result.metadata[NodeRunMetadataKey.PARENT_PARALLEL_ID] = parent_parallel_id - run_result.metadata[NodeRunMetadataKey.PARENT_PARALLEL_START_NODE_ID] = ( + metadata_dict[NodeRunMetadataKey.PARENT_PARALLEL_ID] = parent_parallel_id + metadata_dict[NodeRunMetadataKey.PARENT_PARALLEL_START_NODE_ID] = ( parent_parallel_start_node_id ) + run_result.metadata = metadata_dict yield NodeRunSucceededEvent( id=node_instance.id, @@ -869,8 +874,8 @@ class GraphEngine: variable_pool.add([node_instance.node_id, "error_message"], error_result.error) variable_pool.add([node_instance.node_id, "error_type"], error_result.error_type) # add error message to handle_exceptions - handle_exceptions.append(error_result.error) - node_error_args = { + handle_exceptions.append(error_result.error or "") + node_error_args: dict[str, Any] = { "status": WorkflowNodeExecutionStatus.EXCEPTION, "error": error_result.error, "inputs": error_result.inputs, diff --git a/api/core/workflow/nodes/answer/answer_stream_processor.py b/api/core/workflow/nodes/answer/answer_stream_processor.py index ed033e7f28..40213bd151 100644 --- a/api/core/workflow/nodes/answer/answer_stream_processor.py +++ b/api/core/workflow/nodes/answer/answer_stream_processor.py @@ -63,7 +63,7 @@ class AnswerStreamProcessor(StreamProcessor): self._remove_unreachable_nodes(event) # generate stream outputs - yield from self._generate_stream_outputs_when_node_finished(event) + yield from self._generate_stream_outputs_when_node_finished(cast(NodeRunSucceededEvent, event)) else: yield event @@ -130,7 +130,7 @@ class AnswerStreamProcessor(StreamProcessor): node_type=event.node_type, node_data=event.node_data, chunk_content=text, - from_variable_selector=value_selector, + from_variable_selector=list(value_selector), route_node_state=event.route_node_state, parallel_id=event.parallel_id, parallel_start_node_id=event.parallel_start_node_id, diff --git a/api/core/workflow/nodes/answer/base_stream_processor.py b/api/core/workflow/nodes/answer/base_stream_processor.py index d785397e13..8ffb487ec1 100644 --- a/api/core/workflow/nodes/answer/base_stream_processor.py +++ b/api/core/workflow/nodes/answer/base_stream_processor.py @@ -3,7 +3,7 @@ from abc import ABC, abstractmethod from collections.abc import Generator from core.workflow.entities.variable_pool import VariablePool -from core.workflow.graph_engine.entities.event import GraphEngineEvent, NodeRunSucceededEvent +from core.workflow.graph_engine.entities.event import GraphEngineEvent, NodeRunExceptionEvent, NodeRunSucceededEvent from core.workflow.graph_engine.entities.graph import Graph logger = logging.getLogger(__name__) @@ -19,7 +19,7 @@ class StreamProcessor(ABC): def process(self, generator: Generator[GraphEngineEvent, None, None]) -> Generator[GraphEngineEvent, None, None]: raise NotImplementedError - def _remove_unreachable_nodes(self, event: NodeRunSucceededEvent) -> None: + def _remove_unreachable_nodes(self, event: NodeRunSucceededEvent | NodeRunExceptionEvent) -> None: finished_node_id = event.route_node_state.node_id if finished_node_id not in self.rest_node_ids: return @@ -32,8 +32,8 @@ class StreamProcessor(ABC): return if run_result.edge_source_handle: - reachable_node_ids = [] - unreachable_first_node_ids = [] + reachable_node_ids: list[str] = [] + unreachable_first_node_ids: list[str] = [] if finished_node_id not in self.graph.edge_mapping: logger.warning(f"node {finished_node_id} has no edge mapping") return diff --git a/api/core/workflow/nodes/base/entities.py b/api/core/workflow/nodes/base/entities.py index 529fd7be74..6bf8899f5d 100644 --- a/api/core/workflow/nodes/base/entities.py +++ b/api/core/workflow/nodes/base/entities.py @@ -38,7 +38,8 @@ class DefaultValue(BaseModel): @staticmethod def _validate_array(value: Any, element_type: DefaultValueType) -> bool: """Unified array type validation""" - return isinstance(value, list) and all(isinstance(x, element_type) for x in value) + # FIXME, type ignore here for do not find the reason mypy complain, if find the root cause, please fix it + return isinstance(value, list) and all(isinstance(x, element_type) for x in value) # type: ignore @staticmethod def _convert_number(value: str) -> float: @@ -84,7 +85,7 @@ class DefaultValue(BaseModel): }, } - validator = type_validators.get(self.type) + validator: dict[str, Any] = type_validators.get(self.type, {}) if not validator: if self.type == DefaultValueType.ARRAY_FILES: # Handle files type diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index 4e371ca436..2f82bf8c38 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -125,7 +125,7 @@ class CodeNode(BaseNode[CodeNodeData]): if depth > dify_config.CODE_MAX_DEPTH: raise DepthLimitError(f"Depth limit ${dify_config.CODE_MAX_DEPTH} reached, object too deep.") - transformed_result = {} + transformed_result: dict[str, Any] = {} if output_schema is None: # validate output thought instance type for output_name, output_value in result.items(): diff --git a/api/core/workflow/nodes/code/entities.py b/api/core/workflow/nodes/code/entities.py index e78183baf1..a454035888 100644 --- a/api/core/workflow/nodes/code/entities.py +++ b/api/core/workflow/nodes/code/entities.py @@ -14,7 +14,7 @@ class CodeNodeData(BaseNodeData): class Output(BaseModel): type: Literal["string", "number", "object", "array[string]", "array[number]", "array[object]"] - children: Optional[dict[str, "Output"]] = None + children: Optional[dict[str, "CodeNodeData.Output"]] = None class Dependency(BaseModel): name: str diff --git a/api/core/workflow/nodes/document_extractor/node.py b/api/core/workflow/nodes/document_extractor/node.py index 6d82dbe6d7..0b1dc611c5 100644 --- a/api/core/workflow/nodes/document_extractor/node.py +++ b/api/core/workflow/nodes/document_extractor/node.py @@ -4,6 +4,7 @@ import json import logging import os import tempfile +from typing import cast import docx import pandas as pd @@ -159,7 +160,7 @@ def _extract_text_from_yaml(file_content: bytes) -> str: """Extract the content from yaml file""" try: yaml_data = yaml.safe_load_all(file_content.decode("utf-8", "ignore")) - return yaml.dump_all(yaml_data, allow_unicode=True, sort_keys=False) + return cast(str, yaml.dump_all(yaml_data, allow_unicode=True, sort_keys=False)) except (UnicodeDecodeError, yaml.YAMLError) as e: raise TextExtractionError(f"Failed to decode or parse YAML file: {e}") from e @@ -229,9 +230,9 @@ def _download_file_content(file: File) -> bytes: raise FileDownloadError("Missing URL for remote file") response = ssrf_proxy.get(file.remote_url) response.raise_for_status() - return response.content + return cast(bytes, response.content) else: - return file_manager.download(file) + return cast(bytes, file_manager.download(file)) except Exception as e: raise FileDownloadError(f"Error downloading file: {str(e)}") from e diff --git a/api/core/workflow/nodes/end/end_stream_generate_router.py b/api/core/workflow/nodes/end/end_stream_generate_router.py index 0db1ba9f09..b3678a82b7 100644 --- a/api/core/workflow/nodes/end/end_stream_generate_router.py +++ b/api/core/workflow/nodes/end/end_stream_generate_router.py @@ -67,7 +67,7 @@ class EndStreamGeneratorRouter: and node_type == NodeType.LLM.value and variable_selector.value_selector[1] == "text" ): - value_selectors.append(variable_selector.value_selector) + value_selectors.append(list(variable_selector.value_selector)) return value_selectors @@ -119,8 +119,7 @@ class EndStreamGeneratorRouter: current_node_id: str, end_node_id: str, node_id_config_mapping: dict[str, dict], - reverse_edge_mapping: dict[str, list["GraphEdge"]], - # type: ignore[name-defined] + reverse_edge_mapping: dict[str, list["GraphEdge"]], # type: ignore[name-defined] end_dependencies: dict[str, list[str]], ) -> None: """ diff --git a/api/core/workflow/nodes/end/end_stream_processor.py b/api/core/workflow/nodes/end/end_stream_processor.py index 1aecf863ac..a770eb951f 100644 --- a/api/core/workflow/nodes/end/end_stream_processor.py +++ b/api/core/workflow/nodes/end/end_stream_processor.py @@ -23,7 +23,7 @@ class EndStreamProcessor(StreamProcessor): self.route_position[end_node_id] = 0 self.current_stream_chunk_generating_node_ids: dict[str, list[str]] = {} self.has_output = False - self.output_node_ids = set() + self.output_node_ids: set[str] = set() def process(self, generator: Generator[GraphEngineEvent, None, None]) -> Generator[GraphEngineEvent, None, None]: for event in generator: diff --git a/api/core/workflow/nodes/event/event.py b/api/core/workflow/nodes/event/event.py index 137b476551..9fea3fbda3 100644 --- a/api/core/workflow/nodes/event/event.py +++ b/api/core/workflow/nodes/event/event.py @@ -42,6 +42,6 @@ class RunRetryEvent(BaseModel): class SingleStepRetryEvent(NodeRunResult): """Single step retry event""" - status: str = WorkflowNodeExecutionStatus.RETRY.value + status: WorkflowNodeExecutionStatus = WorkflowNodeExecutionStatus.RETRY elapsed_time: float = Field(..., description="elapsed time") diff --git a/api/core/workflow/nodes/http_request/executor.py b/api/core/workflow/nodes/http_request/executor.py index 575db15d36..cdfdc6e6d5 100644 --- a/api/core/workflow/nodes/http_request/executor.py +++ b/api/core/workflow/nodes/http_request/executor.py @@ -107,9 +107,9 @@ class Executor: if not (key := key.strip()): continue - value = value[0].strip() if value else "" + value_str = value[0].strip() if value else "" result.append( - (self.variable_pool.convert_template(key).text, self.variable_pool.convert_template(value).text) + (self.variable_pool.convert_template(key).text, self.variable_pool.convert_template(value_str).text) ) self.params = result @@ -182,9 +182,10 @@ class Executor: self.variable_pool.convert_template(item.key).text: item.file for item in filter(lambda item: item.type == "file", data) } + files: dict[str, Any] = {} files = {k: self.variable_pool.get_file(selector) for k, selector in file_selectors.items()} files = {k: v for k, v in files.items() if v is not None} - files = {k: variable.value for k, variable in files.items()} + files = {k: variable.value for k, variable in files.items() if variable is not None} files = { k: (v.filename, file_manager.download(v), v.mime_type or "application/octet-stream") for k, v in files.items() @@ -258,7 +259,8 @@ class Executor: response = getattr(ssrf_proxy, self.method)(**request_args) except (ssrf_proxy.MaxRetriesExceededError, httpx.RequestError) as e: raise HttpRequestNodeError(str(e)) - return response + # FIXME: fix type ignore, this maybe httpx type issue + return response # type: ignore def invoke(self) -> Response: # assemble headers @@ -300,37 +302,37 @@ class Executor: continue raw += f"{k}: {v}\r\n" - body = "" + body_string = "" if self.files: for k, v in self.files.items(): - body += f"--{boundary}\r\n" - body += f'Content-Disposition: form-data; name="{k}"\r\n\r\n' - body += f"{v[1]}\r\n" - body += f"--{boundary}--\r\n" + body_string += f"--{boundary}\r\n" + body_string += f'Content-Disposition: form-data; name="{k}"\r\n\r\n' + body_string += f"{v[1]}\r\n" + body_string += f"--{boundary}--\r\n" elif self.node_data.body: if self.content: if isinstance(self.content, str): - body = self.content + body_string = self.content elif isinstance(self.content, bytes): - body = self.content.decode("utf-8", errors="replace") + body_string = self.content.decode("utf-8", errors="replace") elif self.data and self.node_data.body.type == "x-www-form-urlencoded": - body = urlencode(self.data) + body_string = urlencode(self.data) elif self.data and self.node_data.body.type == "form-data": for key, value in self.data.items(): - body += f"--{boundary}\r\n" - body += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' - body += f"{value}\r\n" - body += f"--{boundary}--\r\n" + body_string += f"--{boundary}\r\n" + body_string += f'Content-Disposition: form-data; name="{key}"\r\n\r\n' + body_string += f"{value}\r\n" + body_string += f"--{boundary}--\r\n" elif self.json: - body = json.dumps(self.json) + body_string = json.dumps(self.json) elif self.node_data.body.type == "raw-text": if len(self.node_data.body.data) != 1: raise RequestBodyError("raw-text body type should have exactly one item") - body = self.node_data.body.data[0].value - if body: - raw += f"Content-Length: {len(body)}\r\n" + body_string = self.node_data.body.data[0].value + if body_string: + raw += f"Content-Length: {len(body_string)}\r\n" raw += "\r\n" # Empty line between headers and body - raw += body + raw += body_string return raw diff --git a/api/core/workflow/nodes/http_request/node.py b/api/core/workflow/nodes/http_request/node.py index ebed690f6f..861119f26c 100644 --- a/api/core/workflow/nodes/http_request/node.py +++ b/api/core/workflow/nodes/http_request/node.py @@ -1,7 +1,7 @@ import logging import mimetypes from collections.abc import Mapping, Sequence -from typing import Any +from typing import Any, Optional from configs import dify_config from core.file import File, FileTransferMethod @@ -36,7 +36,7 @@ class HttpRequestNode(BaseNode[HttpRequestNodeData]): _node_type = NodeType.HTTP_REQUEST @classmethod - def get_default_config(cls, filters: dict | None = None) -> dict: + def get_default_config(cls, filters: Optional[dict[str, Any]] = None) -> dict: return { "type": "http-request", "config": { @@ -160,8 +160,8 @@ class HttpRequestNode(BaseNode[HttpRequestNodeData]): ) mapping = {} - for selector in selectors: - mapping[node_id + "." + selector.variable] = selector.value_selector + for selector_iter in selectors: + mapping[node_id + "." + selector_iter.variable] = selector_iter.value_selector return mapping diff --git a/api/core/workflow/nodes/iteration/iteration_node.py b/api/core/workflow/nodes/iteration/iteration_node.py index 6a89cbfad6..f1289558ff 100644 --- a/api/core/workflow/nodes/iteration/iteration_node.py +++ b/api/core/workflow/nodes/iteration/iteration_node.py @@ -361,13 +361,16 @@ class IterationNode(BaseNode[IterationNodeData]): metadata = event.route_node_state.node_run_result.metadata if not metadata: metadata = {} - if NodeRunMetadataKey.ITERATION_ID not in metadata: - metadata[NodeRunMetadataKey.ITERATION_ID] = self.node_id - if self.node_data.is_parallel: - metadata[NodeRunMetadataKey.PARALLEL_MODE_RUN_ID] = parallel_mode_run_id - else: - metadata[NodeRunMetadataKey.ITERATION_INDEX] = iter_run_index + metadata = { + **metadata, + NodeRunMetadataKey.ITERATION_ID: self.node_id, + NodeRunMetadataKey.PARALLEL_MODE_RUN_ID + if self.node_data.is_parallel + else NodeRunMetadataKey.ITERATION_INDEX: parallel_mode_run_id + if self.node_data.is_parallel + else iter_run_index, + } event.route_node_state.node_run_result.metadata = metadata return event diff --git a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py index 4f9e415f4b..bfd93c074d 100644 --- a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py +++ b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py @@ -147,6 +147,8 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): planning_strategy=planning_strategy, ) elif node_data.retrieval_mode == DatasetRetrieveConfigEntity.RetrieveStrategy.MULTIPLE.value: + if node_data.multiple_retrieval_config is None: + raise ValueError("multiple_retrieval_config is required") if node_data.multiple_retrieval_config.reranking_mode == "reranking_model": if node_data.multiple_retrieval_config.reranking_model: reranking_model = { @@ -157,6 +159,8 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): reranking_model = None weights = None elif node_data.multiple_retrieval_config.reranking_mode == "weighted_score": + if node_data.multiple_retrieval_config.weights is None: + raise ValueError("weights is required") reranking_model = None vector_setting = node_data.multiple_retrieval_config.weights.vector_setting weights = { @@ -180,7 +184,9 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): available_datasets=available_datasets, query=query, top_k=node_data.multiple_retrieval_config.top_k, - score_threshold=node_data.multiple_retrieval_config.score_threshold, + score_threshold=node_data.multiple_retrieval_config.score_threshold + if node_data.multiple_retrieval_config.score_threshold is not None + else 0.0, reranking_mode=node_data.multiple_retrieval_config.reranking_mode, reranking_model=reranking_model, weights=weights, @@ -205,7 +211,7 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): "content": item.page_content, } retrieval_resource_list.append(source) - document_score_list = {} + document_score_list: dict[str, float] = {} # deal with dify documents if dify_documents: document_score_list = {} @@ -260,7 +266,9 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): retrieval_resource_list.append(source) if retrieval_resource_list: retrieval_resource_list = sorted( - retrieval_resource_list, key=lambda x: x.get("metadata").get("score") or 0.0, reverse=True + retrieval_resource_list, + key=lambda x: x["metadata"]["score"] if x["metadata"].get("score") is not None else 0.0, + reverse=True, ) position = 1 for item in retrieval_resource_list: @@ -295,6 +303,8 @@ class KnowledgeRetrievalNode(BaseNode[KnowledgeRetrievalNodeData]): :param node_data: node data :return: """ + if node_data.single_retrieval_config is None: + raise ValueError("single_retrieval_config is required") model_name = node_data.single_retrieval_config.model.name provider_name = node_data.single_retrieval_config.model.provider diff --git a/api/core/workflow/nodes/list_operator/node.py b/api/core/workflow/nodes/list_operator/node.py index 79066cece4..432c57294e 100644 --- a/api/core/workflow/nodes/list_operator/node.py +++ b/api/core/workflow/nodes/list_operator/node.py @@ -1,5 +1,5 @@ from collections.abc import Callable, Sequence -from typing import Literal, Union +from typing import Any, Literal, Union from core.file import File from core.variables import ArrayFileSegment, ArrayNumberSegment, ArrayStringSegment @@ -17,9 +17,9 @@ class ListOperatorNode(BaseNode[ListOperatorNodeData]): _node_type = NodeType.LIST_OPERATOR def _run(self): - inputs = {} - process_data = {} - outputs = {} + inputs: dict[str, list] = {} + process_data: dict[str, list] = {} + outputs: dict[str, Any] = {} variable = self.graph_runtime_state.variable_pool.get(self.node_data.variable) if variable is None: @@ -93,6 +93,8 @@ class ListOperatorNode(BaseNode[ListOperatorNodeData]): def _apply_filter( self, variable: Union[ArrayFileSegment, ArrayNumberSegment, ArrayStringSegment] ) -> Union[ArrayFileSegment, ArrayNumberSegment, ArrayStringSegment]: + filter_func: Callable[[Any], bool] + result: list[Any] = [] for condition in self.node_data.filter_by.conditions: if isinstance(variable, ArrayStringSegment): if not isinstance(condition.value, str): @@ -236,6 +238,7 @@ def _get_number_filter_func(*, condition: str, value: int | float) -> Callable[[ def _get_file_filter_func(*, key: str, condition: str, value: str | Sequence[str]) -> Callable[[File], bool]: + extract_func: Callable[[File], Any] if key in {"name", "extension", "mime_type", "url"} and isinstance(value, str): extract_func = _get_file_extract_string_func(key=key) return lambda x: _get_string_filter_func(condition=condition, value=value)(extract_func(x)) @@ -249,47 +252,47 @@ def _get_file_filter_func(*, key: str, condition: str, value: str | Sequence[str raise InvalidKeyError(f"Invalid key: {key}") -def _contains(value: str): +def _contains(value: str) -> Callable[[str], bool]: return lambda x: value in x -def _startswith(value: str): +def _startswith(value: str) -> Callable[[str], bool]: return lambda x: x.startswith(value) -def _endswith(value: str): +def _endswith(value: str) -> Callable[[str], bool]: return lambda x: x.endswith(value) -def _is(value: str): +def _is(value: str) -> Callable[[str], bool]: return lambda x: x is value -def _in(value: str | Sequence[str]): +def _in(value: str | Sequence[str]) -> Callable[[str], bool]: return lambda x: x in value -def _eq(value: int | float): +def _eq(value: int | float) -> Callable[[int | float], bool]: return lambda x: x == value -def _ne(value: int | float): +def _ne(value: int | float) -> Callable[[int | float], bool]: return lambda x: x != value -def _lt(value: int | float): +def _lt(value: int | float) -> Callable[[int | float], bool]: return lambda x: x < value -def _le(value: int | float): +def _le(value: int | float) -> Callable[[int | float], bool]: return lambda x: x <= value -def _gt(value: int | float): +def _gt(value: int | float) -> Callable[[int | float], bool]: return lambda x: x > value -def _ge(value: int | float): +def _ge(value: int | float) -> Callable[[int | float], bool]: return lambda x: x >= value @@ -302,6 +305,7 @@ def _order_string(*, order: Literal["asc", "desc"], array: Sequence[str]): def _order_file(*, order: Literal["asc", "desc"], order_by: str = "", array: Sequence[File]): + extract_func: Callable[[File], Any] if order_by in {"name", "type", "extension", "mime_type", "transfer_method", "url"}: extract_func = _get_file_extract_string_func(key=order_by) return sorted(array, key=lambda x: extract_func(x), reverse=order == "desc") diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py index 55fac45576..6909b30c9e 100644 --- a/api/core/workflow/nodes/llm/node.py +++ b/api/core/workflow/nodes/llm/node.py @@ -88,8 +88,8 @@ class LLMNode(BaseNode[LLMNodeData]): _node_data_cls = LLMNodeData _node_type = NodeType.LLM - def _run(self) -> NodeRunResult | Generator[NodeEvent | InNodeEvent, None, None]: - node_inputs = None + def _run(self) -> Generator[NodeEvent | InNodeEvent, None, None]: + node_inputs: Optional[dict[str, Any]] = None process_data = None try: @@ -196,7 +196,6 @@ class LLMNode(BaseNode[LLMNodeData]): error_type=type(e).__name__, ) ) - return except Exception as e: yield RunCompletedEvent( run_result=NodeRunResult( @@ -206,7 +205,6 @@ class LLMNode(BaseNode[LLMNodeData]): process_data=process_data, ) ) - return outputs = {"text": result_text, "usage": jsonable_encoder(usage), "finish_reason": finish_reason} @@ -302,7 +300,7 @@ class LLMNode(BaseNode[LLMNodeData]): return messages def _fetch_jinja_inputs(self, node_data: LLMNodeData) -> dict[str, str]: - variables = {} + variables: dict[str, Any] = {} if not node_data.prompt_config: return variables @@ -319,7 +317,7 @@ class LLMNode(BaseNode[LLMNodeData]): """ # check if it's a context structure if "metadata" in input_dict and "_source" in input_dict["metadata"] and "content" in input_dict: - return input_dict["content"] + return str(input_dict["content"]) # else, parse the dict try: @@ -557,7 +555,8 @@ class LLMNode(BaseNode[LLMNodeData]): variable_pool: VariablePool, jinja2_variables: Sequence[VariableSelector], ) -> tuple[Sequence[PromptMessage], Optional[Sequence[str]]]: - prompt_messages = [] + # FIXME: fix the type error cause prompt_messages is type quick a few times + prompt_messages: list[Any] = [] if isinstance(prompt_template, list): # For chat model @@ -783,7 +782,7 @@ class LLMNode(BaseNode[LLMNodeData]): else: raise InvalidVariableTypeError(f"Invalid prompt template type: {type(prompt_template)}") - variable_mapping = {} + variable_mapping: dict[str, Any] = {} for variable_selector in variable_selectors: variable_mapping[variable_selector.variable] = variable_selector.value_selector @@ -981,7 +980,7 @@ def _handle_memory_chat_mode( memory_config: MemoryConfig | None, model_config: ModelConfigWithCredentialsEntity, ) -> Sequence[PromptMessage]: - memory_messages = [] + memory_messages: Sequence[PromptMessage] = [] # Get messages from memory for chat model if memory and memory_config: rest_tokens = _calculate_rest_token(prompt_messages=[], model_config=model_config) diff --git a/api/core/workflow/nodes/loop/loop_node.py b/api/core/workflow/nodes/loop/loop_node.py index 6fdff96602..a366c287c2 100644 --- a/api/core/workflow/nodes/loop/loop_node.py +++ b/api/core/workflow/nodes/loop/loop_node.py @@ -14,8 +14,8 @@ class LoopNode(BaseNode[LoopNodeData]): _node_data_cls = LoopNodeData _node_type = NodeType.LOOP - def _run(self) -> LoopState: - return super()._run() + def _run(self) -> LoopState: # type: ignore + return super()._run() # type: ignore @classmethod def get_conditions(cls, node_config: dict[str, Any]) -> list[Condition]: @@ -28,7 +28,7 @@ class LoopNode(BaseNode[LoopNodeData]): # TODO waiting for implementation return [ - Condition( + Condition( # type: ignore variable_selector=[node_id, "index"], comparison_operator="≤", value_type="value_selector", diff --git a/api/core/workflow/nodes/parameter_extractor/entities.py b/api/core/workflow/nodes/parameter_extractor/entities.py index a001b44dc7..369eb13b04 100644 --- a/api/core/workflow/nodes/parameter_extractor/entities.py +++ b/api/core/workflow/nodes/parameter_extractor/entities.py @@ -25,7 +25,7 @@ class ParameterConfig(BaseModel): raise ValueError("Parameter name is required") if value in {"__reason", "__is_success"}: raise ValueError("Invalid parameter name, __reason and __is_success are reserved") - return value + return str(value) class ParameterExtractorNodeData(BaseNodeData): @@ -52,7 +52,7 @@ class ParameterExtractorNodeData(BaseNodeData): :return: parameter json schema """ - parameters = {"type": "object", "properties": {}, "required": []} + parameters: dict[str, Any] = {"type": "object", "properties": {}, "required": []} for parameter in self.parameters: parameter_schema: dict[str, Any] = {"description": parameter.description} diff --git a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py index c8c854a43b..9c88047f2c 100644 --- a/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py +++ b/api/core/workflow/nodes/parameter_extractor/parameter_extractor_node.py @@ -63,7 +63,8 @@ class ParameterExtractorNode(LLMNode): Parameter Extractor Node. """ - _node_data_cls = ParameterExtractorNodeData + # FIXME: figure out why here is different from super class + _node_data_cls = ParameterExtractorNodeData # type: ignore _node_type = NodeType.PARAMETER_EXTRACTOR _model_instance: Optional[ModelInstance] = None @@ -253,6 +254,9 @@ class ParameterExtractorNode(LLMNode): # deduct quota self.deduct_llm_quota(tenant_id=self.tenant_id, model_instance=model_instance, usage=usage) + if text is None: + text = "" + return text, usage, tool_call def _generate_function_call_prompt( @@ -605,9 +609,10 @@ class ParameterExtractorNode(LLMNode): json_str = extract_json(result[idx:]) if json_str: try: - return json.loads(json_str) + return cast(dict, json.loads(json_str)) except Exception: pass + return None def _extract_json_from_tool_call(self, tool_call: AssistantPromptMessage.ToolCall) -> Optional[dict]: """ @@ -616,13 +621,13 @@ class ParameterExtractorNode(LLMNode): if not tool_call or not tool_call.function.arguments: return None - return json.loads(tool_call.function.arguments) + return cast(dict, json.loads(tool_call.function.arguments)) def _generate_default_result(self, data: ParameterExtractorNodeData) -> dict: """ Generate default result. """ - result = {} + result: dict[str, Any] = {} for parameter in data.parameters: if parameter.type == "number": result[parameter.name] = 0 @@ -772,7 +777,7 @@ class ParameterExtractorNode(LLMNode): *, graph_config: Mapping[str, Any], node_id: str, - node_data: ParameterExtractorNodeData, + node_data: ParameterExtractorNodeData, # type: ignore ) -> Mapping[str, Sequence[str]]: """ Extract variable selector to variable mapping @@ -781,6 +786,7 @@ class ParameterExtractorNode(LLMNode): :param node_data: node data :return: """ + # FIXME: fix the type error later variable_mapping: dict[str, Sequence[str]] = {"query": node_data.query} if node_data.instruction: diff --git a/api/core/workflow/nodes/parameter_extractor/prompts.py b/api/core/workflow/nodes/parameter_extractor/prompts.py index e603add170..6c3155ac9a 100644 --- a/api/core/workflow/nodes/parameter_extractor/prompts.py +++ b/api/core/workflow/nodes/parameter_extractor/prompts.py @@ -1,3 +1,5 @@ +from typing import Any + FUNCTION_CALLING_EXTRACTOR_NAME = "extract_parameters" FUNCTION_CALLING_EXTRACTOR_SYSTEM_PROMPT = f"""You are a helpful assistant tasked with extracting structured information based on specific criteria provided. Follow the guidelines below to ensure consistency and accuracy. @@ -35,7 +37,7 @@ FUNCTION_CALLING_EXTRACTOR_USER_TEMPLATE = f"""extract structured information fr """ # noqa: E501 -FUNCTION_CALLING_EXTRACTOR_EXAMPLE = [ +FUNCTION_CALLING_EXTRACTOR_EXAMPLE: list[dict[str, Any]] = [ { "user": { "query": "What is the weather today in SF?", diff --git a/api/core/workflow/nodes/question_classifier/question_classifier_node.py b/api/core/workflow/nodes/question_classifier/question_classifier_node.py index 31f8368d59..0ec44eefac 100644 --- a/api/core/workflow/nodes/question_classifier/question_classifier_node.py +++ b/api/core/workflow/nodes/question_classifier/question_classifier_node.py @@ -1,6 +1,6 @@ import json from collections.abc import Mapping, Sequence -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import Any, Optional, cast from core.app.entities.app_invoke_entities import ModelConfigWithCredentialsEntity from core.memory.token_buffer_memory import TokenBufferMemory @@ -34,12 +34,9 @@ from .template_prompts import ( QUESTION_CLASSIFIER_USER_PROMPT_3, ) -if TYPE_CHECKING: - from core.file import File - class QuestionClassifierNode(LLMNode): - _node_data_cls = QuestionClassifierNodeData + _node_data_cls = QuestionClassifierNodeData # type: ignore _node_type = NodeType.QUESTION_CLASSIFIER def _run(self): @@ -61,7 +58,7 @@ class QuestionClassifierNode(LLMNode): node_data.instruction = node_data.instruction or "" node_data.instruction = variable_pool.convert_template(node_data.instruction).text - files: Sequence[File] = ( + files = ( self._fetch_files( selector=node_data.vision.configs.variable_selector, ) @@ -168,7 +165,7 @@ class QuestionClassifierNode(LLMNode): *, graph_config: Mapping[str, Any], node_id: str, - node_data: QuestionClassifierNodeData, + node_data: Any, ) -> Mapping[str, Sequence[str]]: """ Extract variable selector to variable mapping @@ -177,6 +174,7 @@ class QuestionClassifierNode(LLMNode): :param node_data: node data :return: """ + node_data = cast(QuestionClassifierNodeData, node_data) variable_mapping = {"query": node_data.query_variable_selector} variable_selectors = [] if node_data.instruction: diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index 983fa7e623..01d07e4949 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -9,7 +9,6 @@ from core.callback_handler.workflow_tool_callback_handler import DifyWorkflowCal from core.file import File, FileTransferMethod, FileType from core.tools.entities.tool_entities import ToolInvokeMessage, ToolParameter from core.tools.tool_engine import ToolEngine -from core.tools.tool_manager import ToolManager from core.tools.utils.message_transformer import ToolFileMessageTransformer from core.workflow.entities.node_entities import NodeRunMetadataKey, NodeRunResult from core.workflow.entities.variable_pool import VariablePool @@ -46,6 +45,8 @@ class ToolNode(BaseNode[ToolNodeData]): # get tool runtime try: + from core.tools.tool_manager import ToolManager + tool_runtime = ToolManager.get_workflow_tool_runtime( self.tenant_id, self.app_id, self.node_id, self.node_data, self.invoke_from ) @@ -142,7 +143,7 @@ class ToolNode(BaseNode[ToolNodeData]): """ tool_parameters_dictionary = {parameter.name: parameter for parameter in tool_parameters} - result = {} + result: dict[str, Any] = {} for parameter_name in node_data.tool_parameters: parameter = tool_parameters_dictionary.get(parameter_name) if not parameter: @@ -264,9 +265,9 @@ class ToolNode(BaseNode[ToolNodeData]): """ return "\n".join( [ - f"{message.message}" + str(message.message) if message.type == ToolInvokeMessage.MessageType.TEXT - else f"Link: {message.message}" + else f"Link: {str(message.message)}" for message in tool_response if message.type in {ToolInvokeMessage.MessageType.TEXT, ToolInvokeMessage.MessageType.LINK} ] diff --git a/api/core/workflow/nodes/variable_assigner/v1/node.py b/api/core/workflow/nodes/variable_assigner/v1/node.py index 8eb4bd5c2d..9acc76f326 100644 --- a/api/core/workflow/nodes/variable_assigner/v1/node.py +++ b/api/core/workflow/nodes/variable_assigner/v1/node.py @@ -36,6 +36,8 @@ class VariableAssignerNode(BaseNode[VariableAssignerData]): case WriteMode.CLEAR: income_value = get_zero_value(original_variable.value_type) + if income_value is None: + raise VariableOperatorNodeError("income value not found") updated_variable = original_variable.model_copy(update={"value": income_value.to_object()}) case _: diff --git a/api/core/workflow/nodes/variable_assigner/v2/node.py b/api/core/workflow/nodes/variable_assigner/v2/node.py index d73c744202..0c4aae827c 100644 --- a/api/core/workflow/nodes/variable_assigner/v2/node.py +++ b/api/core/workflow/nodes/variable_assigner/v2/node.py @@ -1,5 +1,5 @@ import json -from typing import Any +from typing import Any, cast from core.variables import SegmentType, Variable from core.workflow.constants import CONVERSATION_VARIABLE_NODE_ID @@ -29,7 +29,7 @@ class VariableAssignerNode(BaseNode[VariableAssignerNodeData]): def _run(self) -> NodeRunResult: inputs = self.node_data.model_dump() - process_data = {} + process_data: dict[str, Any] = {} # NOTE: This node has no outputs updated_variables: list[Variable] = [] @@ -119,7 +119,7 @@ class VariableAssignerNode(BaseNode[VariableAssignerNodeData]): else: conversation_id = conversation_id.value common_helpers.update_conversation_variable( - conversation_id=conversation_id, + conversation_id=cast(str, conversation_id), variable=variable, ) diff --git a/api/core/workflow/workflow_entry.py b/api/core/workflow/workflow_entry.py index 811e40c11e..b14c6fafbd 100644 --- a/api/core/workflow/workflow_entry.py +++ b/api/core/workflow/workflow_entry.py @@ -129,11 +129,11 @@ class WorkflowEntry: :return: """ # fetch node info from workflow graph - graph = workflow.graph_dict - if not graph: + workflow_graph = workflow.graph_dict + if not workflow_graph: raise ValueError("workflow graph not found") - nodes = graph.get("nodes") + nodes = workflow_graph.get("nodes") if not nodes: raise ValueError("nodes not found in workflow graph") @@ -196,7 +196,8 @@ class WorkflowEntry: @staticmethod def handle_special_values(value: Optional[Mapping[str, Any]]) -> Mapping[str, Any] | None: - return WorkflowEntry._handle_special_values(value) + result = WorkflowEntry._handle_special_values(value) + return result if isinstance(result, Mapping) or result is None else dict(result) @staticmethod def _handle_special_values(value: Any) -> Any: @@ -208,10 +209,10 @@ class WorkflowEntry: res[k] = WorkflowEntry._handle_special_values(v) return res if isinstance(value, list): - res = [] + res_list = [] for item in value: - res.append(WorkflowEntry._handle_special_values(item)) - return res + res_list.append(WorkflowEntry._handle_special_values(item)) + return res_list if isinstance(value, File): return value.to_dict() return value diff --git a/api/events/event_handlers/create_document_index.py b/api/events/event_handlers/create_document_index.py index 24fa013697..8a677f6b6f 100644 --- a/api/events/event_handlers/create_document_index.py +++ b/api/events/event_handlers/create_document_index.py @@ -14,7 +14,7 @@ from models.dataset import Document @document_index_created.connect def handle(sender, **kwargs): dataset_id = sender - document_ids = kwargs.get("document_ids") + document_ids = kwargs.get("document_ids", []) documents = [] start_at = time.perf_counter() for document_id in document_ids: diff --git a/api/events/event_handlers/create_site_record_when_app_created.py b/api/events/event_handlers/create_site_record_when_app_created.py index 1515661b2d..5e7caf8cbe 100644 --- a/api/events/event_handlers/create_site_record_when_app_created.py +++ b/api/events/event_handlers/create_site_record_when_app_created.py @@ -8,18 +8,19 @@ def handle(sender, **kwargs): """Create site record when an app is created.""" app = sender account = kwargs.get("account") - site = Site( - app_id=app.id, - title=app.name, - icon_type=app.icon_type, - icon=app.icon, - icon_background=app.icon_background, - default_language=account.interface_language, - customize_token_strategy="not_allow", - code=Site.generate_code(16), - created_by=app.created_by, - updated_by=app.updated_by, - ) + if account is not None: + site = Site( + app_id=app.id, + title=app.name, + icon_type=app.icon_type, + icon=app.icon, + icon_background=app.icon_background, + default_language=account.interface_language, + customize_token_strategy="not_allow", + code=Site.generate_code(16), + created_by=app.created_by, + updated_by=app.updated_by, + ) - db.session.add(site) - db.session.commit() + db.session.add(site) + db.session.commit() diff --git a/api/events/event_handlers/deduct_quota_when_message_created.py b/api/events/event_handlers/deduct_quota_when_message_created.py index 843a232096..1ed37efba0 100644 --- a/api/events/event_handlers/deduct_quota_when_message_created.py +++ b/api/events/event_handlers/deduct_quota_when_message_created.py @@ -44,7 +44,7 @@ def handle(sender, **kwargs): else: used_quota = 1 - if used_quota is not None: + if used_quota is not None and system_configuration.current_quota_type is not None: db.session.query(Provider).filter( Provider.tenant_id == application_generate_entity.app_config.tenant_id, Provider.provider_name == model_config.provider, diff --git a/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py b/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py index 9c5955c8c5..f89fae24a5 100644 --- a/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py +++ b/api/events/event_handlers/delete_tool_parameters_cache_when_sync_draft_workflow.py @@ -8,7 +8,10 @@ from events.app_event import app_draft_workflow_was_synced @app_draft_workflow_was_synced.connect def handle(sender, **kwargs): app = sender - for node_data in kwargs.get("synced_draft_workflow").graph_dict.get("nodes", []): + synced_draft_workflow = kwargs.get("synced_draft_workflow") + if synced_draft_workflow is None: + return + for node_data in synced_draft_workflow.graph_dict.get("nodes", []): if node_data.get("data", {}).get("type") == NodeType.TOOL.value: try: tool_entity = ToolEntity(**node_data["data"]) diff --git a/api/events/event_handlers/update_app_dataset_join_when_app_model_config_updated.py b/api/events/event_handlers/update_app_dataset_join_when_app_model_config_updated.py index de7c0f4dfe..408ed31096 100644 --- a/api/events/event_handlers/update_app_dataset_join_when_app_model_config_updated.py +++ b/api/events/event_handlers/update_app_dataset_join_when_app_model_config_updated.py @@ -8,16 +8,18 @@ from models.model import AppModelConfig def handle(sender, **kwargs): app = sender app_model_config = kwargs.get("app_model_config") + if app_model_config is None: + return dataset_ids = get_dataset_ids_from_model_config(app_model_config) app_dataset_joins = db.session.query(AppDatasetJoin).filter(AppDatasetJoin.app_id == app.id).all() - removed_dataset_ids = [] + removed_dataset_ids: set[int] = set() if not app_dataset_joins: added_dataset_ids = dataset_ids else: - old_dataset_ids = set() + old_dataset_ids: set[int] = set() old_dataset_ids.update(app_dataset_join.dataset_id for app_dataset_join in app_dataset_joins) added_dataset_ids = dataset_ids - old_dataset_ids @@ -37,8 +39,8 @@ def handle(sender, **kwargs): db.session.commit() -def get_dataset_ids_from_model_config(app_model_config: AppModelConfig) -> set: - dataset_ids = set() +def get_dataset_ids_from_model_config(app_model_config: AppModelConfig) -> set[int]: + dataset_ids: set[int] = set() if not app_model_config: return dataset_ids diff --git a/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py b/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py index 453395e8d7..7a31c82f6a 100644 --- a/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py +++ b/api/events/event_handlers/update_app_dataset_join_when_app_published_workflow_updated.py @@ -17,11 +17,11 @@ def handle(sender, **kwargs): dataset_ids = get_dataset_ids_from_workflow(published_workflow) app_dataset_joins = db.session.query(AppDatasetJoin).filter(AppDatasetJoin.app_id == app.id).all() - removed_dataset_ids = [] + removed_dataset_ids: set[int] = set() if not app_dataset_joins: added_dataset_ids = dataset_ids else: - old_dataset_ids = set() + old_dataset_ids: set[int] = set() old_dataset_ids.update(app_dataset_join.dataset_id for app_dataset_join in app_dataset_joins) added_dataset_ids = dataset_ids - old_dataset_ids @@ -41,8 +41,8 @@ def handle(sender, **kwargs): db.session.commit() -def get_dataset_ids_from_workflow(published_workflow: Workflow) -> set: - dataset_ids = set() +def get_dataset_ids_from_workflow(published_workflow: Workflow) -> set[int]: + dataset_ids: set[int] = set() graph = published_workflow.graph_dict if not graph: return dataset_ids @@ -60,7 +60,7 @@ def get_dataset_ids_from_workflow(published_workflow: Workflow) -> set: for node in knowledge_retrieval_nodes: try: node_data = KnowledgeRetrievalNodeData(**node.get("data", {})) - dataset_ids.update(node_data.dataset_ids) + dataset_ids.update(int(dataset_id) for dataset_id in node_data.dataset_ids) except Exception as e: continue diff --git a/api/extensions/__init__.py b/api/extensions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/extensions/ext_app_metrics.py b/api/extensions/ext_app_metrics.py index de1cdfeb98..b7d412d68d 100644 --- a/api/extensions/ext_app_metrics.py +++ b/api/extensions/ext_app_metrics.py @@ -54,12 +54,14 @@ def init_app(app: DifyApp): from extensions.ext_database import db engine = db.engine + # TODO: Fix the type error + # FIXME maybe its sqlalchemy issue return { "pid": os.getpid(), - "pool_size": engine.pool.size(), - "checked_in_connections": engine.pool.checkedin(), - "checked_out_connections": engine.pool.checkedout(), - "overflow_connections": engine.pool.overflow(), - "connection_timeout": engine.pool.timeout(), - "recycle_time": db.engine.pool._recycle, + "pool_size": engine.pool.size(), # type: ignore + "checked_in_connections": engine.pool.checkedin(), # type: ignore + "checked_out_connections": engine.pool.checkedout(), # type: ignore + "overflow_connections": engine.pool.overflow(), # type: ignore + "connection_timeout": engine.pool.timeout(), # type: ignore + "recycle_time": db.engine.pool._recycle, # type: ignore } diff --git a/api/extensions/ext_celery.py b/api/extensions/ext_celery.py index 9dbc4b93d4..30f216ff95 100644 --- a/api/extensions/ext_celery.py +++ b/api/extensions/ext_celery.py @@ -1,8 +1,8 @@ from datetime import timedelta import pytz -from celery import Celery, Task -from celery.schedules import crontab +from celery import Celery, Task # type: ignore +from celery.schedules import crontab # type: ignore from configs import dify_config from dify_app import DifyApp @@ -47,7 +47,7 @@ def init_app(app: DifyApp) -> Celery: worker_log_format=dify_config.LOG_FORMAT, worker_task_log_format=dify_config.LOG_FORMAT, worker_hijack_root_logger=False, - timezone=pytz.timezone(dify_config.LOG_TZ), + timezone=pytz.timezone(dify_config.LOG_TZ or "UTC"), ) if dify_config.BROKER_USE_SSL: diff --git a/api/extensions/ext_compress.py b/api/extensions/ext_compress.py index 9c3a663af4..26ff6427be 100644 --- a/api/extensions/ext_compress.py +++ b/api/extensions/ext_compress.py @@ -7,7 +7,7 @@ def is_enabled() -> bool: def init_app(app: DifyApp): - from flask_compress import Compress + from flask_compress import Compress # type: ignore compress = Compress() compress.init_app(app) diff --git a/api/extensions/ext_logging.py b/api/extensions/ext_logging.py index 9fc29b4eb1..e1c459e8c1 100644 --- a/api/extensions/ext_logging.py +++ b/api/extensions/ext_logging.py @@ -11,7 +11,7 @@ from dify_app import DifyApp def init_app(app: DifyApp): - log_handlers = [] + log_handlers: list[logging.Handler] = [] log_file = dify_config.LOG_FILE if log_file: log_dir = os.path.dirname(log_file) @@ -49,7 +49,8 @@ def init_app(app: DifyApp): return datetime.utcfromtimestamp(seconds).astimezone(timezone).timetuple() for handler in logging.root.handlers: - handler.formatter.converter = time_converter + if handler.formatter: + handler.formatter.converter = time_converter def get_request_id(): diff --git a/api/extensions/ext_login.py b/api/extensions/ext_login.py index b295530714..10fb89eb73 100644 --- a/api/extensions/ext_login.py +++ b/api/extensions/ext_login.py @@ -1,6 +1,6 @@ import json -import flask_login +import flask_login # type: ignore from flask import Response, request from flask_login import user_loaded_from_request, user_logged_in from werkzeug.exceptions import Unauthorized diff --git a/api/extensions/ext_mail.py b/api/extensions/ext_mail.py index 468aedd47e..9240ebe7fc 100644 --- a/api/extensions/ext_mail.py +++ b/api/extensions/ext_mail.py @@ -26,7 +26,7 @@ class Mail: match mail_type: case "resend": - import resend + import resend # type: ignore api_key = dify_config.RESEND_API_KEY if not api_key: @@ -48,9 +48,9 @@ class Mail: self._client = SMTPClient( server=dify_config.SMTP_SERVER, port=dify_config.SMTP_PORT, - username=dify_config.SMTP_USERNAME, - password=dify_config.SMTP_PASSWORD, - _from=dify_config.MAIL_DEFAULT_SEND_FROM, + username=dify_config.SMTP_USERNAME or "", + password=dify_config.SMTP_PASSWORD or "", + _from=dify_config.MAIL_DEFAULT_SEND_FROM or "", use_tls=dify_config.SMTP_USE_TLS, opportunistic_tls=dify_config.SMTP_OPPORTUNISTIC_TLS, ) diff --git a/api/extensions/ext_migrate.py b/api/extensions/ext_migrate.py index 6d8f35c30d..5f862181fa 100644 --- a/api/extensions/ext_migrate.py +++ b/api/extensions/ext_migrate.py @@ -2,7 +2,7 @@ from dify_app import DifyApp def init_app(app: DifyApp): - import flask_migrate + import flask_migrate # type: ignore from extensions.ext_database import db diff --git a/api/extensions/ext_proxy_fix.py b/api/extensions/ext_proxy_fix.py index 3b895ac95b..514e065825 100644 --- a/api/extensions/ext_proxy_fix.py +++ b/api/extensions/ext_proxy_fix.py @@ -6,4 +6,4 @@ def init_app(app: DifyApp): if dify_config.RESPECT_XFORWARD_HEADERS_ENABLED: from werkzeug.middleware.proxy_fix import ProxyFix - app.wsgi_app = ProxyFix(app.wsgi_app) + app.wsgi_app = ProxyFix(app.wsgi_app) # type: ignore diff --git a/api/extensions/ext_sentry.py b/api/extensions/ext_sentry.py index 3ec8ae6e1d..3a74aace6a 100644 --- a/api/extensions/ext_sentry.py +++ b/api/extensions/ext_sentry.py @@ -6,7 +6,7 @@ def init_app(app: DifyApp): if dify_config.SENTRY_DSN: import openai import sentry_sdk - from langfuse import parse_error + from langfuse import parse_error # type: ignore from sentry_sdk.integrations.celery import CeleryIntegration from sentry_sdk.integrations.flask import FlaskIntegration from werkzeug.exceptions import HTTPException diff --git a/api/extensions/ext_storage.py b/api/extensions/ext_storage.py index 42422263c4..588bdb2d27 100644 --- a/api/extensions/ext_storage.py +++ b/api/extensions/ext_storage.py @@ -1,6 +1,6 @@ import logging from collections.abc import Callable, Generator -from typing import Union +from typing import Literal, Union, overload from flask import Flask @@ -79,6 +79,12 @@ class Storage: logger.exception(f"Failed to save file {filename}") raise e + @overload + def load(self, filename: str, /, *, stream: Literal[False] = False) -> bytes: ... + + @overload + def load(self, filename: str, /, *, stream: Literal[True]) -> Generator: ... + def load(self, filename: str, /, *, stream: bool = False) -> Union[bytes, Generator]: try: if stream: diff --git a/api/extensions/storage/aliyun_oss_storage.py b/api/extensions/storage/aliyun_oss_storage.py index 58c917dbd3..00bf5d4f93 100644 --- a/api/extensions/storage/aliyun_oss_storage.py +++ b/api/extensions/storage/aliyun_oss_storage.py @@ -1,7 +1,7 @@ import posixpath from collections.abc import Generator -import oss2 as aliyun_s3 +import oss2 as aliyun_s3 # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -33,7 +33,7 @@ class AliyunOssStorage(BaseStorage): def load_once(self, filename: str) -> bytes: obj = self.client.get_object(self.__wrapper_folder_filename(filename)) - data = obj.read() + data: bytes = obj.read() return data def load_stream(self, filename: str) -> Generator: @@ -41,14 +41,14 @@ class AliyunOssStorage(BaseStorage): while chunk := obj.read(4096): yield chunk - def download(self, filename, target_filepath): + def download(self, filename: str, target_filepath): self.client.get_object_to_file(self.__wrapper_folder_filename(filename), target_filepath) - def exists(self, filename): + def exists(self, filename: str): return self.client.object_exists(self.__wrapper_folder_filename(filename)) - def delete(self, filename): + def delete(self, filename: str): self.client.delete_object(self.__wrapper_folder_filename(filename)) - def __wrapper_folder_filename(self, filename) -> str: + def __wrapper_folder_filename(self, filename: str) -> str: return posixpath.join(self.folder, filename) if self.folder else filename diff --git a/api/extensions/storage/aws_s3_storage.py b/api/extensions/storage/aws_s3_storage.py index ce36c2e7de..7b6b2eedd6 100644 --- a/api/extensions/storage/aws_s3_storage.py +++ b/api/extensions/storage/aws_s3_storage.py @@ -1,9 +1,9 @@ import logging from collections.abc import Generator -import boto3 -from botocore.client import Config -from botocore.exceptions import ClientError +import boto3 # type: ignore +from botocore.client import Config # type: ignore +from botocore.exceptions import ClientError # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -53,7 +53,7 @@ class AwsS3Storage(BaseStorage): def load_once(self, filename: str) -> bytes: try: - data = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].read() + data: bytes = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].read() except ClientError as ex: if ex.response["Error"]["Code"] == "NoSuchKey": raise FileNotFoundError("File not found") diff --git a/api/extensions/storage/azure_blob_storage.py b/api/extensions/storage/azure_blob_storage.py index b26caa8671..2f8532f4f8 100644 --- a/api/extensions/storage/azure_blob_storage.py +++ b/api/extensions/storage/azure_blob_storage.py @@ -27,7 +27,7 @@ class AzureBlobStorage(BaseStorage): client = self._sync_client() blob = client.get_container_client(container=self.bucket_name) blob = blob.get_blob_client(blob=filename) - data = blob.download_blob().readall() + data: bytes = blob.download_blob().readall() return data def load_stream(self, filename: str) -> Generator: @@ -63,11 +63,11 @@ class AzureBlobStorage(BaseStorage): sas_token = cache_result.decode("utf-8") else: sas_token = generate_account_sas( - account_name=self.account_name, - account_key=self.account_key, + account_name=self.account_name or "", + account_key=self.account_key or "", resource_types=ResourceTypes(service=True, container=True, object=True), permission=AccountSasPermissions(read=True, write=True, delete=True, list=True, add=True, create=True), expiry=datetime.now(UTC).replace(tzinfo=None) + timedelta(hours=1), ) redis_client.set(cache_key, sas_token, ex=3000) - return BlobServiceClient(account_url=self.account_url, credential=sas_token) + return BlobServiceClient(account_url=self.account_url or "", credential=sas_token) diff --git a/api/extensions/storage/baidu_obs_storage.py b/api/extensions/storage/baidu_obs_storage.py index e0d2140e91..b94efa08be 100644 --- a/api/extensions/storage/baidu_obs_storage.py +++ b/api/extensions/storage/baidu_obs_storage.py @@ -2,9 +2,9 @@ import base64 import hashlib from collections.abc import Generator -from baidubce.auth.bce_credentials import BceCredentials -from baidubce.bce_client_configuration import BceClientConfiguration -from baidubce.services.bos.bos_client import BosClient +from baidubce.auth.bce_credentials import BceCredentials # type: ignore +from baidubce.bce_client_configuration import BceClientConfiguration # type: ignore +from baidubce.services.bos.bos_client import BosClient # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -36,7 +36,8 @@ class BaiduObsStorage(BaseStorage): def load_once(self, filename: str) -> bytes: response = self.client.get_object(bucket_name=self.bucket_name, key=filename) - return response.data.read() + data: bytes = response.data.read() + return data def load_stream(self, filename: str) -> Generator: response = self.client.get_object(bucket_name=self.bucket_name, key=filename).data diff --git a/api/extensions/storage/google_cloud_storage.py b/api/extensions/storage/google_cloud_storage.py index 26b662d2f0..705639f42e 100644 --- a/api/extensions/storage/google_cloud_storage.py +++ b/api/extensions/storage/google_cloud_storage.py @@ -3,7 +3,7 @@ import io import json from collections.abc import Generator -from google.cloud import storage as google_cloud_storage +from google.cloud import storage as google_cloud_storage # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -35,7 +35,7 @@ class GoogleCloudStorage(BaseStorage): def load_once(self, filename: str) -> bytes: bucket = self.client.get_bucket(self.bucket_name) blob = bucket.get_blob(filename) - data = blob.download_as_bytes() + data: bytes = blob.download_as_bytes() return data def load_stream(self, filename: str) -> Generator: diff --git a/api/extensions/storage/huawei_obs_storage.py b/api/extensions/storage/huawei_obs_storage.py index 20be70ef83..07f1d19970 100644 --- a/api/extensions/storage/huawei_obs_storage.py +++ b/api/extensions/storage/huawei_obs_storage.py @@ -1,6 +1,6 @@ from collections.abc import Generator -from obs import ObsClient +from obs import ObsClient # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -23,7 +23,7 @@ class HuaweiObsStorage(BaseStorage): self.client.putObject(bucketName=self.bucket_name, objectKey=filename, content=data) def load_once(self, filename: str) -> bytes: - data = self.client.getObject(bucketName=self.bucket_name, objectKey=filename)["body"].response.read() + data: bytes = self.client.getObject(bucketName=self.bucket_name, objectKey=filename)["body"].response.read() return data def load_stream(self, filename: str) -> Generator: diff --git a/api/extensions/storage/opendal_storage.py b/api/extensions/storage/opendal_storage.py index e671eff059..b78fc94dae 100644 --- a/api/extensions/storage/opendal_storage.py +++ b/api/extensions/storage/opendal_storage.py @@ -3,7 +3,7 @@ import os from collections.abc import Generator from pathlib import Path -import opendal +import opendal # type: ignore[import] from dotenv import dotenv_values from extensions.storage.base_storage import BaseStorage @@ -18,7 +18,7 @@ def _get_opendal_kwargs(*, scheme: str, env_file_path: str = ".env", prefix: str if key.startswith(config_prefix): kwargs[key[len(config_prefix) :].lower()] = value - file_env_vars = dotenv_values(env_file_path) + file_env_vars: dict = dotenv_values(env_file_path) or {} for key, value in file_env_vars.items(): if key.startswith(config_prefix) and key[len(config_prefix) :].lower() not in kwargs and value: kwargs[key[len(config_prefix) :].lower()] = value @@ -48,7 +48,7 @@ class OpenDALStorage(BaseStorage): if not self.exists(filename): raise FileNotFoundError("File not found") - content = self.op.read(path=filename) + content: bytes = self.op.read(path=filename) logger.debug(f"file {filename} loaded") return content @@ -75,7 +75,7 @@ class OpenDALStorage(BaseStorage): # error handler here when opendal python-binding has a exists method, we should use it # more https://github.com/apache/opendal/blob/main/bindings/python/src/operator.rs try: - res = self.op.stat(path=filename).mode.is_file() + res: bool = self.op.stat(path=filename).mode.is_file() logger.debug(f"file {filename} checked") return res except Exception: diff --git a/api/extensions/storage/oracle_oci_storage.py b/api/extensions/storage/oracle_oci_storage.py index b59f83b8de..82829f7fd5 100644 --- a/api/extensions/storage/oracle_oci_storage.py +++ b/api/extensions/storage/oracle_oci_storage.py @@ -1,7 +1,7 @@ from collections.abc import Generator -import boto3 -from botocore.exceptions import ClientError +import boto3 # type: ignore +from botocore.exceptions import ClientError # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -27,7 +27,7 @@ class OracleOCIStorage(BaseStorage): def load_once(self, filename: str) -> bytes: try: - data = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].read() + data: bytes = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].read() except ClientError as ex: if ex.response["Error"]["Code"] == "NoSuchKey": raise FileNotFoundError("File not found") diff --git a/api/extensions/storage/supabase_storage.py b/api/extensions/storage/supabase_storage.py index 9f7c69a9ae..711c3f7211 100644 --- a/api/extensions/storage/supabase_storage.py +++ b/api/extensions/storage/supabase_storage.py @@ -32,7 +32,7 @@ class SupabaseStorage(BaseStorage): self.client.storage.from_(self.bucket_name).upload(filename, data) def load_once(self, filename: str) -> bytes: - content = self.client.storage.from_(self.bucket_name).download(filename) + content: bytes = self.client.storage.from_(self.bucket_name).download(filename) return content def load_stream(self, filename: str) -> Generator: diff --git a/api/extensions/storage/tencent_cos_storage.py b/api/extensions/storage/tencent_cos_storage.py index 13a6c9239c..9cdd3e67f7 100644 --- a/api/extensions/storage/tencent_cos_storage.py +++ b/api/extensions/storage/tencent_cos_storage.py @@ -1,6 +1,6 @@ from collections.abc import Generator -from qcloud_cos import CosConfig, CosS3Client +from qcloud_cos import CosConfig, CosS3Client # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -25,7 +25,7 @@ class TencentCosStorage(BaseStorage): self.client.put_object(Bucket=self.bucket_name, Body=data, Key=filename) def load_once(self, filename: str) -> bytes: - data = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].get_raw_stream().read() + data: bytes = self.client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].get_raw_stream().read() return data def load_stream(self, filename: str) -> Generator: diff --git a/api/extensions/storage/volcengine_tos_storage.py b/api/extensions/storage/volcengine_tos_storage.py index de82be04ea..55fe6545ec 100644 --- a/api/extensions/storage/volcengine_tos_storage.py +++ b/api/extensions/storage/volcengine_tos_storage.py @@ -1,6 +1,6 @@ from collections.abc import Generator -import tos +import tos # type: ignore from configs import dify_config from extensions.storage.base_storage import BaseStorage @@ -24,6 +24,8 @@ class VolcengineTosStorage(BaseStorage): def load_once(self, filename: str) -> bytes: data = self.client.get_object(bucket=self.bucket_name, key=filename).read() + if not isinstance(data, bytes): + raise TypeError("Expected bytes, got {}".format(type(data).__name__)) return data def load_stream(self, filename: str) -> Generator: diff --git a/api/factories/__init__.py b/api/factories/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/factories/file_factory.py b/api/factories/file_factory.py index 13034f5cf5..856cf62e3e 100644 --- a/api/factories/file_factory.py +++ b/api/factories/file_factory.py @@ -64,7 +64,7 @@ def build_from_mapping( if not build_func: raise ValueError(f"Invalid file transfer method: {transfer_method}") - file = build_func( + file: File = build_func( mapping=mapping, tenant_id=tenant_id, transfer_method=transfer_method, @@ -72,7 +72,7 @@ def build_from_mapping( if config and not _is_file_valid_with_config( input_file_type=mapping.get("type", FileType.CUSTOM), - file_extension=file.extension, + file_extension=file.extension or "", file_transfer_method=file.transfer_method, config=config, ): @@ -281,6 +281,7 @@ def _get_file_type_by_extension(extension: str) -> FileType | None: return FileType.AUDIO elif extension in DOCUMENT_EXTENSIONS: return FileType.DOCUMENT + return None def _get_file_type_by_mimetype(mime_type: str) -> FileType | None: diff --git a/api/factories/variable_factory.py b/api/factories/variable_factory.py index 16a578728a..bbca8448ec 100644 --- a/api/factories/variable_factory.py +++ b/api/factories/variable_factory.py @@ -1,5 +1,5 @@ from collections.abc import Mapping, Sequence -from typing import Any +from typing import Any, cast from uuid import uuid4 from configs import dify_config @@ -84,6 +84,8 @@ def _build_variable_from_mapping(*, mapping: Mapping[str, Any], selector: Sequen raise VariableError("missing value type") if (value := mapping.get("value")) is None: raise VariableError("missing value") + # FIXME: using Any here, fix it later + result: Any match value_type: case SegmentType.STRING: result = StringVariable.model_validate(mapping) @@ -109,7 +111,7 @@ def _build_variable_from_mapping(*, mapping: Mapping[str, Any], selector: Sequen raise VariableError(f"variable size {result.size} exceeds limit {dify_config.MAX_VARIABLE_SIZE}") if not result.selector: result = result.model_copy(update={"selector": selector}) - return result + return cast(Variable, result) def build_segment(value: Any, /) -> Segment: @@ -164,10 +166,13 @@ def segment_to_variable( raise UnsupportedSegmentTypeError(f"not supported segment type {segment_type}") variable_class = SEGMENT_TO_VARIABLE_MAP[segment_type] - return variable_class( - id=id, - name=name, - description=description, - value=segment.value, - selector=selector, + return cast( + Variable, + variable_class( + id=id, + name=name, + description=description, + value=segment.value, + selector=selector, + ), ) diff --git a/api/fields/annotation_fields.py b/api/fields/annotation_fields.py index 379dcc6d16..1c58b3a257 100644 --- a/api/fields/annotation_fields.py +++ b/api/fields/annotation_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/api_based_extension_fields.py b/api/fields/api_based_extension_fields.py index a85d4a34db..d40407bfcc 100644 --- a/api/fields/api_based_extension_fields.py +++ b/api/fields/api_based_extension_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/app_fields.py b/api/fields/app_fields.py index abb27fdad1..73800eab85 100644 --- a/api/fields/app_fields.py +++ b/api/fields/app_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.workflow_fields import workflow_partial_fields from libs.helper import AppIconUrlField, TimestampField diff --git a/api/fields/conversation_fields.py b/api/fields/conversation_fields.py index 6a9e347b1e..c54554a6de 100644 --- a/api/fields/conversation_fields.py +++ b/api/fields/conversation_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.member_fields import simple_account_fields from libs.helper import TimestampField diff --git a/api/fields/conversation_variable_fields.py b/api/fields/conversation_variable_fields.py index 983e50e73c..c6385efb5a 100644 --- a/api/fields/conversation_variable_fields.py +++ b/api/fields/conversation_variable_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/data_source_fields.py b/api/fields/data_source_fields.py index 071071376f..608672121e 100644 --- a/api/fields/data_source_fields.py +++ b/api/fields/data_source_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/dataset_fields.py b/api/fields/dataset_fields.py index 533e3a0837..a74e6f54fb 100644 --- a/api/fields/dataset_fields.py +++ b/api/fields/dataset_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/document_fields.py b/api/fields/document_fields.py index a83ec7bc97..2b2ac6243f 100644 --- a/api/fields/document_fields.py +++ b/api/fields/document_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.dataset_fields import dataset_fields from libs.helper import TimestampField diff --git a/api/fields/end_user_fields.py b/api/fields/end_user_fields.py index 99e529f9d1..aefa0b2758 100644 --- a/api/fields/end_user_fields.py +++ b/api/fields/end_user_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore simple_end_user_fields = { "id": fields.String, diff --git a/api/fields/external_dataset_fields.py b/api/fields/external_dataset_fields.py index 2281460fe2..9cc4e14a05 100644 --- a/api/fields/external_dataset_fields.py +++ b/api/fields/external_dataset_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/file_fields.py b/api/fields/file_fields.py index afaacc0568..f896c15f0f 100644 --- a/api/fields/file_fields.py +++ b/api/fields/file_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/hit_testing_fields.py b/api/fields/hit_testing_fields.py index f36e80f8d4..aaafcab8ab 100644 --- a/api/fields/hit_testing_fields.py +++ b/api/fields/hit_testing_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/installed_app_fields.py b/api/fields/installed_app_fields.py index e0b3e340f6..16f265b9bb 100644 --- a/api/fields/installed_app_fields.py +++ b/api/fields/installed_app_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import AppIconUrlField, TimestampField diff --git a/api/fields/member_fields.py b/api/fields/member_fields.py index 1cf8e408d1..0c854c640c 100644 --- a/api/fields/member_fields.py +++ b/api/fields/member_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/message_fields.py b/api/fields/message_fields.py index 5f6e7884a6..0571faab08 100644 --- a/api/fields/message_fields.py +++ b/api/fields/message_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.conversation_fields import message_file_fields from libs.helper import TimestampField diff --git a/api/fields/raws.py b/api/fields/raws.py index 15ec16ab13..493d4b6cce 100644 --- a/api/fields/raws.py +++ b/api/fields/raws.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from core.file import File diff --git a/api/fields/segment_fields.py b/api/fields/segment_fields.py index 2dd4cb45be..4413af3160 100644 --- a/api/fields/segment_fields.py +++ b/api/fields/segment_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from libs.helper import TimestampField diff --git a/api/fields/tag_fields.py b/api/fields/tag_fields.py index 9af4fc57dd..986cd725f7 100644 --- a/api/fields/tag_fields.py +++ b/api/fields/tag_fields.py @@ -1,3 +1,3 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore tag_fields = {"id": fields.String, "name": fields.String, "type": fields.String, "binding_count": fields.String} diff --git a/api/fields/workflow_app_log_fields.py b/api/fields/workflow_app_log_fields.py index a53b546249..c45b33597b 100644 --- a/api/fields/workflow_app_log_fields.py +++ b/api/fields/workflow_app_log_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.end_user_fields import simple_end_user_fields from fields.member_fields import simple_account_fields diff --git a/api/fields/workflow_fields.py b/api/fields/workflow_fields.py index 0d860d6f40..bd093d4063 100644 --- a/api/fields/workflow_fields.py +++ b/api/fields/workflow_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from core.helper import encrypter from core.variables import SecretVariable, SegmentType, Variable diff --git a/api/fields/workflow_run_fields.py b/api/fields/workflow_run_fields.py index 74fdf8bd97..ef59c57ec3 100644 --- a/api/fields/workflow_run_fields.py +++ b/api/fields/workflow_run_fields.py @@ -1,4 +1,4 @@ -from flask_restful import fields +from flask_restful import fields # type: ignore from fields.end_user_fields import simple_end_user_fields from fields.member_fields import simple_account_fields diff --git a/api/libs/external_api.py b/api/libs/external_api.py index 179617ac0a..922d2d9cd3 100644 --- a/api/libs/external_api.py +++ b/api/libs/external_api.py @@ -1,8 +1,9 @@ import re import sys +from typing import Any from flask import current_app, got_request_exception -from flask_restful import Api, http_status_message +from flask_restful import Api, http_status_message # type: ignore from werkzeug.datastructures import Headers from werkzeug.exceptions import HTTPException @@ -84,7 +85,7 @@ class ExternalApi(Api): # record the exception in the logs when we have a server error of status code: 500 if status_code and status_code >= 500: - exc_info = sys.exc_info() + exc_info: Any = sys.exc_info() if exc_info[1] is None: exc_info = None current_app.log_exception(exc_info) @@ -100,7 +101,7 @@ class ExternalApi(Api): resp = self.make_response(data, status_code, headers, fallback_mediatype=fallback_mediatype) elif status_code == 400: if isinstance(data.get("message"), dict): - param_key, param_value = list(data.get("message").items())[0] + param_key, param_value = list(data.get("message", {}).items())[0] data = {"code": "invalid_param", "message": param_value, "params": param_key} else: if "code" not in data: diff --git a/api/libs/gmpy2_pkcs10aep_cipher.py b/api/libs/gmpy2_pkcs10aep_cipher.py index 83f9c74e33..2dae87e171 100644 --- a/api/libs/gmpy2_pkcs10aep_cipher.py +++ b/api/libs/gmpy2_pkcs10aep_cipher.py @@ -23,7 +23,7 @@ from hashlib import sha1 import Crypto.Hash.SHA1 import Crypto.Util.number -import gmpy2 +import gmpy2 # type: ignore from Crypto import Random from Crypto.Signature.pss import MGF1 from Crypto.Util.number import bytes_to_long, ceil_div, long_to_bytes @@ -191,12 +191,12 @@ class PKCS1OAepCipher: # Step 3g one_pos = hLen + db[hLen:].find(b"\x01") lHash1 = db[:hLen] - invalid = bord(y) | int(one_pos < hLen) + invalid = bord(y) | int(one_pos < hLen) # type: ignore hash_compare = strxor(lHash1, lHash) for x in hash_compare: - invalid |= bord(x) + invalid |= bord(x) # type: ignore for x in db[hLen:one_pos]: - invalid |= bord(x) + invalid |= bord(x) # type: ignore if invalid != 0: raise ValueError("Incorrect decryption.") # Step 4 diff --git a/api/libs/helper.py b/api/libs/helper.py index 91b1d1fe17..eaa4efdb71 100644 --- a/api/libs/helper.py +++ b/api/libs/helper.py @@ -13,7 +13,7 @@ from typing import Any, Optional, Union, cast from zoneinfo import available_timezones from flask import Response, stream_with_context -from flask_restful import fields +from flask_restful import fields # type: ignore from configs import dify_config from core.app.features.rate_limiting.rate_limit import RateLimitGenerator @@ -248,13 +248,13 @@ class TokenManager: if token_data_json is None: logging.warning(f"{token_type} token {token} not found with key {key}") return None - token_data = json.loads(token_data_json) + token_data: Optional[dict[str, Any]] = json.loads(token_data_json) return token_data @classmethod def _get_current_token_for_account(cls, account_id: str, token_type: str) -> Optional[str]: key = cls._get_account_token_key(account_id, token_type) - current_token = redis_client.get(key) + current_token: Optional[str] = redis_client.get(key) return current_token @classmethod diff --git a/api/libs/json_in_md_parser.py b/api/libs/json_in_md_parser.py index 267af611f5..9ab53b6294 100644 --- a/api/libs/json_in_md_parser.py +++ b/api/libs/json_in_md_parser.py @@ -10,6 +10,7 @@ def parse_json_markdown(json_string: str) -> dict: ends = ["```", "``", "`", "}"] end_index = -1 start_index = 0 + parsed: dict = {} for s in starts: start_index = json_string.find(s) if start_index != -1: diff --git a/api/libs/login.py b/api/libs/login.py index 0ea191a185..5395534a6d 100644 --- a/api/libs/login.py +++ b/api/libs/login.py @@ -1,8 +1,9 @@ from functools import wraps +from typing import Any from flask import current_app, g, has_request_context, request -from flask_login import user_logged_in -from flask_login.config import EXEMPT_METHODS +from flask_login import user_logged_in # type: ignore +from flask_login.config import EXEMPT_METHODS # type: ignore from werkzeug.exceptions import Unauthorized from werkzeug.local import LocalProxy @@ -12,7 +13,7 @@ from models.account import Account, Tenant, TenantAccountJoin #: A proxy for the current user. If no user is logged in, this will be an #: anonymous user -current_user = LocalProxy(lambda: _get_user()) +current_user: Any = LocalProxy(lambda: _get_user()) def login_required(func): @@ -79,12 +80,12 @@ def login_required(func): # Login admin if account: account.current_tenant = tenant - current_app.login_manager._update_request_context_with_user(account) - user_logged_in.send(current_app._get_current_object(), user=_get_user()) + current_app.login_manager._update_request_context_with_user(account) # type: ignore + user_logged_in.send(current_app._get_current_object(), user=_get_user()) # type: ignore if request.method in EXEMPT_METHODS or dify_config.LOGIN_DISABLED: pass elif not current_user.is_authenticated: - return current_app.login_manager.unauthorized() + return current_app.login_manager.unauthorized() # type: ignore # flask 1.x compatibility # current_app.ensure_sync is only available in Flask >= 2.0 @@ -98,7 +99,7 @@ def login_required(func): def _get_user(): if has_request_context(): if "_login_user" not in g: - current_app.login_manager._load_user() + current_app.login_manager._load_user() # type: ignore return g._login_user diff --git a/api/libs/oauth.py b/api/libs/oauth.py index 6b6919de24..df75b55019 100644 --- a/api/libs/oauth.py +++ b/api/libs/oauth.py @@ -77,9 +77,9 @@ class GitHubOAuth(OAuth): email_response = requests.get(self._EMAIL_INFO_URL, headers=headers) email_info = email_response.json() - primary_email = next((email for email in email_info if email["primary"] == True), None) + primary_email: dict = next((email for email in email_info if email["primary"] == True), {}) - return {**user_info, "email": primary_email["email"]} + return {**user_info, "email": primary_email.get("email", "")} def _transform_user_info(self, raw_info: dict) -> OAuthUserInfo: email = raw_info.get("email") @@ -130,4 +130,4 @@ class GoogleOAuth(OAuth): return response.json() def _transform_user_info(self, raw_info: dict) -> OAuthUserInfo: - return OAuthUserInfo(id=str(raw_info["sub"]), name=None, email=raw_info["email"]) + return OAuthUserInfo(id=str(raw_info["sub"]), name="", email=raw_info["email"]) diff --git a/api/libs/oauth_data_source.py b/api/libs/oauth_data_source.py index 1d39abd8fa..0c872a0066 100644 --- a/api/libs/oauth_data_source.py +++ b/api/libs/oauth_data_source.py @@ -1,8 +1,9 @@ import datetime import urllib.parse +from typing import Any import requests -from flask_login import current_user +from flask_login import current_user # type: ignore from extensions.ext_database import db from models.source import DataSourceOauthBinding @@ -226,7 +227,7 @@ class NotionOAuth(OAuthDataSource): has_more = True while has_more: - data = { + data: dict[str, Any] = { "filter": {"value": "page", "property": "object"}, **({"start_cursor": next_cursor} if next_cursor else {}), } @@ -281,7 +282,7 @@ class NotionOAuth(OAuthDataSource): has_more = True while has_more: - data = { + data: dict[str, Any] = { "filter": {"value": "database", "property": "object"}, **({"start_cursor": next_cursor} if next_cursor else {}), } diff --git a/api/libs/threadings_utils.py b/api/libs/threadings_utils.py index d356def418..e4d63fd314 100644 --- a/api/libs/threadings_utils.py +++ b/api/libs/threadings_utils.py @@ -9,8 +9,8 @@ def apply_gevent_threading_patch(): :return: """ if not dify_config.DEBUG: - from gevent import monkey - from grpc.experimental import gevent as grpc_gevent + from gevent import monkey # type: ignore + from grpc.experimental import gevent as grpc_gevent # type: ignore # gevent monkey.patch_all() diff --git a/api/models/account.py b/api/models/account.py index a8602d10a9..88c96da1a1 100644 --- a/api/models/account.py +++ b/api/models/account.py @@ -1,7 +1,7 @@ import enum import json -from flask_login import UserMixin +from flask_login import UserMixin # type: ignore from sqlalchemy import func from .engine import db @@ -16,7 +16,7 @@ class AccountStatus(enum.StrEnum): CLOSED = "closed" -class Account(UserMixin, db.Model): +class Account(UserMixin, db.Model): # type: ignore[name-defined] __tablename__ = "accounts" __table_args__ = (db.PrimaryKeyConstraint("id", name="account_pkey"), db.Index("account_email_idx", "email")) @@ -43,7 +43,8 @@ class Account(UserMixin, db.Model): @property def current_tenant(self): - return self._current_tenant + # FIXME: fix the type error later, because the type is important maybe cause some bugs + return self._current_tenant # type: ignore @current_tenant.setter def current_tenant(self, value: "Tenant"): @@ -52,7 +53,8 @@ class Account(UserMixin, db.Model): if ta: tenant.current_role = ta.role else: - tenant = None + # FIXME: fix the type error later, because the type is important maybe cause some bugs + tenant = None # type: ignore self._current_tenant = tenant @property @@ -89,7 +91,7 @@ class Account(UserMixin, db.Model): return AccountStatus(status_str) @classmethod - def get_by_openid(cls, provider: str, open_id: str) -> db.Model: + def get_by_openid(cls, provider: str, open_id: str): account_integrate = ( db.session.query(AccountIntegrate) .filter(AccountIntegrate.provider == provider, AccountIntegrate.open_id == open_id) @@ -134,7 +136,7 @@ class TenantAccountRole(enum.StrEnum): @staticmethod def is_valid_role(role: str) -> bool: - return role and role in { + return role in { TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR, @@ -144,15 +146,15 @@ class TenantAccountRole(enum.StrEnum): @staticmethod def is_privileged_role(role: str) -> bool: - return role and role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN} + return role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN} @staticmethod def is_admin_role(role: str) -> bool: - return role and role == TenantAccountRole.ADMIN + return role == TenantAccountRole.ADMIN @staticmethod def is_non_owner_role(role: str) -> bool: - return role and role in { + return role in { TenantAccountRole.ADMIN, TenantAccountRole.EDITOR, TenantAccountRole.NORMAL, @@ -161,11 +163,11 @@ class TenantAccountRole(enum.StrEnum): @staticmethod def is_editing_role(role: str) -> bool: - return role and role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR} + return role in {TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR} @staticmethod def is_dataset_edit_role(role: str) -> bool: - return role and role in { + return role in { TenantAccountRole.OWNER, TenantAccountRole.ADMIN, TenantAccountRole.EDITOR, @@ -173,7 +175,7 @@ class TenantAccountRole(enum.StrEnum): } -class Tenant(db.Model): +class Tenant(db.Model): # type: ignore[name-defined] __tablename__ = "tenants" __table_args__ = (db.PrimaryKeyConstraint("id", name="tenant_pkey"),) @@ -209,7 +211,7 @@ class TenantAccountJoinRole(enum.Enum): DATASET_OPERATOR = "dataset_operator" -class TenantAccountJoin(db.Model): +class TenantAccountJoin(db.Model): # type: ignore[name-defined] __tablename__ = "tenant_account_joins" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tenant_account_join_pkey"), @@ -228,7 +230,7 @@ class TenantAccountJoin(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class AccountIntegrate(db.Model): +class AccountIntegrate(db.Model): # type: ignore[name-defined] __tablename__ = "account_integrates" __table_args__ = ( db.PrimaryKeyConstraint("id", name="account_integrate_pkey"), @@ -245,7 +247,7 @@ class AccountIntegrate(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class InvitationCode(db.Model): +class InvitationCode(db.Model): # type: ignore[name-defined] __tablename__ = "invitation_codes" __table_args__ = ( db.PrimaryKeyConstraint("id", name="invitation_code_pkey"), diff --git a/api/models/api_based_extension.py b/api/models/api_based_extension.py index fbffe7a3b2..6b6d808710 100644 --- a/api/models/api_based_extension.py +++ b/api/models/api_based_extension.py @@ -13,7 +13,7 @@ class APIBasedExtensionPoint(enum.Enum): APP_MODERATION_OUTPUT = "app.moderation.output" -class APIBasedExtension(db.Model): +class APIBasedExtension(db.Model): # type: ignore[name-defined] __tablename__ = "api_based_extensions" __table_args__ = ( db.PrimaryKeyConstraint("id", name="api_based_extension_pkey"), diff --git a/api/models/dataset.py b/api/models/dataset.py index 7279e8d5b3..b9b41dcf47 100644 --- a/api/models/dataset.py +++ b/api/models/dataset.py @@ -9,6 +9,7 @@ import pickle import re import time from json import JSONDecodeError +from typing import Any, cast from sqlalchemy import func from sqlalchemy.dialects.postgresql import JSONB @@ -29,7 +30,7 @@ class DatasetPermissionEnum(enum.StrEnum): PARTIAL_TEAM = "partial_members" -class Dataset(db.Model): +class Dataset(db.Model): # type: ignore[name-defined] __tablename__ = "datasets" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_pkey"), @@ -200,7 +201,7 @@ class Dataset(db.Model): return f"Vector_index_{normalized_dataset_id}_Node" -class DatasetProcessRule(db.Model): +class DatasetProcessRule(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_process_rules" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_process_rule_pkey"), @@ -216,7 +217,7 @@ class DatasetProcessRule(db.Model): MODES = ["automatic", "custom"] PRE_PROCESSING_RULES = ["remove_stopwords", "remove_extra_spaces", "remove_urls_emails"] - AUTOMATIC_RULES = { + AUTOMATIC_RULES: dict[str, Any] = { "pre_processing_rules": [ {"id": "remove_extra_spaces", "enabled": True}, {"id": "remove_urls_emails", "enabled": False}, @@ -242,7 +243,7 @@ class DatasetProcessRule(db.Model): return None -class Document(db.Model): +class Document(db.Model): # type: ignore[name-defined] __tablename__ = "documents" __table_args__ = ( db.PrimaryKeyConstraint("id", name="document_pkey"), @@ -492,7 +493,7 @@ class Document(db.Model): ) -class DocumentSegment(db.Model): +class DocumentSegment(db.Model): # type: ignore[name-defined] __tablename__ = "document_segments" __table_args__ = ( db.PrimaryKeyConstraint("id", name="document_segment_pkey"), @@ -604,7 +605,7 @@ class DocumentSegment(db.Model): return text -class AppDatasetJoin(db.Model): +class AppDatasetJoin(db.Model): # type: ignore[name-defined] __tablename__ = "app_dataset_joins" __table_args__ = ( db.PrimaryKeyConstraint("id", name="app_dataset_join_pkey"), @@ -621,7 +622,7 @@ class AppDatasetJoin(db.Model): return db.session.get(App, self.app_id) -class DatasetQuery(db.Model): +class DatasetQuery(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_queries" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_query_pkey"), @@ -638,7 +639,7 @@ class DatasetQuery(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.current_timestamp()) -class DatasetKeywordTable(db.Model): +class DatasetKeywordTable(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_keyword_tables" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_keyword_table_pkey"), @@ -683,7 +684,7 @@ class DatasetKeywordTable(db.Model): return None -class Embedding(db.Model): +class Embedding(db.Model): # type: ignore[name-defined] __tablename__ = "embeddings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="embedding_pkey"), @@ -704,10 +705,10 @@ class Embedding(db.Model): self.embedding = pickle.dumps(embedding_data, protocol=pickle.HIGHEST_PROTOCOL) def get_embedding(self) -> list[float]: - return pickle.loads(self.embedding) + return cast(list[float], pickle.loads(self.embedding)) -class DatasetCollectionBinding(db.Model): +class DatasetCollectionBinding(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_collection_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_collection_bindings_pkey"), @@ -722,7 +723,7 @@ class DatasetCollectionBinding(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class TidbAuthBinding(db.Model): +class TidbAuthBinding(db.Model): # type: ignore[name-defined] __tablename__ = "tidb_auth_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tidb_auth_bindings_pkey"), @@ -742,7 +743,7 @@ class TidbAuthBinding(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class Whitelist(db.Model): +class Whitelist(db.Model): # type: ignore[name-defined] __tablename__ = "whitelists" __table_args__ = ( db.PrimaryKeyConstraint("id", name="whitelists_pkey"), @@ -754,7 +755,7 @@ class Whitelist(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class DatasetPermission(db.Model): +class DatasetPermission(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_permissions" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_permission_pkey"), @@ -771,7 +772,7 @@ class DatasetPermission(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class ExternalKnowledgeApis(db.Model): +class ExternalKnowledgeApis(db.Model): # type: ignore[name-defined] __tablename__ = "external_knowledge_apis" __table_args__ = ( db.PrimaryKeyConstraint("id", name="external_knowledge_apis_pkey"), @@ -824,7 +825,7 @@ class ExternalKnowledgeApis(db.Model): return dataset_bindings -class ExternalKnowledgeBindings(db.Model): +class ExternalKnowledgeBindings(db.Model): # type: ignore[name-defined] __tablename__ = "external_knowledge_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="external_knowledge_bindings_pkey"), diff --git a/api/models/model.py b/api/models/model.py index 1417298c79..2a593f0829 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -4,11 +4,11 @@ import uuid from collections.abc import Mapping from datetime import datetime from enum import Enum, StrEnum -from typing import TYPE_CHECKING, Any, Literal, Optional +from typing import TYPE_CHECKING, Any, Literal, Optional, cast import sqlalchemy as sa from flask import request -from flask_login import UserMixin +from flask_login import UserMixin # type: ignore from sqlalchemy import Float, func, text from sqlalchemy.orm import Mapped, mapped_column @@ -28,7 +28,7 @@ if TYPE_CHECKING: from .workflow import Workflow -class DifySetup(db.Model): +class DifySetup(db.Model): # type: ignore[name-defined] __tablename__ = "dify_setups" __table_args__ = (db.PrimaryKeyConstraint("version", name="dify_setup_pkey"),) @@ -63,7 +63,7 @@ class IconType(Enum): EMOJI = "emoji" -class App(db.Model): +class App(db.Model): # type: ignore[name-defined] __tablename__ = "apps" __table_args__ = (db.PrimaryKeyConstraint("id", name="app_pkey"), db.Index("app_tenant_id_idx", "tenant_id")) @@ -86,7 +86,7 @@ class App(db.Model): is_public = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) is_universal = db.Column(db.Boolean, nullable=False, server_default=db.text("false")) tracing = db.Column(db.Text, nullable=True) - max_active_requests = db.Column(db.Integer, nullable=True) + max_active_requests: Mapped[Optional[int]] = mapped_column(nullable=True) created_by = db.Column(StringUUID, nullable=True) created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) updated_by = db.Column(StringUUID, nullable=True) @@ -154,7 +154,7 @@ class App(db.Model): if self.mode == AppMode.CHAT.value and self.is_agent: return AppMode.AGENT_CHAT.value - return self.mode + return str(self.mode) @property def deleted_tools(self) -> list: @@ -219,7 +219,7 @@ class App(db.Model): return tags or [] -class AppModelConfig(db.Model): +class AppModelConfig(db.Model): # type: ignore[name-defined] __tablename__ = "app_model_configs" __table_args__ = (db.PrimaryKeyConstraint("id", name="app_model_config_pkey"), db.Index("app_app_id_idx", "app_id")) @@ -322,7 +322,7 @@ class AppModelConfig(db.Model): return json.loads(self.external_data_tools) if self.external_data_tools else [] @property - def user_input_form_list(self) -> dict: + def user_input_form_list(self) -> list[dict]: return json.loads(self.user_input_form) if self.user_input_form else [] @property @@ -344,7 +344,7 @@ class AppModelConfig(db.Model): @property def dataset_configs_dict(self) -> dict: if self.dataset_configs: - dataset_configs = json.loads(self.dataset_configs) + dataset_configs: dict = json.loads(self.dataset_configs) if "retrieval_model" not in dataset_configs: return {"retrieval_model": "single"} else: @@ -466,7 +466,7 @@ class AppModelConfig(db.Model): return new_app_model_config -class RecommendedApp(db.Model): +class RecommendedApp(db.Model): # type: ignore[name-defined] __tablename__ = "recommended_apps" __table_args__ = ( db.PrimaryKeyConstraint("id", name="recommended_app_pkey"), @@ -494,7 +494,7 @@ class RecommendedApp(db.Model): return app -class InstalledApp(db.Model): +class InstalledApp(db.Model): # type: ignore[name-defined] __tablename__ = "installed_apps" __table_args__ = ( db.PrimaryKeyConstraint("id", name="installed_app_pkey"), @@ -523,7 +523,7 @@ class InstalledApp(db.Model): return tenant -class Conversation(db.Model): +class Conversation(db.Model): # type: ignore[name-defined] __tablename__ = "conversations" __table_args__ = ( db.PrimaryKeyConstraint("id", name="conversation_pkey"), @@ -602,6 +602,8 @@ class Conversation(db.Model): @property def model_config(self): model_config = {} + app_model_config: Optional[AppModelConfig] = None + if self.mode == AppMode.ADVANCED_CHAT.value: if self.override_model_configs: override_model_configs = json.loads(self.override_model_configs) @@ -613,6 +615,7 @@ class Conversation(db.Model): if "model" in override_model_configs: app_model_config = AppModelConfig() app_model_config = app_model_config.from_model_config_dict(override_model_configs) + assert app_model_config is not None, "app model config not found" model_config = app_model_config.to_dict() else: model_config["configs"] = override_model_configs @@ -755,7 +758,7 @@ class Conversation(db.Model): return self.override_model_configs is not None -class Message(db.Model): +class Message(db.Model): # type: ignore[name-defined] __tablename__ = "messages" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_pkey"), @@ -995,7 +998,7 @@ class Message(db.Model): if not current_app: raise ValueError(f"App {self.app_id} not found") - files: list[File] = [] + files = [] for message_file in message_files: if message_file.transfer_method == "local_file": if message_file.upload_file_id is None: @@ -1102,7 +1105,7 @@ class Message(db.Model): ) -class MessageFeedback(db.Model): +class MessageFeedback(db.Model): # type: ignore[name-defined] __tablename__ = "message_feedbacks" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_feedback_pkey"), @@ -1129,7 +1132,7 @@ class MessageFeedback(db.Model): return account -class MessageFile(db.Model): +class MessageFile(db.Model): # type: ignore[name-defined] __tablename__ = "message_files" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_file_pkey"), @@ -1170,7 +1173,7 @@ class MessageFile(db.Model): created_at: Mapped[datetime] = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class MessageAnnotation(db.Model): +class MessageAnnotation(db.Model): # type: ignore[name-defined] __tablename__ = "message_annotations" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_annotation_pkey"), @@ -1201,7 +1204,7 @@ class MessageAnnotation(db.Model): return account -class AppAnnotationHitHistory(db.Model): +class AppAnnotationHitHistory(db.Model): # type: ignore[name-defined] __tablename__ = "app_annotation_hit_histories" __table_args__ = ( db.PrimaryKeyConstraint("id", name="app_annotation_hit_histories_pkey"), @@ -1239,7 +1242,7 @@ class AppAnnotationHitHistory(db.Model): return account -class AppAnnotationSetting(db.Model): +class AppAnnotationSetting(db.Model): # type: ignore[name-defined] __tablename__ = "app_annotation_settings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="app_annotation_settings_pkey"), @@ -1287,7 +1290,7 @@ class AppAnnotationSetting(db.Model): return collection_binding_detail -class OperationLog(db.Model): +class OperationLog(db.Model): # type: ignore[name-defined] __tablename__ = "operation_logs" __table_args__ = ( db.PrimaryKeyConstraint("id", name="operation_log_pkey"), @@ -1304,7 +1307,7 @@ class OperationLog(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class EndUser(UserMixin, db.Model): +class EndUser(UserMixin, db.Model): # type: ignore[name-defined] __tablename__ = "end_users" __table_args__ = ( db.PrimaryKeyConstraint("id", name="end_user_pkey"), @@ -1324,7 +1327,7 @@ class EndUser(UserMixin, db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class Site(db.Model): +class Site(db.Model): # type: ignore[name-defined] __tablename__ = "sites" __table_args__ = ( db.PrimaryKeyConstraint("id", name="site_pkey"), @@ -1381,7 +1384,7 @@ class Site(db.Model): return dify_config.APP_WEB_URL or request.url_root.rstrip("/") -class ApiToken(db.Model): +class ApiToken(db.Model): # type: ignore[name-defined] __tablename__ = "api_tokens" __table_args__ = ( db.PrimaryKeyConstraint("id", name="api_token_pkey"), @@ -1408,7 +1411,7 @@ class ApiToken(db.Model): return result -class UploadFile(db.Model): +class UploadFile(db.Model): # type: ignore[name-defined] __tablename__ = "upload_files" __table_args__ = ( db.PrimaryKeyConstraint("id", name="upload_file_pkey"), @@ -1470,7 +1473,7 @@ class UploadFile(db.Model): self.source_url = source_url -class ApiRequest(db.Model): +class ApiRequest(db.Model): # type: ignore[name-defined] __tablename__ = "api_requests" __table_args__ = ( db.PrimaryKeyConstraint("id", name="api_request_pkey"), @@ -1487,7 +1490,7 @@ class ApiRequest(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class MessageChain(db.Model): +class MessageChain(db.Model): # type: ignore[name-defined] __tablename__ = "message_chains" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_chain_pkey"), @@ -1502,7 +1505,7 @@ class MessageChain(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.current_timestamp()) -class MessageAgentThought(db.Model): +class MessageAgentThought(db.Model): # type: ignore[name-defined] __tablename__ = "message_agent_thoughts" __table_args__ = ( db.PrimaryKeyConstraint("id", name="message_agent_thought_pkey"), @@ -1542,7 +1545,7 @@ class MessageAgentThought(db.Model): @property def files(self) -> list: if self.message_files: - return json.loads(self.message_files) + return cast(list[Any], json.loads(self.message_files)) else: return [] @@ -1554,7 +1557,7 @@ class MessageAgentThought(db.Model): def tool_labels(self) -> dict: try: if self.tool_labels_str: - return json.loads(self.tool_labels_str) + return cast(dict, json.loads(self.tool_labels_str)) else: return {} except Exception as e: @@ -1564,7 +1567,7 @@ class MessageAgentThought(db.Model): def tool_meta(self) -> dict: try: if self.tool_meta_str: - return json.loads(self.tool_meta_str) + return cast(dict, json.loads(self.tool_meta_str)) else: return {} except Exception as e: @@ -1612,9 +1615,11 @@ class MessageAgentThought(db.Model): except Exception as e: if self.observation: return dict.fromkeys(tools, self.observation) + else: + return {} -class DatasetRetrieverResource(db.Model): +class DatasetRetrieverResource(db.Model): # type: ignore[name-defined] __tablename__ = "dataset_retriever_resources" __table_args__ = ( db.PrimaryKeyConstraint("id", name="dataset_retriever_resource_pkey"), @@ -1641,7 +1646,7 @@ class DatasetRetrieverResource(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.current_timestamp()) -class Tag(db.Model): +class Tag(db.Model): # type: ignore[name-defined] __tablename__ = "tags" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tag_pkey"), @@ -1659,7 +1664,7 @@ class Tag(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class TagBinding(db.Model): +class TagBinding(db.Model): # type: ignore[name-defined] __tablename__ = "tag_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tag_binding_pkey"), @@ -1675,7 +1680,7 @@ class TagBinding(db.Model): created_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class TraceAppConfig(db.Model): +class TraceAppConfig(db.Model): # type: ignore[name-defined] __tablename__ = "trace_app_config" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tracing_app_config_pkey"), diff --git a/api/models/provider.py b/api/models/provider.py index fdd3e802d7..abe673975c 100644 --- a/api/models/provider.py +++ b/api/models/provider.py @@ -36,7 +36,7 @@ class ProviderQuotaType(Enum): raise ValueError(f"No matching enum found for value '{value}'") -class Provider(db.Model): +class Provider(db.Model): # type: ignore[name-defined] """ Provider model representing the API providers and their configurations. """ @@ -89,7 +89,7 @@ class Provider(db.Model): return self.is_valid and self.token_is_set -class ProviderModel(db.Model): +class ProviderModel(db.Model): # type: ignore[name-defined] """ Provider model representing the API provider_models and their configurations. """ @@ -114,7 +114,7 @@ class ProviderModel(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class TenantDefaultModel(db.Model): +class TenantDefaultModel(db.Model): # type: ignore[name-defined] __tablename__ = "tenant_default_models" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tenant_default_model_pkey"), @@ -130,7 +130,7 @@ class TenantDefaultModel(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class TenantPreferredModelProvider(db.Model): +class TenantPreferredModelProvider(db.Model): # type: ignore[name-defined] __tablename__ = "tenant_preferred_model_providers" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tenant_preferred_model_provider_pkey"), @@ -145,7 +145,7 @@ class TenantPreferredModelProvider(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class ProviderOrder(db.Model): +class ProviderOrder(db.Model): # type: ignore[name-defined] __tablename__ = "provider_orders" __table_args__ = ( db.PrimaryKeyConstraint("id", name="provider_order_pkey"), @@ -170,7 +170,7 @@ class ProviderOrder(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class ProviderModelSetting(db.Model): +class ProviderModelSetting(db.Model): # type: ignore[name-defined] """ Provider model settings for record the model enabled status and load balancing status. """ @@ -192,7 +192,7 @@ class ProviderModelSetting(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class LoadBalancingModelConfig(db.Model): +class LoadBalancingModelConfig(db.Model): # type: ignore[name-defined] """ Configurations for load balancing models. """ diff --git a/api/models/source.py b/api/models/source.py index 114db8e110..881cfaac7d 100644 --- a/api/models/source.py +++ b/api/models/source.py @@ -7,7 +7,7 @@ from .engine import db from .types import StringUUID -class DataSourceOauthBinding(db.Model): +class DataSourceOauthBinding(db.Model): # type: ignore[name-defined] __tablename__ = "data_source_oauth_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="source_binding_pkey"), @@ -25,7 +25,7 @@ class DataSourceOauthBinding(db.Model): disabled = db.Column(db.Boolean, nullable=True, server_default=db.text("false")) -class DataSourceApiKeyAuthBinding(db.Model): +class DataSourceApiKeyAuthBinding(db.Model): # type: ignore[name-defined] __tablename__ = "data_source_api_key_auth_bindings" __table_args__ = ( db.PrimaryKeyConstraint("id", name="data_source_api_key_auth_binding_pkey"), diff --git a/api/models/task.py b/api/models/task.py index 27571e2474..0db1c63229 100644 --- a/api/models/task.py +++ b/api/models/task.py @@ -1,11 +1,11 @@ from datetime import UTC, datetime -from celery import states +from celery import states # type: ignore from .engine import db -class CeleryTask(db.Model): +class CeleryTask(db.Model): # type: ignore[name-defined] """Task result/status.""" __tablename__ = "celery_taskmeta" @@ -29,7 +29,7 @@ class CeleryTask(db.Model): queue = db.Column(db.String(155), nullable=True) -class CeleryTaskSet(db.Model): +class CeleryTaskSet(db.Model): # type: ignore[name-defined] """TaskSet result.""" __tablename__ = "celery_tasksetmeta" diff --git a/api/models/tools.py b/api/models/tools.py index e90ab669c6..4151a2e9f6 100644 --- a/api/models/tools.py +++ b/api/models/tools.py @@ -14,7 +14,7 @@ from .model import Account, App, Tenant from .types import StringUUID -class BuiltinToolProvider(db.Model): +class BuiltinToolProvider(db.Model): # type: ignore[name-defined] """ This table stores the tool provider information for built-in tools for each tenant. """ @@ -41,10 +41,10 @@ class BuiltinToolProvider(db.Model): @property def credentials(self) -> dict: - return json.loads(self.encrypted_credentials) + return dict(json.loads(self.encrypted_credentials)) -class PublishedAppTool(db.Model): +class PublishedAppTool(db.Model): # type: ignore[name-defined] """ The table stores the apps published as a tool for each person. """ @@ -86,7 +86,7 @@ class PublishedAppTool(db.Model): return db.session.query(App).filter(App.id == self.app_id).first() -class ApiToolProvider(db.Model): +class ApiToolProvider(db.Model): # type: ignore[name-defined] """ The table stores the api providers. """ @@ -133,7 +133,7 @@ class ApiToolProvider(db.Model): @property def credentials(self) -> dict: - return json.loads(self.credentials_str) + return dict(json.loads(self.credentials_str)) @property def user(self) -> Account | None: @@ -144,7 +144,7 @@ class ApiToolProvider(db.Model): return db.session.query(Tenant).filter(Tenant.id == self.tenant_id).first() -class ToolLabelBinding(db.Model): +class ToolLabelBinding(db.Model): # type: ignore[name-defined] """ The table stores the labels for tools. """ @@ -164,7 +164,7 @@ class ToolLabelBinding(db.Model): label_name = db.Column(db.String(40), nullable=False) -class WorkflowToolProvider(db.Model): +class WorkflowToolProvider(db.Model): # type: ignore[name-defined] """ The table stores the workflow providers. """ @@ -218,7 +218,7 @@ class WorkflowToolProvider(db.Model): return db.session.query(App).filter(App.id == self.app_id).first() -class ToolModelInvoke(db.Model): +class ToolModelInvoke(db.Model): # type: ignore[name-defined] """ store the invoke logs from tool invoke """ @@ -255,7 +255,7 @@ class ToolModelInvoke(db.Model): updated_at = db.Column(db.DateTime, nullable=False, server_default=func.current_timestamp()) -class ToolConversationVariables(db.Model): +class ToolConversationVariables(db.Model): # type: ignore[name-defined] """ store the conversation variables from tool invoke """ @@ -283,10 +283,10 @@ class ToolConversationVariables(db.Model): @property def variables(self) -> dict: - return json.loads(self.variables_str) + return dict(json.loads(self.variables_str)) -class ToolFile(db.Model): +class ToolFile(db.Model): # type: ignore[name-defined] __tablename__ = "tool_files" __table_args__ = ( db.PrimaryKeyConstraint("id", name="tool_file_pkey"), diff --git a/api/models/web.py b/api/models/web.py index 028a768519..864428fe09 100644 --- a/api/models/web.py +++ b/api/models/web.py @@ -6,7 +6,7 @@ from .model import Message from .types import StringUUID -class SavedMessage(db.Model): +class SavedMessage(db.Model): # type: ignore[name-defined] __tablename__ = "saved_messages" __table_args__ = ( db.PrimaryKeyConstraint("id", name="saved_message_pkey"), @@ -25,7 +25,7 @@ class SavedMessage(db.Model): return db.session.query(Message).filter(Message.id == self.message_id).first() -class PinnedConversation(db.Model): +class PinnedConversation(db.Model): # type: ignore[name-defined] __tablename__ = "pinned_conversations" __table_args__ = ( db.PrimaryKeyConstraint("id", name="pinned_conversation_pkey"), diff --git a/api/models/workflow.py b/api/models/workflow.py index d5be949bf4..880e044d07 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -2,7 +2,7 @@ import json from collections.abc import Mapping, Sequence from datetime import UTC, datetime from enum import Enum, StrEnum -from typing import Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union import sqlalchemy as sa from sqlalchemy import func @@ -20,6 +20,9 @@ from .account import Account from .engine import db from .types import StringUUID +if TYPE_CHECKING: + from models.model import AppMode, Message + class WorkflowType(Enum): """ @@ -56,7 +59,7 @@ class WorkflowType(Enum): return cls.WORKFLOW if app_mode == AppMode.WORKFLOW else cls.CHAT -class Workflow(db.Model): +class Workflow(db.Model): # type: ignore[name-defined] """ Workflow, for `Workflow App` and `Chat App workflow mode`. @@ -182,7 +185,7 @@ class Workflow(db.Model): self._features = value @property - def features_dict(self) -> Mapping[str, Any]: + def features_dict(self) -> dict[str, Any]: return json.loads(self.features) if self.features else {} def user_input_form(self, to_old_structure: bool = False) -> list: @@ -199,7 +202,7 @@ class Workflow(db.Model): return [] # get user_input_form from start node - variables = start_node.get("data", {}).get("variables", []) + variables: list[Any] = start_node.get("data", {}).get("variables", []) if to_old_structure: old_structure_variables = [] @@ -344,7 +347,7 @@ class WorkflowRunStatus(StrEnum): raise ValueError(f"invalid workflow run status value {value}") -class WorkflowRun(db.Model): +class WorkflowRun(db.Model): # type: ignore[name-defined] """ Workflow Run @@ -546,7 +549,7 @@ class WorkflowNodeExecutionStatus(Enum): raise ValueError(f"invalid workflow node execution status value {value}") -class WorkflowNodeExecution(db.Model): +class WorkflowNodeExecution(db.Model): # type: ignore[name-defined] """ Workflow Node Execution @@ -712,7 +715,7 @@ class WorkflowAppLogCreatedFrom(Enum): raise ValueError(f"invalid workflow app log created from value {value}") -class WorkflowAppLog(db.Model): +class WorkflowAppLog(db.Model): # type: ignore[name-defined] """ Workflow App execution log, excluding workflow debugging records. @@ -774,7 +777,7 @@ class WorkflowAppLog(db.Model): return db.session.get(EndUser, self.created_by) if created_by_role == CreatedByRole.END_USER else None -class ConversationVariable(db.Model): +class ConversationVariable(db.Model): # type: ignore[name-defined] __tablename__ = "workflow_conversation_variables" id: Mapped[str] = db.Column(StringUUID, primary_key=True) diff --git a/api/mypy.ini b/api/mypy.ini new file mode 100644 index 0000000000..2c754f9fcd --- /dev/null +++ b/api/mypy.ini @@ -0,0 +1,10 @@ +[mypy] +warn_return_any = True +warn_unused_configs = True +check_untyped_defs = True +exclude = (?x)( + core/tools/provider/builtin/ + | core/model_runtime/model_providers/ + | tests/ + | migrations/ + ) \ No newline at end of file diff --git a/api/poetry.lock b/api/poetry.lock index 35fda9b36f..b42eb22dd4 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -5643,6 +5643,58 @@ files = [ {file = "multitasking-0.0.11.tar.gz", hash = "sha256:4d6bc3cc65f9b2dca72fb5a787850a88dae8f620c2b36ae9b55248e51bcd6026"}, ] +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -6537,6 +6589,21 @@ sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-d test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] xml = ["lxml (>=4.9.2)"] +[[package]] +name = "pandas-stubs" +version = "2.2.3.241126" +description = "Type annotations for pandas" +optional = false +python-versions = ">=3.10" +files = [ + {file = "pandas_stubs-2.2.3.241126-py3-none-any.whl", hash = "sha256:74aa79c167af374fe97068acc90776c0ebec5266a6e5c69fe11e9c2cf51f2267"}, + {file = "pandas_stubs-2.2.3.241126.tar.gz", hash = "sha256:cf819383c6d9ae7d4dabf34cd47e1e45525bb2f312e6ad2939c2c204cb708acd"}, +] + +[package.dependencies] +numpy = ">=1.23.5" +types-pytz = ">=2022.1.1" + [[package]] name = "pathos" version = "0.3.3" @@ -9255,13 +9322,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "sqlparse" -version = "0.5.2" +version = "0.5.3" description = "A non-validating SQL parser." optional = false python-versions = ">=3.8" files = [ - {file = "sqlparse-0.5.2-py3-none-any.whl", hash = "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e"}, - {file = "sqlparse-0.5.2.tar.gz", hash = "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f"}, + {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"}, + {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"}, ] [package.extras] @@ -9847,6 +9914,17 @@ rich = ">=10.11.0" shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" +[[package]] +name = "types-pytz" +version = "2024.2.0.20241003" +description = "Typing stubs for pytz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44"}, + {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, +] + [[package]] name = "types-requests" version = "2.32.0.20241016" @@ -10313,82 +10391,82 @@ ark = ["anyio (>=3.5.0,<5)", "cached-property", "httpx (>=0.23.0,<1)", "pydantic [[package]] name = "watchfiles" -version = "1.0.0" +version = "1.0.3" description = "Simple, modern and high performance file watching and code reload in python." optional = false python-versions = ">=3.9" files = [ - {file = "watchfiles-1.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1d19df28f99d6a81730658fbeb3ade8565ff687f95acb59665f11502b441be5f"}, - {file = "watchfiles-1.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:28babb38cf2da8e170b706c4b84aa7e4528a6fa4f3ee55d7a0866456a1662041"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12ab123135b2f42517f04e720526d41448667ae8249e651385afb5cda31fedc0"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13a4f9ee0cd25682679eea5c14fc629e2eaa79aab74d963bc4e21f43b8ea1877"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e1d9284cc84de7855fcf83472e51d32daf6f6cecd094160192628bc3fee1b78"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ee5edc939f53466b329bbf2e58333a5461e6c7b50c980fa6117439e2c18b42d"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dccfc70480087567720e4e36ec381bba1ed68d7e5f368fe40c93b3b1eba0105"}, - {file = "watchfiles-1.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83a6d33a9eda0af6a7470240d1af487807adc269704fe76a4972dd982d16236"}, - {file = "watchfiles-1.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:905f69aad276639eff3893759a07d44ea99560e67a1cf46ff389cd62f88872a2"}, - {file = "watchfiles-1.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:09551237645d6bff3972592f2aa5424df9290e7a2e15d63c5f47c48cde585935"}, - {file = "watchfiles-1.0.0-cp310-none-win32.whl", hash = "sha256:d2b39aa8edd9e5f56f99a2a2740a251dc58515398e9ed5a4b3e5ff2827060755"}, - {file = "watchfiles-1.0.0-cp310-none-win_amd64.whl", hash = "sha256:2de52b499e1ab037f1a87cb8ebcb04a819bf087b1015a4cf6dcf8af3c2a2613e"}, - {file = "watchfiles-1.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:fbd0ab7a9943bbddb87cbc2bf2f09317e74c77dc55b1f5657f81d04666c25269"}, - {file = "watchfiles-1.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:774ef36b16b7198669ce655d4f75b4c3d370e7f1cbdfb997fb10ee98717e2058"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b4fb98100267e6a5ebaff6aaa5d20aea20240584647470be39fe4823012ac96"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0fc3bf0effa2d8075b70badfdd7fb839d7aa9cea650d17886982840d71fdeabf"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:648e2b6db53eca6ef31245805cd528a16f56fa4cc15aeec97795eaf713c11435"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa13d604fcb9417ae5f2e3de676e66aa97427d888e83662ad205bed35a313176"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:936f362e7ff28311b16f0b97ec51e8f2cc451763a3264640c6ed40fb252d1ee4"}, - {file = "watchfiles-1.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:245fab124b9faf58430da547512d91734858df13f2ddd48ecfa5e493455ffccb"}, - {file = "watchfiles-1.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4ff9c7e84e8b644a8f985c42bcc81457240316f900fc72769aaedec9d088055a"}, - {file = "watchfiles-1.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9c9a8d8fd97defe935ef8dd53d562e68942ad65067cd1c54d6ed8a088b1d931d"}, - {file = "watchfiles-1.0.0-cp311-none-win32.whl", hash = "sha256:a0abf173975eb9dd17bb14c191ee79999e650997cc644562f91df06060610e62"}, - {file = "watchfiles-1.0.0-cp311-none-win_amd64.whl", hash = "sha256:2a825ba4b32c214e3855b536eb1a1f7b006511d8e64b8215aac06eb680642d84"}, - {file = "watchfiles-1.0.0-cp311-none-win_arm64.whl", hash = "sha256:a5a7a06cfc65e34fd0a765a7623c5ba14707a0870703888e51d3d67107589817"}, - {file = "watchfiles-1.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:28fb64b5843d94e2c2483f7b024a1280662a44409bedee8f2f51439767e2d107"}, - {file = "watchfiles-1.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e3750434c83b61abb3163b49c64b04180b85b4dabb29a294513faec57f2ffdb7"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bedf84835069f51c7b026b3ca04e2e747ea8ed0a77c72006172c72d28c9f69fc"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:90004553be36427c3d06ec75b804233f8f816374165d5225b93abd94ba6e7234"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b46e15c34d4e401e976d6949ad3a74d244600d5c4b88c827a3fdf18691a46359"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:487d15927f1b0bd24e7df921913399bb1ab94424c386bea8b267754d698f8f0e"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ff236d7a3f4b0a42f699a22fc374ba526bc55048a70cbb299661158e1bb5e1f"}, - {file = "watchfiles-1.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c01446626574561756067f00b37e6b09c8622b0fc1e9fdbc7cbcea328d4e514"}, - {file = "watchfiles-1.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b551c465a59596f3d08170bd7e1c532c7260dd90ed8135778038e13c5d48aa81"}, - {file = "watchfiles-1.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1ed613ee107269f66c2df631ec0fc8efddacface85314d392a4131abe299f00"}, - {file = "watchfiles-1.0.0-cp312-none-win32.whl", hash = "sha256:5f75cd42e7e2254117cf37ff0e68c5b3f36c14543756b2da621408349bd9ca7c"}, - {file = "watchfiles-1.0.0-cp312-none-win_amd64.whl", hash = "sha256:cf517701a4a872417f4e02a136e929537743461f9ec6cdb8184d9a04f4843545"}, - {file = "watchfiles-1.0.0-cp312-none-win_arm64.whl", hash = "sha256:8a2127cd68950787ee36753e6d401c8ea368f73beaeb8e54df5516a06d1ecd82"}, - {file = "watchfiles-1.0.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:95de85c254f7fe8cbdf104731f7f87f7f73ae229493bebca3722583160e6b152"}, - {file = "watchfiles-1.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:533a7cbfe700e09780bb31c06189e39c65f06c7f447326fee707fd02f9a6e945"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2218e78e2c6c07b1634a550095ac2a429026b2d5cbcd49a594f893f2bb8c936"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9122b8fdadc5b341315d255ab51d04893f417df4e6c1743b0aac8bf34e96e025"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9272fdbc0e9870dac3b505bce1466d386b4d8d6d2bacf405e603108d50446940"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a3b33c3aefe9067ebd87846806cd5fc0b017ab70d628aaff077ab9abf4d06b3"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bc338ce9f8846543d428260fa0f9a716626963148edc937d71055d01d81e1525"}, - {file = "watchfiles-1.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ac778a460ea22d63c7e6fb0bc0f5b16780ff0b128f7f06e57aaec63bd339285"}, - {file = "watchfiles-1.0.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:53ae447f06f8f29f5ab40140f19abdab822387a7c426a369eb42184b021e97eb"}, - {file = "watchfiles-1.0.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1f73c2147a453315d672c1ad907abe6d40324e34a185b51e15624bc793f93cc6"}, - {file = "watchfiles-1.0.0-cp313-none-win32.whl", hash = "sha256:eba98901a2eab909dbd79681190b9049acc650f6111fde1845484a4450761e98"}, - {file = "watchfiles-1.0.0-cp313-none-win_amd64.whl", hash = "sha256:d562a6114ddafb09c33246c6ace7effa71ca4b6a2324a47f4b09b6445ea78941"}, - {file = "watchfiles-1.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3d94fd83ed54266d789f287472269c0def9120a2022674990bd24ad989ebd7a0"}, - {file = "watchfiles-1.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48051d1c504448b2fcda71c5e6e3610ae45de6a0b8f5a43b961f250be4bdf5a8"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29cf884ad4285d23453c702ed03d689f9c0e865e3c85d20846d800d4787de00f"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d3572d4c34c4e9c33d25b3da47d9570d5122f8433b9ac6519dca49c2740d23cd"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c2696611182c85eb0e755b62b456f48debff484b7306b56f05478b843ca8ece"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:550109001920a993a4383b57229c717fa73627d2a4e8fcb7ed33c7f1cddb0c85"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b555a93c15bd2c71081922be746291d776d47521a00703163e5fbe6d2a402399"}, - {file = "watchfiles-1.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:947ccba18a38b85c366dafeac8df2f6176342d5992ca240a9d62588b214d731f"}, - {file = "watchfiles-1.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ffd98a299b0a74d1b704ef0ed959efb753e656a4e0425c14e46ae4c3cbdd2919"}, - {file = "watchfiles-1.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f8c4f3a1210ed099a99e6a710df4ff2f8069411059ffe30fa5f9467ebed1256b"}, - {file = "watchfiles-1.0.0-cp39-none-win32.whl", hash = "sha256:1e176b6b4119b3f369b2b4e003d53a226295ee862c0962e3afd5a1c15680b4e3"}, - {file = "watchfiles-1.0.0-cp39-none-win_amd64.whl", hash = "sha256:2d9c0518fabf4a3f373b0a94bb9e4ea7a1df18dec45e26a4d182aa8918dee855"}, - {file = "watchfiles-1.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f159ac795785cde4899e0afa539f4c723fb5dd336ce5605bc909d34edd00b79b"}, - {file = "watchfiles-1.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c3d258d78341d5d54c0c804a5b7faa66cd30ba50b2756a7161db07ce15363b8d"}, - {file = "watchfiles-1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbd0311588c2de7f9ea5cf3922ccacfd0ec0c1922870a2be503cc7df1ca8be7"}, - {file = "watchfiles-1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a13ac46b545a7d0d50f7641eefe47d1597e7d1783a5d89e09d080e6dff44b0"}, - {file = "watchfiles-1.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2bca898c1dc073912d3db7fa6926cc08be9575add9e84872de2c99c688bac4e"}, - {file = "watchfiles-1.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:06d828fe2adc4ac8a64b875ca908b892a3603d596d43e18f7948f3fef5fc671c"}, - {file = "watchfiles-1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:074c7618cd6c807dc4eaa0982b4a9d3f8051cd0b72793511848fd64630174b17"}, - {file = "watchfiles-1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95dc785bc284552d044e561b8f4fe26d01ab5ca40d35852a6572d542adfeb4bc"}, - {file = "watchfiles-1.0.0.tar.gz", hash = "sha256:37566c844c9ce3b5deb964fe1a23378e575e74b114618d211fbda8f59d7b5dab"}, + {file = "watchfiles-1.0.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:1da46bb1eefb5a37a8fb6fd52ad5d14822d67c498d99bda8754222396164ae42"}, + {file = "watchfiles-1.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2b961b86cd3973f5822826017cad7f5a75795168cb645c3a6b30c349094e02e3"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34e87c7b3464d02af87f1059fedda5484e43b153ef519e4085fe1a03dd94801e"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d9dd2b89a16cf7ab9c1170b5863e68de6bf83db51544875b25a5f05a7269e678"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b4691234d31686dca133c920f94e478b548a8e7c750f28dbbc2e4333e0d3da9"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90b0fe1fcea9bd6e3084b44875e179b4adcc4057a3b81402658d0eb58c98edf8"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b90651b4cf9e158d01faa0833b073e2e37719264bcee3eac49fc3c74e7d304b"}, + {file = "watchfiles-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2e9fe695ff151b42ab06501820f40d01310fbd58ba24da8923ace79cf6d702d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62691f1c0894b001c7cde1195c03b7801aaa794a837bd6eef24da87d1542838d"}, + {file = "watchfiles-1.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:275c1b0e942d335fccb6014d79267d1b9fa45b5ac0639c297f1e856f2f532552"}, + {file = "watchfiles-1.0.3-cp310-cp310-win32.whl", hash = "sha256:06ce08549e49ba69ccc36fc5659a3d0ff4e3a07d542b895b8a9013fcab46c2dc"}, + {file = "watchfiles-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f280b02827adc9d87f764972fbeb701cf5611f80b619c20568e1982a277d6146"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ffe709b1d0bc2e9921257569675674cafb3a5f8af689ab9f3f2b3f88775b960f"}, + {file = "watchfiles-1.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:418c5ce332f74939ff60691e5293e27c206c8164ce2b8ce0d9abf013003fb7fe"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f492d2907263d6d0d52f897a68647195bc093dafed14508a8d6817973586b6b"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48c9f3bc90c556a854f4cab6a79c16974099ccfa3e3e150673d82d47a4bc92c9"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75d3bcfa90454dba8df12adc86b13b6d85fda97d90e708efc036c2760cc6ba44"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5691340f259b8f76b45fb31b98e594d46c36d1dc8285efa7975f7f50230c9093"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e263cc718545b7f897baeac1f00299ab6fabe3e18caaacacb0edf6d5f35513c"}, + {file = "watchfiles-1.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c6cf7709ed3e55704cc06f6e835bf43c03bc8e3cb8ff946bf69a2e0a78d9d77"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:703aa5e50e465be901e0e0f9d5739add15e696d8c26c53bc6fc00eb65d7b9469"}, + {file = "watchfiles-1.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bfcae6aecd9e0cb425f5145afee871465b98b75862e038d42fe91fd753ddd780"}, + {file = "watchfiles-1.0.3-cp311-cp311-win32.whl", hash = "sha256:6a76494d2c5311584f22416c5a87c1e2cb954ff9b5f0988027bc4ef2a8a67181"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:cf745cbfad6389c0e331786e5fe9ae3f06e9d9c2ce2432378e1267954793975c"}, + {file = "watchfiles-1.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:2dcc3f60c445f8ce14156854a072ceb36b83807ed803d37fdea2a50e898635d6"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:93436ed550e429da007fbafb723e0769f25bae178fbb287a94cb4ccdf42d3af3"}, + {file = "watchfiles-1.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c18f3502ad0737813c7dad70e3e1cc966cc147fbaeef47a09463bbffe70b0a00"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5bc3ca468bb58a2ef50441f953e1f77b9a61bd1b8c347c8223403dc9b4ac9a"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0d1ec043f02ca04bf21b1b32cab155ce90c651aaf5540db8eb8ad7f7e645cba8"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f58d3bfafecf3d81c15d99fc0ecf4319e80ac712c77cf0ce2661c8cf8bf84066"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1df924ba82ae9e77340101c28d56cbaff2c991bd6fe8444a545d24075abb0a87"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:632a52dcaee44792d0965c17bdfe5dc0edad5b86d6a29e53d6ad4bf92dc0ff49"}, + {file = "watchfiles-1.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bf4b459d94a0387617a1b499f314aa04d8a64b7a0747d15d425b8c8b151da0"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ca94c85911601b097d53caeeec30201736ad69a93f30d15672b967558df02885"}, + {file = "watchfiles-1.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65ab1fb635476f6170b07e8e21db0424de94877e4b76b7feabfe11f9a5fc12b5"}, + {file = "watchfiles-1.0.3-cp312-cp312-win32.whl", hash = "sha256:49bc1bc26abf4f32e132652f4b3bfeec77d8f8f62f57652703ef127e85a3e38d"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:48681c86f2cb08348631fed788a116c89c787fdf1e6381c5febafd782f6c3b44"}, + {file = "watchfiles-1.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:9e080cf917b35b20c889225a13f290f2716748362f6071b859b60b8847a6aa43"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e153a690b7255c5ced17895394b4f109d5dcc2a4f35cb809374da50f0e5c456a"}, + {file = "watchfiles-1.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ac1be85fe43b4bf9a251978ce5c3bb30e1ada9784290441f5423a28633a958a7"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a2ec98e31e1844eac860e70d9247db9d75440fc8f5f679c37d01914568d18721"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0179252846be03fa97d4d5f8233d1c620ef004855f0717712ae1c558f1974a16"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:995c374e86fa82126c03c5b4630c4e312327ecfe27761accb25b5e1d7ab50ec8"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29b9cb35b7f290db1c31fb2fdf8fc6d3730cfa4bca4b49761083307f441cac5a"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f8dc09ae69af50bead60783180f656ad96bd33ffbf6e7a6fce900f6d53b08f1"}, + {file = "watchfiles-1.0.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:489b80812f52a8d8c7b0d10f0d956db0efed25df2821c7a934f6143f76938bd6"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:228e2247de583475d4cebf6b9af5dc9918abb99d1ef5ee737155bb39fb33f3c0"}, + {file = "watchfiles-1.0.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1550be1a5cb3be08a3fb84636eaafa9b7119b70c71b0bed48726fd1d5aa9b868"}, + {file = "watchfiles-1.0.3-cp313-cp313-win32.whl", hash = "sha256:16db2d7e12f94818cbf16d4c8938e4d8aaecee23826344addfaaa671a1527b07"}, + {file = "watchfiles-1.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:160eff7d1267d7b025e983ca8460e8cc67b328284967cbe29c05f3c3163711a3"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c05b021f7b5aa333124f2a64d56e4cb9963b6efdf44e8d819152237bbd93ba15"}, + {file = "watchfiles-1.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:310505ad305e30cb6c5f55945858cdbe0eb297fc57378f29bacceb534ac34199"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddff3f8b9fa24a60527c137c852d0d9a7da2a02cf2151650029fdc97c852c974"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46e86ed457c3486080a72bc837300dd200e18d08183f12b6ca63475ab64ed651"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f79fe7993e230a12172ce7d7c7db061f046f672f2b946431c81aff8f60b2758b"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea2b51c5f38bad812da2ec0cd7eec09d25f521a8b6b6843cbccedd9a1d8a5c15"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fe4e740ea94978b2b2ab308cbf9270a246bcbb44401f77cc8740348cbaeac3d"}, + {file = "watchfiles-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9af037d3df7188ae21dc1c7624501f2f90d81be6550904e07869d8d0e6766655"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52bb50a4c4ca2a689fdba84ba8ecc6a4e6210f03b6af93181bb61c4ec3abaf86"}, + {file = "watchfiles-1.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c14a07bdb475eb696f85c715dbd0f037918ccbb5248290448488a0b4ef201aad"}, + {file = "watchfiles-1.0.3-cp39-cp39-win32.whl", hash = "sha256:be37f9b1f8934cd9e7eccfcb5612af9fb728fecbe16248b082b709a9d1b348bf"}, + {file = "watchfiles-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ef9ec8068cf23458dbf36a08e0c16f0a2df04b42a8827619646637be1769300a"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:84fac88278f42d61c519a6c75fb5296fd56710b05bbdcc74bdf85db409a03780"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c68be72b1666d93b266714f2d4092d78dc53bd11cf91ed5a3c16527587a52e29"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889a37e2acf43c377b5124166bece139b4c731b61492ab22e64d371cce0e6e80"}, + {file = "watchfiles-1.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ca05cacf2e5c4a97d02a2878a24020daca21dbb8823b023b978210a75c79098"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8af4b582d5fc1b8465d1d2483e5e7b880cc1a4e99f6ff65c23d64d070867ac58"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:127de3883bdb29dbd3b21f63126bb8fa6e773b74eaef46521025a9ce390e1073"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713f67132346bdcb4c12df185c30cf04bdf4bf6ea3acbc3ace0912cab6b7cb8c"}, + {file = "watchfiles-1.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abd85de513eb83f5ec153a802348e7a5baa4588b818043848247e3e8986094e8"}, + {file = "watchfiles-1.0.3.tar.gz", hash = "sha256:f3ff7da165c99a5412fe5dd2304dd2dbaaaa5da718aad942dcb3a178eaa70c56"}, ] [package.dependencies] @@ -11095,4 +11173,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.13" -content-hash = "14476bf95504a4df4b8d5a5c6608c6aa3dae7499d27d1e41ef39d761cc7c693d" +content-hash = "f4accd01805cbf080c4c5295f97a06c8e4faec7365d2c43d0435e56b46461732" diff --git a/api/pyproject.toml b/api/pyproject.toml index da9eabecf5..28e0305406 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -60,6 +60,7 @@ oci = "~2.135.1" openai = "~1.52.0" openpyxl = "~3.1.5" pandas = { version = "~2.2.2", extras = ["performance", "excel"] } +pandas-stubs = "~2.2.3.241009" psycopg2-binary = "~2.9.6" pycryptodome = "3.19.1" pydantic = "~2.9.2" @@ -84,6 +85,7 @@ tencentcloud-sdk-python-hunyuan = "~3.0.1158" tiktoken = "~0.8.0" tokenizers = "~0.15.0" transformers = "~4.35.0" +types-pytz = "~2024.2.0.20241003" unstructured = { version = "~0.16.1", extras = ["docx", "epub", "md", "msg", "ppt", "pptx"] } validators = "0.21.0" volcengine-python-sdk = {extras = ["ark"], version = "~1.0.98"} @@ -173,6 +175,7 @@ optional = true [tool.poetry.group.dev.dependencies] coverage = "~7.2.4" faker = "~32.1.0" +mypy = "~1.13.0" pytest = "~8.3.2" pytest-benchmark = "~4.0.0" pytest-env = "~1.1.3" diff --git a/api/schedule/clean_messages.py b/api/schedule/clean_messages.py index 97e5c77e95..48bdc872f4 100644 --- a/api/schedule/clean_messages.py +++ b/api/schedule/clean_messages.py @@ -32,8 +32,9 @@ def clean_messages(): while True: try: # Main query with join and filter + # FIXME:for mypy no paginate method error messages = ( - db.session.query(Message) + db.session.query(Message) # type: ignore .filter(Message.created_at < plan_sandbox_clean_message_day) .order_by(Message.created_at.desc()) .limit(100) diff --git a/api/schedule/clean_unused_datasets_task.py b/api/schedule/clean_unused_datasets_task.py index e12be649e4..f66b3c4797 100644 --- a/api/schedule/clean_unused_datasets_task.py +++ b/api/schedule/clean_unused_datasets_task.py @@ -52,8 +52,7 @@ def clean_unused_datasets_task(): # Main query with join and filter datasets = ( - db.session.query(Dataset) - .outerjoin(document_subquery_new, Dataset.id == document_subquery_new.c.dataset_id) + Dataset.query.outerjoin(document_subquery_new, Dataset.id == document_subquery_new.c.dataset_id) .outerjoin(document_subquery_old, Dataset.id == document_subquery_old.c.dataset_id) .filter( Dataset.created_at < plan_sandbox_clean_day, @@ -120,8 +119,7 @@ def clean_unused_datasets_task(): # Main query with join and filter datasets = ( - db.session.query(Dataset) - .outerjoin(document_subquery_new, Dataset.id == document_subquery_new.c.dataset_id) + Dataset.query.outerjoin(document_subquery_new, Dataset.id == document_subquery_new.c.dataset_id) .outerjoin(document_subquery_old, Dataset.id == document_subquery_old.c.dataset_id) .filter( Dataset.created_at < plan_pro_clean_day, diff --git a/api/schedule/create_tidb_serverless_task.py b/api/schedule/create_tidb_serverless_task.py index a20b500308..1c985461c6 100644 --- a/api/schedule/create_tidb_serverless_task.py +++ b/api/schedule/create_tidb_serverless_task.py @@ -36,14 +36,15 @@ def create_tidb_serverless_task(): def create_clusters(batch_size): try: + # TODO: maybe we can set the default value for the following parameters in the config file new_clusters = TidbService.batch_create_tidb_serverless_cluster( - batch_size, - dify_config.TIDB_PROJECT_ID, - dify_config.TIDB_API_URL, - dify_config.TIDB_IAM_API_URL, - dify_config.TIDB_PUBLIC_KEY, - dify_config.TIDB_PRIVATE_KEY, - dify_config.TIDB_REGION, + batch_size=batch_size, + project_id=dify_config.TIDB_PROJECT_ID or "", + api_url=dify_config.TIDB_API_URL or "", + iam_url=dify_config.TIDB_IAM_API_URL or "", + public_key=dify_config.TIDB_PUBLIC_KEY or "", + private_key=dify_config.TIDB_PRIVATE_KEY or "", + region=dify_config.TIDB_REGION or "", ) for new_cluster in new_clusters: tidb_auth_binding = TidbAuthBinding( diff --git a/api/schedule/update_tidb_serverless_status_task.py b/api/schedule/update_tidb_serverless_status_task.py index b2d8746f9c..11a39e60ee 100644 --- a/api/schedule/update_tidb_serverless_status_task.py +++ b/api/schedule/update_tidb_serverless_status_task.py @@ -36,13 +36,14 @@ def update_clusters(tidb_serverless_list: list[TidbAuthBinding]): # batch 20 for i in range(0, len(tidb_serverless_list), 20): items = tidb_serverless_list[i : i + 20] + # TODO: maybe we can set the default value for the following parameters in the config file TidbService.batch_update_tidb_serverless_cluster_status( - items, - dify_config.TIDB_PROJECT_ID, - dify_config.TIDB_API_URL, - dify_config.TIDB_IAM_API_URL, - dify_config.TIDB_PUBLIC_KEY, - dify_config.TIDB_PRIVATE_KEY, + tidb_serverless_list=items, + project_id=dify_config.TIDB_PROJECT_ID or "", + api_url=dify_config.TIDB_API_URL or "", + iam_url=dify_config.TIDB_IAM_API_URL or "", + public_key=dify_config.TIDB_PUBLIC_KEY or "", + private_key=dify_config.TIDB_PRIVATE_KEY or "", ) except Exception as e: click.echo(click.style(f"Error: {e}", fg="red")) diff --git a/api/services/account_service.py b/api/services/account_service.py index 22b54a3ab8..91075ec46b 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -6,7 +6,7 @@ import secrets import uuid from datetime import UTC, datetime, timedelta from hashlib import sha256 -from typing import Any, Optional +from typing import Any, Optional, cast from pydantic import BaseModel from sqlalchemy import func @@ -119,7 +119,7 @@ class AccountService: account.last_active_at = datetime.now(UTC).replace(tzinfo=None) db.session.commit() - return account + return cast(Account, account) @staticmethod def get_account_jwt_token(account: Account) -> str: @@ -132,7 +132,7 @@ class AccountService: "sub": "Console API Passport", } - token = PassportService().issue(payload) + token: str = PassportService().issue(payload) return token @staticmethod @@ -164,7 +164,7 @@ class AccountService: db.session.commit() - return account + return cast(Account, account) @staticmethod def update_account_password(account, password, new_password): @@ -347,6 +347,8 @@ class AccountService: language: Optional[str] = "en-US", ): account_email = account.email if account else email + if account_email is None: + raise ValueError("Email must be provided.") if cls.reset_password_rate_limiter.is_rate_limited(account_email): from controllers.console.auth.error import PasswordResetRateLimitExceededError @@ -377,6 +379,8 @@ class AccountService: def send_email_code_login_email( cls, account: Optional[Account] = None, email: Optional[str] = None, language: Optional[str] = "en-US" ): + if email is None: + raise ValueError("Email must be provided.") if cls.email_code_login_rate_limiter.is_rate_limited(email): from controllers.console.auth.error import EmailCodeLoginRateLimitExceededError @@ -669,7 +673,7 @@ class TenantService: @staticmethod def get_tenant_count() -> int: """Get tenant count""" - return db.session.query(func.count(Tenant.id)).scalar() + return cast(int, db.session.query(func.count(Tenant.id)).scalar()) @staticmethod def check_member_permission(tenant: Tenant, operator: Account, member: Account | None, action: str) -> None: @@ -733,10 +737,10 @@ class TenantService: db.session.commit() @staticmethod - def get_custom_config(tenant_id: str) -> None: - tenant = db.session.query(Tenant).filter(Tenant.id == tenant_id).one_or_404() + def get_custom_config(tenant_id: str) -> dict: + tenant = Tenant.query.filter(Tenant.id == tenant_id).one_or_404() - return tenant.custom_config_dict + return cast(dict, tenant.custom_config_dict) class RegisterService: @@ -807,7 +811,7 @@ class RegisterService: account.status = AccountStatus.ACTIVE.value if not status else status.value account.initialized_at = datetime.now(UTC).replace(tzinfo=None) - if open_id is not None or provider is not None: + if open_id is not None and provider is not None: AccountService.link_account_integrate(provider, open_id, account) if FeatureService.get_system_features().is_allow_create_workspace: @@ -828,10 +832,11 @@ class RegisterService: @classmethod def invite_new_member( - cls, tenant: Tenant, email: str, language: str, role: str = "normal", inviter: Account = None + cls, tenant: Tenant, email: str, language: str, role: str = "normal", inviter: Optional[Account] = None ) -> str: """Invite new member""" account = Account.query.filter_by(email=email).first() + assert inviter is not None, "Inviter must be provided." if not account: TenantService.check_member_permission(tenant, inviter, None, "add") @@ -894,7 +899,9 @@ class RegisterService: redis_client.delete(cls._get_invitation_token_key(token)) @classmethod - def get_invitation_if_token_valid(cls, workspace_id: str, email: str, token: str) -> Optional[dict[str, Any]]: + def get_invitation_if_token_valid( + cls, workspace_id: Optional[str], email: str, token: str + ) -> Optional[dict[str, Any]]: invitation_data = cls._get_invitation_by_token(token, workspace_id, email) if not invitation_data: return None @@ -953,7 +960,7 @@ class RegisterService: if not data: return None - invitation = json.loads(data) + invitation: dict = json.loads(data) return invitation diff --git a/api/services/advanced_prompt_template_service.py b/api/services/advanced_prompt_template_service.py index d2cd7bea67..6dc1affa11 100644 --- a/api/services/advanced_prompt_template_service.py +++ b/api/services/advanced_prompt_template_service.py @@ -48,6 +48,8 @@ class AdvancedPromptTemplateService: return cls.get_chat_prompt( copy.deepcopy(COMPLETION_APP_CHAT_PROMPT_CONFIG), has_context, context_prompt ) + # default return empty dict + return {} @classmethod def get_completion_prompt(cls, prompt_template: dict, has_context: str, context: str) -> dict: @@ -91,3 +93,5 @@ class AdvancedPromptTemplateService: return cls.get_chat_prompt( copy.deepcopy(BAICHUAN_COMPLETION_APP_CHAT_PROMPT_CONFIG), has_context, baichuan_context_prompt ) + # default return empty dict + return {} diff --git a/api/services/agent_service.py b/api/services/agent_service.py index c8819535f1..b02f762ad2 100644 --- a/api/services/agent_service.py +++ b/api/services/agent_service.py @@ -1,5 +1,7 @@ +from typing import Optional + import pytz -from flask_login import current_user +from flask_login import current_user # type: ignore from core.app.app_config.easy_ui_based_app.agent.manager import AgentConfigManager from core.tools.tool_manager import ToolManager @@ -14,7 +16,7 @@ class AgentService: """ Service to get agent logs """ - conversation: Conversation = ( + conversation: Optional[Conversation] = ( db.session.query(Conversation) .filter( Conversation.id == conversation_id, @@ -26,7 +28,7 @@ class AgentService: if not conversation: raise ValueError(f"Conversation not found: {conversation_id}") - message: Message = ( + message: Optional[Message] = ( db.session.query(Message) .filter( Message.id == message_id, @@ -72,7 +74,10 @@ class AgentService: } agent_config = AgentConfigManager.convert(app_model.app_model_config.to_dict()) - agent_tools = agent_config.tools + if not agent_config: + return result + + agent_tools = agent_config.tools or [] def find_agent_tool(tool_name: str): for agent_tool in agent_tools: diff --git a/api/services/annotation_service.py b/api/services/annotation_service.py index f45c21cb18..a946405c95 100644 --- a/api/services/annotation_service.py +++ b/api/services/annotation_service.py @@ -1,8 +1,9 @@ import datetime import uuid +from typing import cast import pandas as pd -from flask_login import current_user +from flask_login import current_user # type: ignore from sqlalchemy import or_ from werkzeug.datastructures import FileStorage from werkzeug.exceptions import NotFound @@ -71,7 +72,7 @@ class AppAnnotationService: app_id, annotation_setting.collection_binding_id, ) - return annotation + return cast(MessageAnnotation, annotation) @classmethod def enable_app_annotation(cls, args: dict, app_id: str) -> dict: @@ -124,8 +125,7 @@ class AppAnnotationService: raise NotFound("App not found") if keyword: annotations = ( - db.session.query(MessageAnnotation) - .filter(MessageAnnotation.app_id == app_id) + MessageAnnotation.query.filter(MessageAnnotation.app_id == app_id) .filter( or_( MessageAnnotation.question.ilike("%{}%".format(keyword)), @@ -137,8 +137,7 @@ class AppAnnotationService: ) else: annotations = ( - db.session.query(MessageAnnotation) - .filter(MessageAnnotation.app_id == app_id) + MessageAnnotation.query.filter(MessageAnnotation.app_id == app_id) .order_by(MessageAnnotation.created_at.desc(), MessageAnnotation.id.desc()) .paginate(page=page, per_page=limit, max_per_page=100, error_out=False) ) @@ -327,8 +326,7 @@ class AppAnnotationService: raise NotFound("Annotation not found") annotation_hit_histories = ( - db.session.query(AppAnnotationHitHistory) - .filter( + AppAnnotationHitHistory.query.filter( AppAnnotationHitHistory.app_id == app_id, AppAnnotationHitHistory.annotation_id == annotation_id, ) diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index 7c1a175988..b191fa2397 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -1,7 +1,7 @@ import logging import uuid from enum import StrEnum -from typing import Optional +from typing import Optional, cast from uuid import uuid4 import yaml @@ -103,7 +103,7 @@ class AppDslService: raise ValueError(f"Invalid import_mode: {import_mode}") # Get YAML content - content = "" + content: bytes | str = b"" if mode == ImportMode.YAML_URL: if not yaml_url: return Import( @@ -136,7 +136,7 @@ class AppDslService: ) try: - content = content.decode("utf-8") + content = cast(bytes, content).decode("utf-8") except UnicodeDecodeError as e: return Import( id=import_id, @@ -362,6 +362,9 @@ class AppDslService: app.icon_background = icon_background or app_data.get("icon_background", app.icon_background) app.updated_by = account.id else: + if account.current_tenant_id is None: + raise ValueError("Current tenant is not set") + # Create new app app = App() app.id = str(uuid4()) diff --git a/api/services/app_generate_service.py b/api/services/app_generate_service.py index 9def7d15e9..51aef7ccab 100644 --- a/api/services/app_generate_service.py +++ b/api/services/app_generate_service.py @@ -118,7 +118,7 @@ class AppGenerateService: @staticmethod def _get_max_active_requests(app_model: App) -> int: max_active_requests = app_model.max_active_requests - if app_model.max_active_requests is None: + if max_active_requests is None: max_active_requests = int(dify_config.APP_MAX_ACTIVE_REQUESTS) return max_active_requests @@ -150,7 +150,7 @@ class AppGenerateService: message_id: str, invoke_from: InvokeFrom, streaming: bool = True, - ) -> Union[dict, Generator]: + ) -> Union[Mapping, Generator]: """ Generate more like this :param app_model: app model diff --git a/api/services/app_service.py b/api/services/app_service.py index 8d8ba735ec..41c15bbf0a 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -1,9 +1,9 @@ import json import logging from datetime import UTC, datetime -from typing import cast +from typing import Optional, cast -from flask_login import current_user +from flask_login import current_user # type: ignore from flask_sqlalchemy.pagination import Pagination from configs import dify_config @@ -83,7 +83,7 @@ class AppService: # get default model instance try: model_instance = model_manager.get_default_model_instance( - tenant_id=account.current_tenant_id, model_type=ModelType.LLM + tenant_id=account.current_tenant_id or "", model_type=ModelType.LLM ) except (ProviderTokenNotInitError, LLMBadRequestError): model_instance = None @@ -100,6 +100,8 @@ class AppService: else: llm_model = cast(LargeLanguageModel, model_instance.model_type_instance) model_schema = llm_model.get_model_schema(model_instance.model, model_instance.credentials) + if model_schema is None: + raise ValueError(f"model schema not found for model {model_instance.model}") default_model_dict = { "provider": model_instance.provider, @@ -109,7 +111,7 @@ class AppService: } else: provider, model = model_manager.get_default_provider_model_name( - tenant_id=account.current_tenant_id, model_type=ModelType.LLM + tenant_id=account.current_tenant_id or "", model_type=ModelType.LLM ) default_model_config["model"]["provider"] = provider default_model_config["model"]["name"] = model @@ -314,7 +316,7 @@ class AppService: """ app_mode = AppMode.value_of(app_model.mode) - meta = {"tool_icons": {}} + meta: dict = {"tool_icons": {}} if app_mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}: workflow = app_model.workflow @@ -336,7 +338,7 @@ class AppService: } ) else: - app_model_config: AppModelConfig = app_model.app_model_config + app_model_config: Optional[AppModelConfig] = app_model.app_model_config if not app_model_config: return meta @@ -352,16 +354,18 @@ class AppService: keys = list(tool.keys()) if len(keys) >= 4: # current tool standard - provider_type = tool.get("provider_type") - provider_id = tool.get("provider_id") - tool_name = tool.get("tool_name") + provider_type = tool.get("provider_type", "") + provider_id = tool.get("provider_id", "") + tool_name = tool.get("tool_name", "") if provider_type == "builtin": meta["tool_icons"][tool_name] = url_prefix + provider_id + "/icon" elif provider_type == "api": try: - provider: ApiToolProvider = ( + provider: Optional[ApiToolProvider] = ( db.session.query(ApiToolProvider).filter(ApiToolProvider.id == provider_id).first() ) + if provider is None: + raise ValueError(f"provider not found for tool {tool_name}") meta["tool_icons"][tool_name] = json.loads(provider.icon) except: meta["tool_icons"][tool_name] = {"background": "#252525", "content": "\ud83d\ude01"} diff --git a/api/services/audio_service.py b/api/services/audio_service.py index 7a0cd5725b..973110f515 100644 --- a/api/services/audio_service.py +++ b/api/services/audio_service.py @@ -110,6 +110,8 @@ class AudioService: voices = model_instance.get_tts_voices() if voices: voice = voices[0].get("value") + if not voice: + raise ValueError("Sorry, no voice available.") else: raise ValueError("Sorry, no voice available.") @@ -121,6 +123,8 @@ class AudioService: if message_id: message = db.session.query(Message).filter(Message.id == message_id).first() + if message is None: + return None if message.answer == "" and message.status == "normal": return None @@ -130,6 +134,8 @@ class AudioService: return Response(stream_with_context(response), content_type="audio/mpeg") return response else: + if not text: + raise ValueError("Text is required") response = invoke_tts(text, app_model, voice) if isinstance(response, Generator): return Response(stream_with_context(response), content_type="audio/mpeg") diff --git a/api/services/auth/firecrawl/firecrawl.py b/api/services/auth/firecrawl/firecrawl.py index afc491398f..50e4edff14 100644 --- a/api/services/auth/firecrawl/firecrawl.py +++ b/api/services/auth/firecrawl/firecrawl.py @@ -11,8 +11,8 @@ class FirecrawlAuth(ApiKeyAuthBase): auth_type = credentials.get("auth_type") if auth_type != "bearer": raise ValueError("Invalid auth type, Firecrawl auth type must be Bearer") - self.api_key = credentials.get("config").get("api_key", None) - self.base_url = credentials.get("config").get("base_url", "https://api.firecrawl.dev") + self.api_key = credentials.get("config", {}).get("api_key", None) + self.base_url = credentials.get("config", {}).get("base_url", "https://api.firecrawl.dev") if not self.api_key: raise ValueError("No API key provided") diff --git a/api/services/auth/jina.py b/api/services/auth/jina.py index de898a1f94..6100e9afc8 100644 --- a/api/services/auth/jina.py +++ b/api/services/auth/jina.py @@ -11,7 +11,7 @@ class JinaAuth(ApiKeyAuthBase): auth_type = credentials.get("auth_type") if auth_type != "bearer": raise ValueError("Invalid auth type, Jina Reader auth type must be Bearer") - self.api_key = credentials.get("config").get("api_key", None) + self.api_key = credentials.get("config", {}).get("api_key", None) if not self.api_key: raise ValueError("No API key provided") diff --git a/api/services/auth/jina/jina.py b/api/services/auth/jina/jina.py index de898a1f94..6100e9afc8 100644 --- a/api/services/auth/jina/jina.py +++ b/api/services/auth/jina/jina.py @@ -11,7 +11,7 @@ class JinaAuth(ApiKeyAuthBase): auth_type = credentials.get("auth_type") if auth_type != "bearer": raise ValueError("Invalid auth type, Jina Reader auth type must be Bearer") - self.api_key = credentials.get("config").get("api_key", None) + self.api_key = credentials.get("config", {}).get("api_key", None) if not self.api_key: raise ValueError("No API key provided") diff --git a/api/services/billing_service.py b/api/services/billing_service.py index edc5168217..d980186488 100644 --- a/api/services/billing_service.py +++ b/api/services/billing_service.py @@ -1,4 +1,5 @@ import os +from typing import Optional import httpx from tenacity import retry, retry_if_not_exception_type, stop_before_delay, wait_fixed @@ -58,11 +59,14 @@ class BillingService: def is_tenant_owner_or_admin(current_user): tenant_id = current_user.current_tenant_id - join = ( + join: Optional[TenantAccountJoin] = ( db.session.query(TenantAccountJoin) .filter(TenantAccountJoin.tenant_id == tenant_id, TenantAccountJoin.account_id == current_user.id) .first() ) + if not join: + raise ValueError("Tenant account join not found") + if not TenantAccountRole.is_privileged_role(join.role): raise ValueError("Only team owner or team admin can perform this action") diff --git a/api/services/conversation_service.py b/api/services/conversation_service.py index 456dc3ebeb..6485cbf37d 100644 --- a/api/services/conversation_service.py +++ b/api/services/conversation_service.py @@ -72,8 +72,7 @@ class ConversationService: sort_direction=sort_direction, reference_conversation=current_page_last_conversation, ) - count_stmt = stmt.where(rest_filter_condition) - count_stmt = select(func.count()).select_from(count_stmt.subquery()) + count_stmt = select(func.count()).select_from(stmt.where(rest_filter_condition).subquery()) rest_count = session.scalar(count_stmt) or 0 if rest_count > 0: has_more = True diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index 4e99c73ad4..d2d8a718d5 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -6,7 +6,7 @@ import time import uuid from typing import Any, Optional -from flask_login import current_user +from flask_login import current_user # type: ignore from sqlalchemy import func from werkzeug.exceptions import NotFound @@ -186,8 +186,9 @@ class DatasetService: return dataset @staticmethod - def get_dataset(dataset_id) -> Dataset: - return Dataset.query.filter_by(id=dataset_id).first() + def get_dataset(dataset_id) -> Optional[Dataset]: + dataset: Optional[Dataset] = Dataset.query.filter_by(id=dataset_id).first() + return dataset @staticmethod def check_dataset_model_setting(dataset): @@ -228,6 +229,8 @@ class DatasetService: @staticmethod def update_dataset(dataset_id, data, user): dataset = DatasetService.get_dataset(dataset_id) + if not dataset: + raise ValueError("Dataset not found") DatasetService.check_dataset_permission(dataset, user) if dataset.provider == "external": @@ -371,7 +374,13 @@ class DatasetService: raise NoPermissionError("You do not have permission to access this dataset.") @staticmethod - def check_dataset_operator_permission(user: Account = None, dataset: Dataset = None): + def check_dataset_operator_permission(user: Optional[Account] = None, dataset: Optional[Dataset] = None): + if not dataset: + raise ValueError("Dataset not found") + + if not user: + raise ValueError("User not found") + if dataset.permission == DatasetPermissionEnum.ONLY_ME: if dataset.created_by != user.id: raise NoPermissionError("You do not have permission to access this dataset.") @@ -765,6 +774,11 @@ class DocumentService: rules=json.dumps(DatasetProcessRule.AUTOMATIC_RULES), created_by=account.id, ) + else: + logging.warn( + f"Invalid process rule mode: {process_rule['mode']}, can not find dataset process rule" + ) + return db.session.add(dataset_process_rule) db.session.commit() lock_name = "add_document_lock_dataset_id_{}".format(dataset.id) @@ -1009,9 +1023,10 @@ class DocumentService: rules=json.dumps(DatasetProcessRule.AUTOMATIC_RULES), created_by=account.id, ) - db.session.add(dataset_process_rule) - db.session.commit() - document.dataset_process_rule_id = dataset_process_rule.id + if dataset_process_rule is not None: + db.session.add(dataset_process_rule) + db.session.commit() + document.dataset_process_rule_id = dataset_process_rule.id # update document data source if document_data.get("data_source"): file_name = "" @@ -1554,7 +1569,7 @@ class SegmentService: segment.word_count = len(content) if document.doc_form == "qa_model": segment.answer = segment_update_entity.answer - segment.word_count += len(segment_update_entity.answer) + segment.word_count += len(segment_update_entity.answer or "") word_count_change = segment.word_count - word_count_change if segment_update_entity.keywords: segment.keywords = segment_update_entity.keywords @@ -1569,7 +1584,8 @@ class SegmentService: db.session.add(document) # update segment index task if segment_update_entity.enabled: - VectorService.create_segments_vector([segment_update_entity.keywords], [segment], dataset) + keywords = segment_update_entity.keywords or [] + VectorService.create_segments_vector([keywords], [segment], dataset) else: segment_hash = helper.generate_text_hash(content) tokens = 0 @@ -1601,7 +1617,7 @@ class SegmentService: segment.disabled_by = None if document.doc_form == "qa_model": segment.answer = segment_update_entity.answer - segment.word_count += len(segment_update_entity.answer) + segment.word_count += len(segment_update_entity.answer or "") word_count_change = segment.word_count - word_count_change # update document word count if word_count_change != 0: @@ -1619,8 +1635,8 @@ class SegmentService: segment.status = "error" segment.error = str(e) db.session.commit() - segment = db.session.query(DocumentSegment).filter(DocumentSegment.id == segment.id).first() - return segment + new_segment = db.session.query(DocumentSegment).filter(DocumentSegment.id == segment.id).first() + return new_segment @classmethod def delete_segment(cls, segment: DocumentSegment, document: Document, dataset: Dataset): @@ -1680,6 +1696,8 @@ class DatasetCollectionBindingService: .order_by(DatasetCollectionBinding.created_at) .first() ) + if not dataset_collection_binding: + raise ValueError("Dataset collection binding not found") return dataset_collection_binding diff --git a/api/services/enterprise/base.py b/api/services/enterprise/base.py index 92098f06cc..3c3f970444 100644 --- a/api/services/enterprise/base.py +++ b/api/services/enterprise/base.py @@ -8,8 +8,8 @@ class EnterpriseRequest: secret_key = os.environ.get("ENTERPRISE_API_SECRET_KEY", "ENTERPRISE_API_SECRET_KEY") proxies = { - "http": None, - "https": None, + "http": "", + "https": "", } @classmethod diff --git a/api/services/entities/model_provider_entities.py b/api/services/entities/model_provider_entities.py index c519f0b0e5..334d009ee5 100644 --- a/api/services/entities/model_provider_entities.py +++ b/api/services/entities/model_provider_entities.py @@ -4,7 +4,11 @@ from typing import Optional from pydantic import BaseModel, ConfigDict from configs import dify_config -from core.entities.model_entities import ModelWithProviderEntity, ProviderModelWithStatusEntity +from core.entities.model_entities import ( + ModelWithProviderEntity, + ProviderModelWithStatusEntity, + SimpleModelProviderEntity, +) from core.entities.provider_entities import QuotaConfiguration from core.model_runtime.entities.common_entities import I18nObject from core.model_runtime.entities.model_entities import ModelType @@ -148,7 +152,7 @@ class ModelWithProviderEntityResponse(ModelWithProviderEntity): Model with provider entity. """ - provider: SimpleProviderEntityResponse + provider: SimpleModelProviderEntity def __init__(self, model: ModelWithProviderEntity) -> None: super().__init__(**model.model_dump()) diff --git a/api/services/external_knowledge_service.py b/api/services/external_knowledge_service.py index 7be20301a7..898624066b 100644 --- a/api/services/external_knowledge_service.py +++ b/api/services/external_knowledge_service.py @@ -1,7 +1,7 @@ import json from copy import deepcopy from datetime import UTC, datetime -from typing import Any, Optional, Union +from typing import Any, Optional, Union, cast import httpx import validators @@ -45,7 +45,10 @@ class ExternalDatasetService: @staticmethod def create_external_knowledge_api(tenant_id: str, user_id: str, args: dict) -> ExternalKnowledgeApis: - ExternalDatasetService.check_endpoint_and_api_key(args.get("settings")) + settings = args.get("settings") + if settings is None: + raise ValueError("settings is required") + ExternalDatasetService.check_endpoint_and_api_key(settings) external_knowledge_api = ExternalKnowledgeApis( tenant_id=tenant_id, created_by=user_id, @@ -86,11 +89,16 @@ class ExternalDatasetService: @staticmethod def get_external_knowledge_api(external_knowledge_api_id: str) -> ExternalKnowledgeApis: - return ExternalKnowledgeApis.query.filter_by(id=external_knowledge_api_id).first() + external_knowledge_api: Optional[ExternalKnowledgeApis] = ExternalKnowledgeApis.query.filter_by( + id=external_knowledge_api_id + ).first() + if external_knowledge_api is None: + raise ValueError("api template not found") + return external_knowledge_api @staticmethod def update_external_knowledge_api(tenant_id, user_id, external_knowledge_api_id, args) -> ExternalKnowledgeApis: - external_knowledge_api = ExternalKnowledgeApis.query.filter_by( + external_knowledge_api: Optional[ExternalKnowledgeApis] = ExternalKnowledgeApis.query.filter_by( id=external_knowledge_api_id, tenant_id=tenant_id ).first() if external_knowledge_api is None: @@ -127,7 +135,7 @@ class ExternalDatasetService: @staticmethod def get_external_knowledge_binding_with_dataset_id(tenant_id: str, dataset_id: str) -> ExternalKnowledgeBindings: - external_knowledge_binding = ExternalKnowledgeBindings.query.filter_by( + external_knowledge_binding: Optional[ExternalKnowledgeBindings] = ExternalKnowledgeBindings.query.filter_by( dataset_id=dataset_id, tenant_id=tenant_id ).first() if not external_knowledge_binding: @@ -163,8 +171,9 @@ class ExternalDatasetService: "follow_redirects": True, } - response = getattr(ssrf_proxy, settings.request_method)(data=json.dumps(settings.params), files=files, **kwargs) - + response: httpx.Response = getattr(ssrf_proxy, settings.request_method)( + data=json.dumps(settings.params), files=files, **kwargs + ) return response @staticmethod @@ -265,15 +274,15 @@ class ExternalDatasetService: "knowledge_id": external_knowledge_binding.external_knowledge_id, } - external_knowledge_api_setting = { - "url": f"{settings.get('endpoint')}/retrieval", - "request_method": "post", - "headers": headers, - "params": request_params, - } response = ExternalDatasetService.process_external_api( - ExternalKnowledgeApiSetting(**external_knowledge_api_setting), None + ExternalKnowledgeApiSetting( + url=f"{settings.get('endpoint')}/retrieval", + request_method="post", + headers=headers, + params=request_params, + ), + None, ) if response.status_code == 200: - return response.json().get("records", []) + return cast(list[Any], response.json().get("records", [])) return [] diff --git a/api/services/file_service.py b/api/services/file_service.py index b12b95ca13..d417e81734 100644 --- a/api/services/file_service.py +++ b/api/services/file_service.py @@ -3,7 +3,7 @@ import hashlib import uuid from typing import Any, Literal, Union -from flask_login import current_user +from flask_login import current_user # type: ignore from werkzeug.exceptions import NotFound from configs import dify_config @@ -61,14 +61,14 @@ class FileService: # end_user current_tenant_id = user.tenant_id - file_key = "upload_files/" + current_tenant_id + "/" + file_uuid + "." + extension + file_key = "upload_files/" + (current_tenant_id or "") + "/" + file_uuid + "." + extension # save file to storage storage.save(file_key, content) # save file to db upload_file = UploadFile( - tenant_id=current_tenant_id, + tenant_id=current_tenant_id or "", storage_type=dify_config.STORAGE_TYPE, key=file_key, name=filename, diff --git a/api/services/hit_testing_service.py b/api/services/hit_testing_service.py index 7957b4dc82..41b4e1ec46 100644 --- a/api/services/hit_testing_service.py +++ b/api/services/hit_testing_service.py @@ -1,5 +1,6 @@ import logging import time +from typing import Any from core.rag.datasource.retrieval_service import RetrievalService from core.rag.models.document import Document @@ -24,7 +25,7 @@ class HitTestingService: dataset: Dataset, query: str, account: Account, - retrieval_model: dict, + retrieval_model: Any, # FIXME drop this any external_retrieval_model: dict, limit: int = 10, ) -> dict: @@ -68,7 +69,7 @@ class HitTestingService: db.session.add(dataset_query) db.session.commit() - return cls.compact_retrieve_response(dataset, query, all_documents) + return dict(cls.compact_retrieve_response(dataset, query, all_documents)) @classmethod def external_retrieve( @@ -102,13 +103,16 @@ class HitTestingService: db.session.add(dataset_query) db.session.commit() - return cls.compact_external_retrieve_response(dataset, query, all_documents) + return dict(cls.compact_external_retrieve_response(dataset, query, all_documents)) @classmethod def compact_retrieve_response(cls, dataset: Dataset, query: str, documents: list[Document]): records = [] for document in documents: + if document.metadata is None: + continue + index_node_id = document.metadata["doc_id"] segment = ( @@ -140,7 +144,7 @@ class HitTestingService: } @classmethod - def compact_external_retrieve_response(cls, dataset: Dataset, query: str, documents: list): + def compact_external_retrieve_response(cls, dataset: Dataset, query: str, documents: list) -> dict[Any, Any]: records = [] if dataset.provider == "external": for document in documents: @@ -152,11 +156,10 @@ class HitTestingService: } records.append(record) return { - "query": { - "content": query, - }, + "query": {"content": query}, "records": records, } + return {"query": {"content": query}, "records": []} @classmethod def hit_testing_args_check(cls, args): diff --git a/api/services/knowledge_service.py b/api/services/knowledge_service.py index 02fe1d19bc..8df1a6ba14 100644 --- a/api/services/knowledge_service.py +++ b/api/services/knowledge_service.py @@ -1,4 +1,4 @@ -import boto3 +import boto3 # type: ignore from configs import dify_config diff --git a/api/services/message_service.py b/api/services/message_service.py index be2922f4c5..c4447a84da 100644 --- a/api/services/message_service.py +++ b/api/services/message_service.py @@ -157,7 +157,7 @@ class MessageService: user: Optional[Union[Account, EndUser]], rating: Optional[str], content: Optional[str], - ) -> MessageFeedback: + ): if not user: raise ValueError("user cannot be None") @@ -264,6 +264,8 @@ class MessageService: ) app_model_config = app_model_config.from_model_config_dict(conversation_override_model_configs) + if not app_model_config: + raise ValueError("did not find app model config") suggested_questions_after_answer = app_model_config.suggested_questions_after_answer_dict if suggested_questions_after_answer.get("enabled", False) is False: @@ -285,7 +287,7 @@ class MessageService: ) with measure_time() as timer: - questions = LLMGenerator.generate_suggested_questions_after_answer( + questions: list[Message] = LLMGenerator.generate_suggested_questions_after_answer( tenant_id=app_model.tenant_id, histories=histories ) diff --git a/api/services/model_load_balancing_service.py b/api/services/model_load_balancing_service.py index b20bda8755..bacd3a8ec3 100644 --- a/api/services/model_load_balancing_service.py +++ b/api/services/model_load_balancing_service.py @@ -2,7 +2,7 @@ import datetime import json import logging from json import JSONDecodeError -from typing import Optional +from typing import Optional, Union from constants import HIDDEN_VALUE from core.entities.provider_configuration import ProviderConfiguration @@ -88,11 +88,11 @@ class ModelLoadBalancingService: raise ValueError(f"Provider {provider} does not exist.") # Convert model type to ModelType - model_type = ModelType.value_of(model_type) + model_type_enum = ModelType.value_of(model_type) # Get provider model setting provider_model_setting = provider_configuration.get_provider_model_setting( - model_type=model_type, + model_type=model_type_enum, model=model, ) @@ -106,7 +106,7 @@ class ModelLoadBalancingService: .filter( LoadBalancingModelConfig.tenant_id == tenant_id, LoadBalancingModelConfig.provider_name == provider_configuration.provider.provider, - LoadBalancingModelConfig.model_type == model_type.to_origin_model_type(), + LoadBalancingModelConfig.model_type == model_type_enum.to_origin_model_type(), LoadBalancingModelConfig.model_name == model, ) .order_by(LoadBalancingModelConfig.created_at) @@ -124,7 +124,7 @@ class ModelLoadBalancingService: if not inherit_config_exists: # Initialize the inherit configuration - inherit_config = self._init_inherit_config(tenant_id, provider, model, model_type) + inherit_config = self._init_inherit_config(tenant_id, provider, model, model_type_enum) # prepend the inherit configuration load_balancing_configs.insert(0, inherit_config) @@ -148,7 +148,7 @@ class ModelLoadBalancingService: tenant_id=tenant_id, provider=provider, model=model, - model_type=model_type, + model_type=model_type_enum, config_id=load_balancing_config.id, ) @@ -214,7 +214,7 @@ class ModelLoadBalancingService: raise ValueError(f"Provider {provider} does not exist.") # Convert model type to ModelType - model_type = ModelType.value_of(model_type) + model_type_enum = ModelType.value_of(model_type) # Get load balancing configurations load_balancing_model_config = ( @@ -222,7 +222,7 @@ class ModelLoadBalancingService: .filter( LoadBalancingModelConfig.tenant_id == tenant_id, LoadBalancingModelConfig.provider_name == provider_configuration.provider.provider, - LoadBalancingModelConfig.model_type == model_type.to_origin_model_type(), + LoadBalancingModelConfig.model_type == model_type_enum.to_origin_model_type(), LoadBalancingModelConfig.model_name == model, LoadBalancingModelConfig.id == config_id, ) @@ -300,7 +300,7 @@ class ModelLoadBalancingService: raise ValueError(f"Provider {provider} does not exist.") # Convert model type to ModelType - model_type = ModelType.value_of(model_type) + model_type_enum = ModelType.value_of(model_type) if not isinstance(configs, list): raise ValueError("Invalid load balancing configs") @@ -310,7 +310,7 @@ class ModelLoadBalancingService: .filter( LoadBalancingModelConfig.tenant_id == tenant_id, LoadBalancingModelConfig.provider_name == provider_configuration.provider.provider, - LoadBalancingModelConfig.model_type == model_type.to_origin_model_type(), + LoadBalancingModelConfig.model_type == model_type_enum.to_origin_model_type(), LoadBalancingModelConfig.model_name == model, ) .all() @@ -359,7 +359,7 @@ class ModelLoadBalancingService: credentials = self._custom_credentials_validate( tenant_id=tenant_id, provider_configuration=provider_configuration, - model_type=model_type, + model_type=model_type_enum, model=model, credentials=credentials, load_balancing_model_config=load_balancing_config, @@ -395,7 +395,7 @@ class ModelLoadBalancingService: credentials = self._custom_credentials_validate( tenant_id=tenant_id, provider_configuration=provider_configuration, - model_type=model_type, + model_type=model_type_enum, model=model, credentials=credentials, validate=False, @@ -405,7 +405,7 @@ class ModelLoadBalancingService: load_balancing_model_config = LoadBalancingModelConfig( tenant_id=tenant_id, provider_name=provider_configuration.provider.provider, - model_type=model_type.to_origin_model_type(), + model_type=model_type_enum.to_origin_model_type(), model_name=model, name=name, encrypted_config=json.dumps(credentials), @@ -450,7 +450,7 @@ class ModelLoadBalancingService: raise ValueError(f"Provider {provider} does not exist.") # Convert model type to ModelType - model_type = ModelType.value_of(model_type) + model_type_enum = ModelType.value_of(model_type) load_balancing_model_config = None if config_id: @@ -460,7 +460,7 @@ class ModelLoadBalancingService: .filter( LoadBalancingModelConfig.tenant_id == tenant_id, LoadBalancingModelConfig.provider_name == provider, - LoadBalancingModelConfig.model_type == model_type.to_origin_model_type(), + LoadBalancingModelConfig.model_type == model_type_enum.to_origin_model_type(), LoadBalancingModelConfig.model_name == model, LoadBalancingModelConfig.id == config_id, ) @@ -474,7 +474,7 @@ class ModelLoadBalancingService: self._custom_credentials_validate( tenant_id=tenant_id, provider_configuration=provider_configuration, - model_type=model_type, + model_type=model_type_enum, model=model, credentials=credentials, load_balancing_model_config=load_balancing_model_config, @@ -547,19 +547,14 @@ class ModelLoadBalancingService: def _get_credential_schema( self, provider_configuration: ProviderConfiguration - ) -> ModelCredentialSchema | ProviderCredentialSchema: - """ - Get form schemas. - :param provider_configuration: provider configuration - :return: - """ - # Get credential form schemas from model credential schema or provider credential schema + ) -> Union[ModelCredentialSchema, ProviderCredentialSchema]: + """Get form schemas.""" if provider_configuration.provider.model_credential_schema: - credential_schema = provider_configuration.provider.model_credential_schema + return provider_configuration.provider.model_credential_schema + elif provider_configuration.provider.provider_credential_schema: + return provider_configuration.provider.provider_credential_schema else: - credential_schema = provider_configuration.provider.provider_credential_schema - - return credential_schema + raise ValueError("No credential schema found") def _clear_credentials_cache(self, tenant_id: str, config_id: str) -> None: """ diff --git a/api/services/model_provider_service.py b/api/services/model_provider_service.py index 384a072b37..b10c5ad2d6 100644 --- a/api/services/model_provider_service.py +++ b/api/services/model_provider_service.py @@ -7,7 +7,7 @@ from typing import Optional, cast import requests from flask import current_app -from core.entities.model_entities import ModelStatus, ProviderModelWithStatusEntity +from core.entities.model_entities import ModelStatus, ModelWithProviderEntity, ProviderModelWithStatusEntity from core.model_runtime.entities.model_entities import ModelType, ParameterRule from core.model_runtime.model_providers import model_provider_factory from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel @@ -100,23 +100,15 @@ class ModelProviderService: ModelWithProviderEntityResponse(model) for model in provider_configurations.get_models(provider=provider) ] - def get_provider_credentials(self, tenant_id: str, provider: str) -> dict: + def get_provider_credentials(self, tenant_id: str, provider: str): """ get provider credentials. - - :param tenant_id: - :param provider: - :return: """ - # Get all provider configurations of the current workspace provider_configurations = self.provider_manager.get_configurations(tenant_id) - - # Get provider configuration provider_configuration = provider_configurations.get(provider) if not provider_configuration: raise ValueError(f"Provider {provider} does not exist.") - # Get provider custom credentials from workspace return provider_configuration.get_custom_credentials(obfuscated=True) def provider_credentials_validate(self, tenant_id: str, provider: str, credentials: dict) -> None: @@ -176,7 +168,7 @@ class ModelProviderService: # Remove custom provider credentials. provider_configuration.delete_custom_credentials() - def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str) -> dict: + def get_model_credentials(self, tenant_id: str, provider: str, model_type: str, model: str): """ get model credentials. @@ -287,7 +279,7 @@ class ModelProviderService: models = provider_configurations.get_models(model_type=ModelType.value_of(model_type)) # Group models by provider - provider_models = {} + provider_models: dict[str, list[ModelWithProviderEntity]] = {} for model in models: if model.provider.provider not in provider_models: provider_models[model.provider.provider] = [] @@ -362,7 +354,7 @@ class ModelProviderService: return [] # Call get_parameter_rules method of model instance to get model parameter rules - return model_type_instance.get_parameter_rules(model=model, credentials=credentials) + return list(model_type_instance.get_parameter_rules(model=model, credentials=credentials)) def get_default_model_of_model_type(self, tenant_id: str, model_type: str) -> Optional[DefaultModelResponse]: """ @@ -422,6 +414,7 @@ class ModelProviderService: """ provider_instance = model_provider_factory.get_provider_instance(provider) provider_schema = provider_instance.get_provider_schema() + file_name: str | None = None if icon_type.lower() == "icon_small": if not provider_schema.icon_small: @@ -439,6 +432,8 @@ class ModelProviderService: file_name = provider_schema.icon_large.zh_Hans else: file_name = provider_schema.icon_large.en_US + if not file_name: + return None, None root_path = current_app.root_path provider_instance_path = os.path.dirname( @@ -524,7 +519,7 @@ class ModelProviderService: def free_quota_submit(self, tenant_id: str, provider: str): api_key = os.environ.get("FREE_QUOTA_APPLY_API_KEY") - api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL") + api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL", "") api_url = api_base_url + "/api/v1/providers/apply" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} @@ -545,7 +540,7 @@ class ModelProviderService: def free_quota_qualification_verify(self, tenant_id: str, provider: str, token: Optional[str]): api_key = os.environ.get("FREE_QUOTA_APPLY_API_KEY") - api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL") + api_base_url = os.environ.get("FREE_QUOTA_APPLY_BASE_URL", "") api_url = api_base_url + "/api/v1/providers/qualification-verify" headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"} diff --git a/api/services/moderation_service.py b/api/services/moderation_service.py index dfb21e767f..082afeed89 100644 --- a/api/services/moderation_service.py +++ b/api/services/moderation_service.py @@ -1,3 +1,5 @@ +from typing import Optional + from core.moderation.factory import ModerationFactory, ModerationOutputsResult from extensions.ext_database import db from models.model import App, AppModelConfig @@ -5,7 +7,7 @@ from models.model import App, AppModelConfig class ModerationService: def moderation_for_outputs(self, app_id: str, app_model: App, text: str) -> ModerationOutputsResult: - app_model_config: AppModelConfig = None + app_model_config: Optional[AppModelConfig] = None app_model_config = ( db.session.query(AppModelConfig).filter(AppModelConfig.id == app_model.app_model_config_id).first() diff --git a/api/services/ops_service.py b/api/services/ops_service.py index 1160a1f275..fc1e08518b 100644 --- a/api/services/ops_service.py +++ b/api/services/ops_service.py @@ -1,3 +1,5 @@ +from typing import Optional + from core.ops.ops_trace_manager import OpsTraceManager, provider_config_map from extensions.ext_database import db from models.model import App, TraceAppConfig @@ -12,7 +14,7 @@ class OpsService: :param tracing_provider: tracing provider :return: """ - trace_config_data: TraceAppConfig = ( + trace_config_data: Optional[TraceAppConfig] = ( db.session.query(TraceAppConfig) .filter(TraceAppConfig.app_id == app_id, TraceAppConfig.tracing_provider == tracing_provider) .first() @@ -22,7 +24,10 @@ class OpsService: return None # decrypt_token and obfuscated_token - tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id + tenant = db.session.query(App).filter(App.id == app_id).first() + if not tenant: + return None + tenant_id = tenant.tenant_id decrypt_tracing_config = OpsTraceManager.decrypt_tracing_config( tenant_id, tracing_provider, trace_config_data.tracing_config ) @@ -73,8 +78,9 @@ class OpsService: provider_config_map[tracing_provider]["config_class"], provider_config_map[tracing_provider]["other_keys"], ) - default_config_instance = config_class(**tracing_config) - for key in other_keys: + # FIXME: ignore type error + default_config_instance = config_class(**tracing_config) # type: ignore + for key in other_keys: # type: ignore if key in tracing_config and tracing_config[key] == "": tracing_config[key] = getattr(default_config_instance, key, None) @@ -92,7 +98,7 @@ class OpsService: project_url = None # check if trace config already exists - trace_config_data: TraceAppConfig = ( + trace_config_data: Optional[TraceAppConfig] = ( db.session.query(TraceAppConfig) .filter(TraceAppConfig.app_id == app_id, TraceAppConfig.tracing_provider == tracing_provider) .first() @@ -102,7 +108,10 @@ class OpsService: return None # get tenant id - tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id + tenant = db.session.query(App).filter(App.id == app_id).first() + if not tenant: + return None + tenant_id = tenant.tenant_id tracing_config = OpsTraceManager.encrypt_tracing_config(tenant_id, tracing_provider, tracing_config) if project_url: tracing_config["project_url"] = project_url @@ -139,7 +148,10 @@ class OpsService: return None # get tenant id - tenant_id = db.session.query(App).filter(App.id == app_id).first().tenant_id + tenant = db.session.query(App).filter(App.id == app_id).first() + if not tenant: + return None + tenant_id = tenant.tenant_id tracing_config = OpsTraceManager.encrypt_tracing_config( tenant_id, tracing_provider, tracing_config, current_trace_config.tracing_config ) diff --git a/api/services/recommend_app/buildin/buildin_retrieval.py b/api/services/recommend_app/buildin/buildin_retrieval.py index 4704d533a9..523aebeed5 100644 --- a/api/services/recommend_app/buildin/buildin_retrieval.py +++ b/api/services/recommend_app/buildin/buildin_retrieval.py @@ -41,7 +41,7 @@ class BuildInRecommendAppRetrieval(RecommendAppRetrievalBase): Path(path.join(root_path, "constants", "recommended_apps.json")).read_text(encoding="utf-8") ) - return cls.builtin_data + return cls.builtin_data or {} @classmethod def fetch_recommended_apps_from_builtin(cls, language: str) -> dict: @@ -50,8 +50,8 @@ class BuildInRecommendAppRetrieval(RecommendAppRetrievalBase): :param language: language :return: """ - builtin_data = cls._get_builtin_data() - return builtin_data.get("recommended_apps", {}).get(language) + builtin_data: dict[str, dict[str, dict]] = cls._get_builtin_data() + return builtin_data.get("recommended_apps", {}).get(language, {}) @classmethod def fetch_recommended_app_detail_from_builtin(cls, app_id: str) -> Optional[dict]: @@ -60,5 +60,5 @@ class BuildInRecommendAppRetrieval(RecommendAppRetrievalBase): :param app_id: App ID :return: """ - builtin_data = cls._get_builtin_data() + builtin_data: dict[str, dict[str, dict]] = cls._get_builtin_data() return builtin_data.get("app_details", {}).get(app_id) diff --git a/api/services/recommend_app/remote/remote_retrieval.py b/api/services/recommend_app/remote/remote_retrieval.py index b0607a2132..80e1aefc01 100644 --- a/api/services/recommend_app/remote/remote_retrieval.py +++ b/api/services/recommend_app/remote/remote_retrieval.py @@ -47,8 +47,8 @@ class RemoteRecommendAppRetrieval(RecommendAppRetrievalBase): response = requests.get(url, timeout=(3, 10)) if response.status_code != 200: return None - - return response.json() + data: dict = response.json() + return data @classmethod def fetch_recommended_apps_from_dify_official(cls, language: str) -> dict: @@ -63,7 +63,7 @@ class RemoteRecommendAppRetrieval(RecommendAppRetrievalBase): if response.status_code != 200: raise ValueError(f"fetch recommended apps failed, status code: {response.status_code}") - result = response.json() + result: dict = response.json() if "categories" in result: result["categories"] = sorted(result["categories"]) diff --git a/api/services/recommended_app_service.py b/api/services/recommended_app_service.py index 4660316fcf..54c5845515 100644 --- a/api/services/recommended_app_service.py +++ b/api/services/recommended_app_service.py @@ -33,5 +33,5 @@ class RecommendedAppService: """ mode = dify_config.HOSTED_FETCH_APP_TEMPLATES_MODE retrieval_instance = RecommendAppRetrievalFactory.get_recommend_app_factory(mode)() - result = retrieval_instance.get_recommend_app_detail(app_id) + result: dict = retrieval_instance.get_recommend_app_detail(app_id) return result diff --git a/api/services/saved_message_service.py b/api/services/saved_message_service.py index 9fe3cecce7..4cb8700117 100644 --- a/api/services/saved_message_service.py +++ b/api/services/saved_message_service.py @@ -13,6 +13,8 @@ class SavedMessageService: def pagination_by_last_id( cls, app_model: App, user: Optional[Union[Account, EndUser]], last_id: Optional[str], limit: int ) -> InfiniteScrollPagination: + if not user: + raise ValueError("User is required") saved_messages = ( db.session.query(SavedMessage) .filter( @@ -31,6 +33,8 @@ class SavedMessageService: @classmethod def save(cls, app_model: App, user: Optional[Union[Account, EndUser]], message_id: str): + if not user: + return saved_message = ( db.session.query(SavedMessage) .filter( @@ -59,6 +63,8 @@ class SavedMessageService: @classmethod def delete(cls, app_model: App, user: Optional[Union[Account, EndUser]], message_id: str): + if not user: + return saved_message = ( db.session.query(SavedMessage) .filter( diff --git a/api/services/tag_service.py b/api/services/tag_service.py index a374bdcf00..9600601633 100644 --- a/api/services/tag_service.py +++ b/api/services/tag_service.py @@ -1,7 +1,7 @@ import uuid from typing import Optional -from flask_login import current_user +from flask_login import current_user # type: ignore from sqlalchemy import func from werkzeug.exceptions import NotFound @@ -21,7 +21,7 @@ class TagService: if keyword: query = query.filter(db.and_(Tag.name.ilike(f"%{keyword}%"))) query = query.group_by(Tag.id) - results = query.order_by(Tag.created_at.desc()).all() + results: list = query.order_by(Tag.created_at.desc()).all() return results @staticmethod diff --git a/api/services/tools/api_tools_manage_service.py b/api/services/tools/api_tools_manage_service.py index 78a80f70ab..0e3bd3a7b8 100644 --- a/api/services/tools/api_tools_manage_service.py +++ b/api/services/tools/api_tools_manage_service.py @@ -1,6 +1,7 @@ import json import logging -from typing import Optional +from collections.abc import Mapping +from typing import Any, Optional, cast from httpx import get @@ -28,12 +29,12 @@ logger = logging.getLogger(__name__) class ApiToolManageService: @staticmethod - def parser_api_schema(schema: str) -> list[ApiToolBundle]: + def parser_api_schema(schema: str) -> Mapping[str, Any]: """ parse api schema to tool bundle """ try: - warnings = {} + warnings: dict[str, str] = {} try: tool_bundles, schema_type = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, warning=warnings) except Exception as e: @@ -68,13 +69,16 @@ class ApiToolManageService: ), ] - return jsonable_encoder( - { - "schema_type": schema_type, - "parameters_schema": tool_bundles, - "credentials_schema": credentials_schema, - "warning": warnings, - } + return cast( + Mapping, + jsonable_encoder( + { + "schema_type": schema_type, + "parameters_schema": tool_bundles, + "credentials_schema": credentials_schema, + "warning": warnings, + } + ), ) except Exception as e: raise ValueError(f"invalid schema: {str(e)}") @@ -129,7 +133,7 @@ class ApiToolManageService: raise ValueError(f"provider {provider_name} already exists") # parse openapi to tool bundle - extra_info = {} + extra_info: dict[str, str] = {} # extra info like description will be set here tool_bundles, schema_type = ApiToolManageService.convert_schema_to_tool_bundles(schema, extra_info) @@ -262,9 +266,8 @@ class ApiToolManageService: if provider is None: raise ValueError(f"api provider {provider_name} does not exists") - # parse openapi to tool bundle - extra_info = {} + extra_info: dict[str, str] = {} # extra info like description will be set here tool_bundles, schema_type = ApiToolManageService.convert_schema_to_tool_bundles(schema, extra_info) @@ -416,7 +419,7 @@ class ApiToolManageService: provider_controller.validate_credentials_format(credentials) # get tool tool = provider_controller.get_tool(tool_name) - tool = tool.fork_tool_runtime( + runtime_tool = tool.fork_tool_runtime( runtime={ "credentials": credentials, "tenant_id": tenant_id, @@ -454,7 +457,7 @@ class ApiToolManageService: tools = provider_controller.get_tools(user_id=user_id, tenant_id=tenant_id) - for tool in tools: + for tool in tools or []: user_provider.tools.append( ToolTransformService.tool_to_user_tool( tenant_id=tenant_id, tool=tool, credentials=user_provider.original_credentials, labels=labels diff --git a/api/services/tools/builtin_tools_manage_service.py b/api/services/tools/builtin_tools_manage_service.py index fada881fde..21adbb0074 100644 --- a/api/services/tools/builtin_tools_manage_service.py +++ b/api/services/tools/builtin_tools_manage_service.py @@ -50,8 +50,8 @@ class BuiltinToolManageService: credentials = builtin_provider.credentials credentials = tool_provider_configurations.decrypt_tool_credentials(credentials) - result = [] - for tool in tools: + result: list[UserTool] = [] + for tool in tools or []: result.append( ToolTransformService.tool_to_user_tool( tool=tool, @@ -217,6 +217,8 @@ class BuiltinToolManageService: name_func=lambda x: x.identity.name, ): continue + if provider_controller.identity is None: + continue # convert provider controller to user provider user_builtin_provider = ToolTransformService.builtin_provider_to_user_provider( @@ -229,7 +231,7 @@ class BuiltinToolManageService: ToolTransformService.repack_provider(user_builtin_provider) tools = provider_controller.get_tools() - for tool in tools: + for tool in tools or []: user_builtin_provider.tools.append( ToolTransformService.tool_to_user_tool( tenant_id=tenant_id, diff --git a/api/services/tools/tools_transform_service.py b/api/services/tools/tools_transform_service.py index a4aa870dc8..b501554bcd 100644 --- a/api/services/tools/tools_transform_service.py +++ b/api/services/tools/tools_transform_service.py @@ -1,6 +1,6 @@ import json import logging -from typing import Optional, Union +from typing import Optional, Union, cast from configs import dify_config from core.tools.entities.api_entities import UserTool, UserToolProvider @@ -35,7 +35,7 @@ class ToolTransformService: return url_prefix + "builtin/" + provider_name + "/icon" elif provider_type in {ToolProviderType.API.value, ToolProviderType.WORKFLOW.value}: try: - return json.loads(icon) + return cast(dict, json.loads(icon)) except: return {"background": "#252525", "content": "\ud83d\ude01"} @@ -53,8 +53,11 @@ class ToolTransformService: provider_type=provider["type"], provider_name=provider["name"], icon=provider["icon"] ) elif isinstance(provider, UserToolProvider): - provider.icon = ToolTransformService.get_tool_provider_icon_url( - provider_type=provider.type.value, provider_name=provider.name, icon=provider.icon + provider.icon = cast( + str, + ToolTransformService.get_tool_provider_icon_url( + provider_type=provider.type.value, provider_name=provider.name, icon=provider.icon + ), ) @staticmethod @@ -66,6 +69,9 @@ class ToolTransformService: """ convert provider controller to user provider """ + if provider_controller.identity is None: + raise ValueError("provider identity is None") + result = UserToolProvider( id=provider_controller.identity.name, author=provider_controller.identity.author, @@ -93,7 +99,8 @@ class ToolTransformService: # get credentials schema schema = provider_controller.get_credentials_schema() for name, value in schema.items(): - result.masked_credentials[name] = ToolProviderCredentials.CredentialsType.default(value.type) + assert result.masked_credentials is not None, "masked credentials is None" + result.masked_credentials[name] = ToolProviderCredentials.CredentialsType.default(str(value.type)) # check if the provider need credentials if not provider_controller.need_credentials: @@ -149,6 +156,9 @@ class ToolTransformService: """ convert provider controller to user provider """ + if provider_controller.identity is None: + raise ValueError("provider identity is None") + return UserToolProvider( id=provider_controller.provider_id, author=provider_controller.identity.author, @@ -180,6 +190,8 @@ class ToolTransformService: convert provider controller to user provider """ username = "Anonymous" + if db_provider.user is None: + raise ValueError(f"user is None for api provider {db_provider.id}") try: username = db_provider.user.name except Exception as e: @@ -256,19 +268,25 @@ class ToolTransformService: if not found and runtime_parameter.form == ToolParameter.ToolParameterForm.FORM: current_parameters.append(runtime_parameter) + if tool.identity is None: + raise ValueError("tool identity is None") + return UserTool( author=tool.identity.author, name=tool.identity.name, label=tool.identity.label, - description=tool.description.human, + description=I18nObject( + en_US=tool.description.human if tool.description else "", + zh_Hans=tool.description.human if tool.description else "", + ), parameters=current_parameters, labels=labels, ) if isinstance(tool, ApiToolBundle): return UserTool( author=tool.author, - name=tool.operation_id, - label=I18nObject(en_US=tool.operation_id, zh_Hans=tool.operation_id), + name=tool.operation_id or "", + label=I18nObject(en_US=tool.operation_id or "", zh_Hans=tool.operation_id or ""), description=I18nObject(en_US=tool.summary or "", zh_Hans=tool.summary or ""), parameters=tool.parameters, labels=labels, diff --git a/api/services/tools/workflow_tools_manage_service.py b/api/services/tools/workflow_tools_manage_service.py index 318107bebb..69430de432 100644 --- a/api/services/tools/workflow_tools_manage_service.py +++ b/api/services/tools/workflow_tools_manage_service.py @@ -6,8 +6,10 @@ from typing import Any, Optional from sqlalchemy import or_ from core.model_runtime.utils.encoders import jsonable_encoder -from core.tools.entities.api_entities import UserToolProvider +from core.tools.entities.api_entities import UserTool, UserToolProvider +from core.tools.provider.tool_provider import ToolProviderController from core.tools.provider.workflow_tool_provider import WorkflowToolProviderController +from core.tools.tool.tool import Tool from core.tools.tool_label_manager import ToolLabelManager from core.tools.utils.workflow_configuration_sync import WorkflowToolConfigurationUtils from extensions.ext_database import db @@ -32,7 +34,7 @@ class WorkflowToolManageService: label: str, icon: dict, description: str, - parameters: Mapping[str, Any], + parameters: list[Mapping[str, Any]], privacy_policy: str = "", labels: Optional[list[str]] = None, ) -> dict: @@ -97,7 +99,7 @@ class WorkflowToolManageService: label: str, icon: dict, description: str, - parameters: list[dict], + parameters: list[Mapping[str, Any]], privacy_policy: str = "", labels: Optional[list[str]] = None, ) -> dict: @@ -131,7 +133,7 @@ class WorkflowToolManageService: if existing_workflow_tool_provider is not None: raise ValueError(f"Tool with name {name} already exists") - workflow_tool_provider: WorkflowToolProvider = ( + workflow_tool_provider: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id) .first() @@ -140,14 +142,14 @@ class WorkflowToolManageService: if workflow_tool_provider is None: raise ValueError(f"Tool {workflow_tool_id} not found") - app: App = ( + app: Optional[App] = ( db.session.query(App).filter(App.id == workflow_tool_provider.app_id, App.tenant_id == tenant_id).first() ) if app is None: raise ValueError(f"App {workflow_tool_provider.app_id} not found") - workflow: Workflow = app.workflow + workflow: Optional[Workflow] = app.workflow if workflow is None: raise ValueError(f"Workflow not found for app {workflow_tool_provider.app_id}") @@ -193,7 +195,7 @@ class WorkflowToolManageService: # skip deleted tools pass - labels = ToolLabelManager.get_tools_labels(tools) + labels = ToolLabelManager.get_tools_labels([t for t in tools if isinstance(t, ToolProviderController)]) result = [] @@ -202,10 +204,11 @@ class WorkflowToolManageService: provider_controller=tool, labels=labels.get(tool.provider_id, []) ) ToolTransformService.repack_provider(user_tool_provider) + to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id) + if to_user_tool is None or len(to_user_tool) == 0: + continue user_tool_provider.tools = [ - ToolTransformService.tool_to_user_tool( - tool.get_tools(user_id, tenant_id)[0], labels=labels.get(tool.provider_id, []) - ) + ToolTransformService.tool_to_user_tool(to_user_tool[0], labels=labels.get(tool.provider_id, [])) ] result.append(user_tool_provider) @@ -236,7 +239,7 @@ class WorkflowToolManageService: :param workflow_app_id: the workflow app id :return: the tool """ - db_tool: WorkflowToolProvider = ( + db_tool: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id) .first() @@ -245,13 +248,19 @@ class WorkflowToolManageService: if db_tool is None: raise ValueError(f"Tool {workflow_tool_id} not found") - workflow_app: App = db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first() + workflow_app: Optional[App] = ( + db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first() + ) if workflow_app is None: raise ValueError(f"App {db_tool.app_id} not found") tool = ToolTransformService.workflow_provider_to_controller(db_tool) + to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id) + if to_user_tool is None or len(to_user_tool) == 0: + raise ValueError(f"Tool {workflow_tool_id} not found") + return { "name": db_tool.name, "label": db_tool.label, @@ -261,9 +270,9 @@ class WorkflowToolManageService: "description": db_tool.description, "parameters": jsonable_encoder(db_tool.parameter_configurations), "tool": ToolTransformService.tool_to_user_tool( - tool.get_tools(user_id, tenant_id)[0], labels=ToolLabelManager.get_tool_labels(tool) + to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool) ), - "synced": workflow_app.workflow.version == db_tool.version, + "synced": workflow_app.workflow.version == db_tool.version if workflow_app.workflow else False, "privacy_policy": db_tool.privacy_policy, } @@ -276,7 +285,7 @@ class WorkflowToolManageService: :param workflow_app_id: the workflow app id :return: the tool """ - db_tool: WorkflowToolProvider = ( + db_tool: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.app_id == workflow_app_id) .first() @@ -285,12 +294,17 @@ class WorkflowToolManageService: if db_tool is None: raise ValueError(f"Tool {workflow_app_id} not found") - workflow_app: App = db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first() + workflow_app: Optional[App] = ( + db.session.query(App).filter(App.id == db_tool.app_id, App.tenant_id == tenant_id).first() + ) if workflow_app is None: raise ValueError(f"App {db_tool.app_id} not found") tool = ToolTransformService.workflow_provider_to_controller(db_tool) + to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id) + if to_user_tool is None or len(to_user_tool) == 0: + raise ValueError(f"Tool {workflow_app_id} not found") return { "name": db_tool.name, @@ -301,14 +315,14 @@ class WorkflowToolManageService: "description": db_tool.description, "parameters": jsonable_encoder(db_tool.parameter_configurations), "tool": ToolTransformService.tool_to_user_tool( - tool.get_tools(user_id, tenant_id)[0], labels=ToolLabelManager.get_tool_labels(tool) + to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool) ), - "synced": workflow_app.workflow.version == db_tool.version, + "synced": workflow_app.workflow.version == db_tool.version if workflow_app.workflow else False, "privacy_policy": db_tool.privacy_policy, } @classmethod - def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_id: str) -> list[dict]: + def list_single_workflow_tools(cls, user_id: str, tenant_id: str, workflow_tool_id: str) -> list[UserTool]: """ List workflow tool provider tools. :param user_id: the user id @@ -316,7 +330,7 @@ class WorkflowToolManageService: :param workflow_app_id: the workflow app id :return: the list of tools """ - db_tool: WorkflowToolProvider = ( + db_tool: Optional[WorkflowToolProvider] = ( db.session.query(WorkflowToolProvider) .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == workflow_tool_id) .first() @@ -326,9 +340,8 @@ class WorkflowToolManageService: raise ValueError(f"Tool {workflow_tool_id} not found") tool = ToolTransformService.workflow_provider_to_controller(db_tool) + to_user_tool: Optional[list[Tool]] = tool.get_tools(user_id, tenant_id) + if to_user_tool is None or len(to_user_tool) == 0: + raise ValueError(f"Tool {workflow_tool_id} not found") - return [ - ToolTransformService.tool_to_user_tool( - tool.get_tools(user_id, tenant_id)[0], labels=ToolLabelManager.get_tool_labels(tool) - ) - ] + return [ToolTransformService.tool_to_user_tool(to_user_tool[0], labels=ToolLabelManager.get_tool_labels(tool))] diff --git a/api/services/web_conversation_service.py b/api/services/web_conversation_service.py index 508fe20970..f698ed3084 100644 --- a/api/services/web_conversation_service.py +++ b/api/services/web_conversation_service.py @@ -26,6 +26,8 @@ class WebConversationService: pinned: Optional[bool] = None, sort_by="-updated_at", ) -> InfiniteScrollPagination: + if not user: + raise ValueError("User is required") include_ids = None exclude_ids = None if pinned is not None and user: @@ -59,6 +61,8 @@ class WebConversationService: @classmethod def pin(cls, app_model: App, conversation_id: str, user: Optional[Union[Account, EndUser]]): + if not user: + return pinned_conversation = ( db.session.query(PinnedConversation) .filter( @@ -89,6 +93,8 @@ class WebConversationService: @classmethod def unpin(cls, app_model: App, conversation_id: str, user: Optional[Union[Account, EndUser]]): + if not user: + return pinned_conversation = ( db.session.query(PinnedConversation) .filter( diff --git a/api/services/website_service.py b/api/services/website_service.py index 230f5d7815..1ad7d0399d 100644 --- a/api/services/website_service.py +++ b/api/services/website_service.py @@ -1,8 +1,9 @@ import datetime import json +from typing import Any import requests -from flask_login import current_user +from flask_login import current_user # type: ignore from core.helper import encrypter from core.rag.extractor.firecrawl.firecrawl_app import FirecrawlApp @@ -23,9 +24,9 @@ class WebsiteService: @classmethod def crawl_url(cls, args: dict) -> dict: - provider = args.get("provider") + provider = args.get("provider", "") url = args.get("url") - options = args.get("options") + options = args.get("options", "") credentials = ApiKeyAuthService.get_auth_credentials(current_user.current_tenant_id, "website", provider) if provider == "firecrawl": # decrypt api_key @@ -164,16 +165,18 @@ class WebsiteService: return crawl_status_data @classmethod - def get_crawl_url_data(cls, job_id: str, provider: str, url: str, tenant_id: str) -> dict | None: + def get_crawl_url_data(cls, job_id: str, provider: str, url: str, tenant_id: str) -> dict[Any, Any] | None: credentials = ApiKeyAuthService.get_auth_credentials(tenant_id, "website", provider) # decrypt api_key api_key = encrypter.decrypt_token(tenant_id=tenant_id, token=credentials.get("config").get("api_key")) + # FIXME data is redefine too many times here, use Any to ease the type checking, fix it later + data: Any if provider == "firecrawl": file_key = "website_files/" + job_id + ".txt" if storage.exists(file_key): - data = storage.load_once(file_key) - if data: - data = json.loads(data.decode("utf-8")) + d = storage.load_once(file_key) + if d: + data = json.loads(d.decode("utf-8")) else: firecrawl_app = FirecrawlApp(api_key=api_key, base_url=credentials.get("config").get("base_url", None)) result = firecrawl_app.check_crawl_status(job_id) @@ -183,22 +186,17 @@ class WebsiteService: if data: for item in data: if item.get("source_url") == url: - return item + return dict(item) return None elif provider == "jinareader": - file_key = "website_files/" + job_id + ".txt" - if storage.exists(file_key): - data = storage.load_once(file_key) - if data: - data = json.loads(data.decode("utf-8")) - elif not job_id: + if not job_id: response = requests.get( f"https://r.jina.ai/{url}", headers={"Accept": "application/json", "Authorization": f"Bearer {api_key}"}, ) if response.json().get("code") != 200: raise ValueError("Failed to crawl") - return response.json().get("data") + return dict(response.json().get("data", {})) else: api_key = encrypter.decrypt_token(tenant_id=tenant_id, token=credentials.get("config").get("api_key")) response = requests.post( @@ -218,12 +216,13 @@ class WebsiteService: data = response.json().get("data", {}) for item in data.get("processed", {}).values(): if item.get("data", {}).get("url") == url: - return item.get("data", {}) + return dict(item.get("data", {})) + return None else: raise ValueError("Invalid provider") @classmethod - def get_scrape_url_data(cls, provider: str, url: str, tenant_id: str, only_main_content: bool) -> dict | None: + def get_scrape_url_data(cls, provider: str, url: str, tenant_id: str, only_main_content: bool) -> dict: credentials = ApiKeyAuthService.get_auth_credentials(tenant_id, "website", provider) if provider == "firecrawl": # decrypt api_key diff --git a/api/services/workflow/workflow_converter.py b/api/services/workflow/workflow_converter.py index 90b5cc4836..2b0d57bdfd 100644 --- a/api/services/workflow/workflow_converter.py +++ b/api/services/workflow/workflow_converter.py @@ -1,5 +1,5 @@ import json -from typing import Optional +from typing import Any, Optional from core.app.app_config.entities import ( DatasetEntity, @@ -101,7 +101,7 @@ class WorkflowConverter: app_config = self._convert_to_app_config(app_model=app_model, app_model_config=app_model_config) # init workflow graph - graph = {"nodes": [], "edges": []} + graph: dict[str, Any] = {"nodes": [], "edges": []} # Convert list: # - variables -> start @@ -118,7 +118,7 @@ class WorkflowConverter: graph["nodes"].append(start_node) # convert to http request node - external_data_variable_node_mapping = {} + external_data_variable_node_mapping: dict[str, str] = {} if app_config.external_data_variables: http_request_nodes, external_data_variable_node_mapping = self._convert_to_http_request_node( app_model=app_model, @@ -199,15 +199,16 @@ class WorkflowConverter: return workflow def _convert_to_app_config(self, app_model: App, app_model_config: AppModelConfig) -> EasyUIBasedAppConfig: - app_mode = AppMode.value_of(app_model.mode) - if app_mode == AppMode.AGENT_CHAT or app_model.is_agent: + app_mode_enum = AppMode.value_of(app_model.mode) + app_config: EasyUIBasedAppConfig + if app_mode_enum == AppMode.AGENT_CHAT or app_model.is_agent: app_model.mode = AppMode.AGENT_CHAT.value app_config = AgentChatAppConfigManager.get_app_config( app_model=app_model, app_model_config=app_model_config ) - elif app_mode == AppMode.CHAT: + elif app_mode_enum == AppMode.CHAT: app_config = ChatAppConfigManager.get_app_config(app_model=app_model, app_model_config=app_model_config) - elif app_mode == AppMode.COMPLETION: + elif app_mode_enum == AppMode.COMPLETION: app_config = CompletionAppConfigManager.get_app_config( app_model=app_model, app_model_config=app_model_config ) @@ -302,7 +303,7 @@ class WorkflowConverter: nodes.append(http_request_node) # append code node for response body parsing - code_node = { + code_node: dict[str, Any] = { "id": f"code_{index}", "position": None, "data": { @@ -401,6 +402,7 @@ class WorkflowConverter: ) role_prefix = None + prompts: Any = None # Chat Model if model_config.mode == LLMMode.CHAT.value: diff --git a/api/services/workflow_run_service.py b/api/services/workflow_run_service.py index d8ee323908..4343596a23 100644 --- a/api/services/workflow_run_service.py +++ b/api/services/workflow_run_service.py @@ -1,3 +1,5 @@ +from typing import Optional + from extensions.ext_database import db from libs.infinite_scroll_pagination import InfiniteScrollPagination from models.enums import WorkflowRunTriggeredFrom @@ -92,7 +94,7 @@ class WorkflowRunService: return InfiniteScrollPagination(data=workflow_runs, limit=limit, has_more=has_more) - def get_workflow_run(self, app_model: App, run_id: str) -> WorkflowRun: + def get_workflow_run(self, app_model: App, run_id: str) -> Optional[WorkflowRun]: """ Get workflow run detail diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index 84768d5af0..ea8192edde 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -2,7 +2,7 @@ import json import time from collections.abc import Sequence from datetime import UTC, datetime -from typing import Optional, cast +from typing import Any, Optional, cast from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager @@ -242,7 +242,7 @@ class WorkflowService: raise ValueError("Node run failed with no run result") # single step debug mode error handling return if node_run_result.status == WorkflowNodeExecutionStatus.FAILED and node_instance.should_continue_on_error: - node_error_args = { + node_error_args: dict[str, Any] = { "status": WorkflowNodeExecutionStatus.EXCEPTION, "error": node_run_result.error, "inputs": node_run_result.inputs, @@ -338,7 +338,7 @@ class WorkflowService: raise ValueError(f"Current App mode: {app_model.mode} is not supported convert to workflow.") # convert to workflow - new_app = workflow_converter.convert_to_workflow( + new_app: App = workflow_converter.convert_to_workflow( app_model=app_model, account=account, name=args.get("name", "Default Name"), diff --git a/api/services/workspace_service.py b/api/services/workspace_service.py index 8fcb12b1cb..7637b31454 100644 --- a/api/services/workspace_service.py +++ b/api/services/workspace_service.py @@ -1,4 +1,4 @@ -from flask_login import current_user +from flask_login import current_user # type: ignore from configs import dify_config from extensions.ext_database import db @@ -29,6 +29,7 @@ class WorkspaceService: .filter(TenantAccountJoin.tenant_id == tenant.id, TenantAccountJoin.account_id == current_user.id) .first() ) + assert tenant_account_join is not None, "TenantAccountJoin not found" tenant_info["role"] = tenant_account_join.role can_replace_logo = FeatureService.get_features(tenant_info["id"]).can_replace_logo diff --git a/api/tasks/__init__.py b/api/tasks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/tasks/add_document_to_index_task.py b/api/tasks/add_document_to_index_task.py index 09be661216..50bb2b6e63 100644 --- a/api/tasks/add_document_to_index_task.py +++ b/api/tasks/add_document_to_index_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.index_processor.index_processor_factory import IndexProcessorFactory diff --git a/api/tasks/annotation/add_annotation_to_index_task.py b/api/tasks/annotation/add_annotation_to_index_task.py index 25c55bcfaf..aab21a4410 100644 --- a/api/tasks/annotation/add_annotation_to_index_task.py +++ b/api/tasks/annotation/add_annotation_to_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.datasource.vdb.vector_factory import Vector from core.rag.models.document import Document diff --git a/api/tasks/annotation/batch_import_annotations_task.py b/api/tasks/annotation/batch_import_annotations_task.py index fa7e5ac919..06162b02d6 100644 --- a/api/tasks/annotation/batch_import_annotations_task.py +++ b/api/tasks/annotation/batch_import_annotations_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.datasource.vdb.vector_factory import Vector diff --git a/api/tasks/annotation/delete_annotation_index_task.py b/api/tasks/annotation/delete_annotation_index_task.py index f0f6b32b06..a6a598ce4b 100644 --- a/api/tasks/annotation/delete_annotation_index_task.py +++ b/api/tasks/annotation/delete_annotation_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.datasource.vdb.vector_factory import Vector from models.dataset import Dataset diff --git a/api/tasks/annotation/disable_annotation_reply_task.py b/api/tasks/annotation/disable_annotation_reply_task.py index a2f4913513..26bf1c7c9f 100644 --- a/api/tasks/annotation/disable_annotation_reply_task.py +++ b/api/tasks/annotation/disable_annotation_reply_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.datasource.vdb.vector_factory import Vector diff --git a/api/tasks/annotation/enable_annotation_reply_task.py b/api/tasks/annotation/enable_annotation_reply_task.py index 0bdcd0eccd..b42af0c7fa 100644 --- a/api/tasks/annotation/enable_annotation_reply_task.py +++ b/api/tasks/annotation/enable_annotation_reply_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.datasource.vdb.vector_factory import Vector diff --git a/api/tasks/annotation/update_annotation_to_index_task.py b/api/tasks/annotation/update_annotation_to_index_task.py index b685d84d07..8c675feaa6 100644 --- a/api/tasks/annotation/update_annotation_to_index_task.py +++ b/api/tasks/annotation/update_annotation_to_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.datasource.vdb.vector_factory import Vector from core.rag.models.document import Document diff --git a/api/tasks/batch_create_segment_to_index_task.py b/api/tasks/batch_create_segment_to_index_task.py index dcb7009e44..26ae9f8736 100644 --- a/api/tasks/batch_create_segment_to_index_task.py +++ b/api/tasks/batch_create_segment_to_index_task.py @@ -4,7 +4,7 @@ import time import uuid import click -from celery import shared_task +from celery import shared_task # type: ignore from sqlalchemy import func from core.indexing_runner import IndexingRunner @@ -58,12 +58,13 @@ def batch_create_segment_to_index_task( model=dataset.embedding_model, ) word_count_change = 0 + segments_to_insert: list[str] = [] # Explicitly type hint the list as List[str] for segment in content: - content = segment["content"] + content_str = segment["content"] doc_id = str(uuid.uuid4()) - segment_hash = helper.generate_text_hash(content) + segment_hash = helper.generate_text_hash(content_str) # calc embedding use tokens - tokens = embedding_model.get_text_embedding_num_tokens(texts=[content]) if embedding_model else 0 + tokens = embedding_model.get_text_embedding_num_tokens(texts=[content_str]) if embedding_model else 0 max_position = ( db.session.query(func.max(DocumentSegment.position)) .filter(DocumentSegment.document_id == dataset_document.id) @@ -90,6 +91,7 @@ def batch_create_segment_to_index_task( word_count_change += segment_document.word_count db.session.add(segment_document) document_segments.append(segment_document) + segments_to_insert.append(str(segment)) # Cast to string if needed # update document word count dataset_document.word_count += word_count_change db.session.add(dataset_document) diff --git a/api/tasks/clean_dataset_task.py b/api/tasks/clean_dataset_task.py index a555fb2874..d9278c0379 100644 --- a/api/tasks/clean_dataset_task.py +++ b/api/tasks/clean_dataset_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from core.tools.utils.web_reader_tool import get_image_upload_file_ids @@ -71,6 +71,8 @@ def clean_dataset_task( image_upload_file_ids = get_image_upload_file_ids(segment.content) for upload_file_id in image_upload_file_ids: image_file = db.session.query(UploadFile).filter(UploadFile.id == upload_file_id).first() + if image_file is None: + continue try: storage.delete(image_file.key) except Exception: diff --git a/api/tasks/clean_document_task.py b/api/tasks/clean_document_task.py index 4d328643bf..3e80dd1377 100644 --- a/api/tasks/clean_document_task.py +++ b/api/tasks/clean_document_task.py @@ -3,7 +3,7 @@ import time from typing import Optional import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from core.tools.utils.web_reader_tool import get_image_upload_file_ids @@ -44,6 +44,8 @@ def clean_document_task(document_id: str, dataset_id: str, doc_form: str, file_i image_upload_file_ids = get_image_upload_file_ids(segment.content) for upload_file_id in image_upload_file_ids: image_file = db.session.query(UploadFile).filter(UploadFile.id == upload_file_id).first() + if image_file is None: + continue try: storage.delete(image_file.key) except Exception: diff --git a/api/tasks/clean_notion_document_task.py b/api/tasks/clean_notion_document_task.py index 75d9e03130..f5d6406d9c 100644 --- a/api/tasks/clean_notion_document_task.py +++ b/api/tasks/clean_notion_document_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from extensions.ext_database import db diff --git a/api/tasks/create_segment_to_index_task.py b/api/tasks/create_segment_to_index_task.py index 315b01f157..dfa053a43c 100644 --- a/api/tasks/create_segment_to_index_task.py +++ b/api/tasks/create_segment_to_index_task.py @@ -4,7 +4,7 @@ import time from typing import Optional import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.index_processor.index_processor_factory import IndexProcessorFactory diff --git a/api/tasks/deal_dataset_vector_index_task.py b/api/tasks/deal_dataset_vector_index_task.py index cfc54920e2..b025509aeb 100644 --- a/api/tasks/deal_dataset_vector_index_task.py +++ b/api/tasks/deal_dataset_vector_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from core.rag.models.document import Document diff --git a/api/tasks/delete_segment_from_index_task.py b/api/tasks/delete_segment_from_index_task.py index c3e0ea5d9f..45a612c745 100644 --- a/api/tasks/delete_segment_from_index_task.py +++ b/api/tasks/delete_segment_from_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.rag.index_processor.index_processor_factory import IndexProcessorFactory from extensions.ext_database import db diff --git a/api/tasks/disable_segment_from_index_task.py b/api/tasks/disable_segment_from_index_task.py index 15e1e50076..f30a1cc7ac 100644 --- a/api/tasks/disable_segment_from_index_task.py +++ b/api/tasks/disable_segment_from_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.index_processor.index_processor_factory import IndexProcessorFactory diff --git a/api/tasks/document_indexing_sync_task.py b/api/tasks/document_indexing_sync_task.py index 1831691393..ac4e81f95d 100644 --- a/api/tasks/document_indexing_sync_task.py +++ b/api/tasks/document_indexing_sync_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.indexing_runner import DocumentIsPausedError, IndexingRunner diff --git a/api/tasks/document_indexing_task.py b/api/tasks/document_indexing_task.py index 734dd2478a..21b571b6cb 100644 --- a/api/tasks/document_indexing_task.py +++ b/api/tasks/document_indexing_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from configs import dify_config from core.indexing_runner import DocumentIsPausedError, IndexingRunner diff --git a/api/tasks/document_indexing_update_task.py b/api/tasks/document_indexing_update_task.py index 1a52a6636b..5f1e9a892f 100644 --- a/api/tasks/document_indexing_update_task.py +++ b/api/tasks/document_indexing_update_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.indexing_runner import DocumentIsPausedError, IndexingRunner diff --git a/api/tasks/duplicate_document_indexing_task.py b/api/tasks/duplicate_document_indexing_task.py index f4c3dbd2e2..6db2620eb6 100644 --- a/api/tasks/duplicate_document_indexing_task.py +++ b/api/tasks/duplicate_document_indexing_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from configs import dify_config from core.indexing_runner import DocumentIsPausedError, IndexingRunner @@ -26,6 +26,8 @@ def duplicate_document_indexing_task(dataset_id: str, document_ids: list): start_at = time.perf_counter() dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if dataset is None: + raise ValueError("Dataset not found") # check document limit features = FeatureService.get_features(dataset.tenant_id) diff --git a/api/tasks/enable_segment_to_index_task.py b/api/tasks/enable_segment_to_index_task.py index 12639db939..2f6eb7b82a 100644 --- a/api/tasks/enable_segment_to_index_task.py +++ b/api/tasks/enable_segment_to_index_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.index_processor.index_processor_factory import IndexProcessorFactory diff --git a/api/tasks/mail_email_code_login.py b/api/tasks/mail_email_code_login.py index d78fc2b891..5dc935548f 100644 --- a/api/tasks/mail_email_code_login.py +++ b/api/tasks/mail_email_code_login.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from flask import render_template from extensions.ext_mail import mail diff --git a/api/tasks/mail_invite_member_task.py b/api/tasks/mail_invite_member_task.py index c7dfb9bf60..3094527fd4 100644 --- a/api/tasks/mail_invite_member_task.py +++ b/api/tasks/mail_invite_member_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from flask import render_template from configs import dify_config diff --git a/api/tasks/mail_reset_password_task.py b/api/tasks/mail_reset_password_task.py index 8596ca07cf..d5be94431b 100644 --- a/api/tasks/mail_reset_password_task.py +++ b/api/tasks/mail_reset_password_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from flask import render_template from extensions.ext_mail import mail diff --git a/api/tasks/ops_trace_task.py b/api/tasks/ops_trace_task.py index 34c62dc923..bb3b9e17ea 100644 --- a/api/tasks/ops_trace_task.py +++ b/api/tasks/ops_trace_task.py @@ -1,7 +1,7 @@ import json import logging -from celery import shared_task +from celery import shared_task # type: ignore from flask import current_app from core.ops.entities.config_entity import OPS_FILE_PATH, OPS_TRACE_FAILED_KEY diff --git a/api/tasks/recover_document_indexing_task.py b/api/tasks/recover_document_indexing_task.py index 934eb7430c..b603d689ba 100644 --- a/api/tasks/recover_document_indexing_task.py +++ b/api/tasks/recover_document_indexing_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.indexing_runner import DocumentIsPausedError, IndexingRunner diff --git a/api/tasks/remove_app_and_related_data_task.py b/api/tasks/remove_app_and_related_data_task.py index 66f78636ec..c3910e2be3 100644 --- a/api/tasks/remove_app_and_related_data_task.py +++ b/api/tasks/remove_app_and_related_data_task.py @@ -3,7 +3,7 @@ import time from collections.abc import Callable import click -from celery import shared_task +from celery import shared_task # type: ignore from sqlalchemy import delete from sqlalchemy.exc import SQLAlchemyError diff --git a/api/tasks/remove_document_from_index_task.py b/api/tasks/remove_document_from_index_task.py index 1909eaf341..4ba6d1a83e 100644 --- a/api/tasks/remove_document_from_index_task.py +++ b/api/tasks/remove_document_from_index_task.py @@ -2,7 +2,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from werkzeug.exceptions import NotFound from core.rag.index_processor.index_processor_factory import IndexProcessorFactory diff --git a/api/tasks/retry_document_indexing_task.py b/api/tasks/retry_document_indexing_task.py index 73471fd6e7..485caa5152 100644 --- a/api/tasks/retry_document_indexing_task.py +++ b/api/tasks/retry_document_indexing_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.indexing_runner import IndexingRunner from core.rag.index_processor.index_processor_factory import IndexProcessorFactory @@ -22,10 +22,13 @@ def retry_document_indexing_task(dataset_id: str, document_ids: list[str]): Usage: retry_document_indexing_task.delay(dataset_id, document_id) """ - documents = [] + documents: list[Document] = [] start_at = time.perf_counter() dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if not dataset: + raise ValueError("Dataset not found") + for document_id in document_ids: retry_indexing_cache_key = "document_{}_is_retried".format(document_id) # check document limit @@ -55,29 +58,31 @@ def retry_document_indexing_task(dataset_id: str, document_ids: list[str]): document = ( db.session.query(Document).filter(Document.id == document_id, Document.dataset_id == dataset_id).first() ) + if not document: + logging.info(click.style("Document not found: {}".format(document_id), fg="yellow")) + return try: - if document: - # clean old data - index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() + # clean old data + index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() - segments = db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document_id).all() - if segments: - index_node_ids = [segment.index_node_id for segment in segments] - # delete from vector index - index_processor.clean(dataset, index_node_ids) + segments = db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document_id).all() + if segments: + index_node_ids = [segment.index_node_id for segment in segments] + # delete from vector index + index_processor.clean(dataset, index_node_ids) - for segment in segments: - db.session.delete(segment) - db.session.commit() - - document.indexing_status = "parsing" - document.processing_started_at = datetime.datetime.utcnow() - db.session.add(document) + for segment in segments: + db.session.delete(segment) db.session.commit() - indexing_runner = IndexingRunner() - indexing_runner.run([document]) - redis_client.delete(retry_indexing_cache_key) + document.indexing_status = "parsing" + document.processing_started_at = datetime.datetime.utcnow() + db.session.add(document) + db.session.commit() + + indexing_runner = IndexingRunner() + indexing_runner.run([document]) + redis_client.delete(retry_indexing_cache_key) except Exception as ex: document.indexing_status = "error" document.error = str(ex) diff --git a/api/tasks/sync_website_document_indexing_task.py b/api/tasks/sync_website_document_indexing_task.py index 1d2a338c83..5d6b069cf4 100644 --- a/api/tasks/sync_website_document_indexing_task.py +++ b/api/tasks/sync_website_document_indexing_task.py @@ -3,7 +3,7 @@ import logging import time import click -from celery import shared_task +from celery import shared_task # type: ignore from core.indexing_runner import IndexingRunner from core.rag.index_processor.index_processor_factory import IndexProcessorFactory @@ -25,6 +25,8 @@ def sync_website_document_indexing_task(dataset_id: str, document_id: str): start_at = time.perf_counter() dataset = db.session.query(Dataset).filter(Dataset.id == dataset_id).first() + if dataset is None: + raise ValueError("Dataset not found") sync_indexing_cache_key = "document_{}_is_sync".format(document_id) # check document limit @@ -52,29 +54,31 @@ def sync_website_document_indexing_task(dataset_id: str, document_id: str): logging.info(click.style("Start sync website document: {}".format(document_id), fg="green")) document = db.session.query(Document).filter(Document.id == document_id, Document.dataset_id == dataset_id).first() + if not document: + logging.info(click.style("Document not found: {}".format(document_id), fg="yellow")) + return try: - if document: - # clean old data - index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() + # clean old data + index_processor = IndexProcessorFactory(document.doc_form).init_index_processor() - segments = db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document_id).all() - if segments: - index_node_ids = [segment.index_node_id for segment in segments] - # delete from vector index - index_processor.clean(dataset, index_node_ids) + segments = db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document_id).all() + if segments: + index_node_ids = [segment.index_node_id for segment in segments] + # delete from vector index + index_processor.clean(dataset, index_node_ids) - for segment in segments: - db.session.delete(segment) - db.session.commit() - - document.indexing_status = "parsing" - document.processing_started_at = datetime.datetime.utcnow() - db.session.add(document) + for segment in segments: + db.session.delete(segment) db.session.commit() - indexing_runner = IndexingRunner() - indexing_runner.run([document]) - redis_client.delete(sync_indexing_cache_key) + document.indexing_status = "parsing" + document.processing_started_at = datetime.datetime.utcnow() + db.session.add(document) + db.session.commit() + + indexing_runner = IndexingRunner() + indexing_runner.run([document]) + redis_client.delete(sync_indexing_cache_key) except Exception as ex: document.indexing_status = "error" document.error = str(ex) diff --git a/api/tests/artifact_tests/dependencies/test_dependencies_sorted.py b/api/tests/artifact_tests/dependencies/test_dependencies_sorted.py index 64f2884c4b..57fba31763 100644 --- a/api/tests/artifact_tests/dependencies/test_dependencies_sorted.py +++ b/api/tests/artifact_tests/dependencies/test_dependencies_sorted.py @@ -1,6 +1,6 @@ from typing import Any -import toml +import toml # type: ignore def load_api_poetry_configs() -> dict[str, Any]: @@ -38,7 +38,7 @@ def test_group_dependencies_version_operator(): ) -def test_duplicated_dependency_crossing_groups(): +def test_duplicated_dependency_crossing_groups() -> None: all_dependency_names: list[str] = [] for dependencies in load_all_dependency_groups().values(): dependency_names = list(dependencies.keys()) diff --git a/api/tests/integration_tests/controllers/test_controllers.py b/api/tests/integration_tests/controllers/test_controllers.py index 6371694694..5e3ee6bedc 100644 --- a/api/tests/integration_tests/controllers/test_controllers.py +++ b/api/tests/integration_tests/controllers/test_controllers.py @@ -1,6 +1,6 @@ from unittest.mock import patch -from app_fixture import app, mock_user +from app_fixture import mock_user # type: ignore def test_post_requires_login(app): diff --git a/api/tests/integration_tests/model_runtime/__mock/google.py b/api/tests/integration_tests/model_runtime/__mock/google.py index 5ea86baa83..b90f8b4444 100644 --- a/api/tests/integration_tests/model_runtime/__mock/google.py +++ b/api/tests/integration_tests/model_runtime/__mock/google.py @@ -1,7 +1,7 @@ from collections.abc import Generator from unittest.mock import MagicMock -import google.generativeai.types.generation_types as generation_config_types +import google.generativeai.types.generation_types as generation_config_types # type: ignore import pytest from _pytest.monkeypatch import MonkeyPatch from google.ai import generativelanguage as glm @@ -45,7 +45,7 @@ class MockGoogleClass: return GenerateContentResponse(done=True, iterator=None, result=glm.GenerateContentResponse({}), chunks=[]) @staticmethod - def generate_content_stream() -> Generator[GenerateContentResponse, None, None]: + def generate_content_stream() -> MockGoogleResponseClass: return MockGoogleResponseClass() def generate_content( diff --git a/api/tests/integration_tests/model_runtime/__mock/huggingface.py b/api/tests/integration_tests/model_runtime/__mock/huggingface.py index 97038ef596..4de5251440 100644 --- a/api/tests/integration_tests/model_runtime/__mock/huggingface.py +++ b/api/tests/integration_tests/model_runtime/__mock/huggingface.py @@ -2,7 +2,7 @@ import os import pytest from _pytest.monkeypatch import MonkeyPatch -from huggingface_hub import InferenceClient +from huggingface_hub import InferenceClient # type: ignore from tests.integration_tests.model_runtime.__mock.huggingface_chat import MockHuggingfaceChatClass diff --git a/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py b/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py index 9ee76c935c..77c7e7f5e4 100644 --- a/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py +++ b/api/tests/integration_tests/model_runtime/__mock/huggingface_chat.py @@ -3,15 +3,15 @@ from collections.abc import Generator from typing import Any, Literal, Optional, Union from _pytest.monkeypatch import MonkeyPatch -from huggingface_hub import InferenceClient -from huggingface_hub.inference._text_generation import ( +from huggingface_hub import InferenceClient # type: ignore +from huggingface_hub.inference._text_generation import ( # type: ignore Details, StreamDetails, TextGenerationResponse, TextGenerationStreamResponse, Token, ) -from huggingface_hub.utils import BadRequestError +from huggingface_hub.utils import BadRequestError # type: ignore class MockHuggingfaceChatClass: diff --git a/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py b/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py index 6a25398cbf..4e00660a29 100644 --- a/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py +++ b/api/tests/integration_tests/model_runtime/__mock/nomic_embeddings.py @@ -6,7 +6,7 @@ import pytest # import monkeypatch from _pytest.monkeypatch import MonkeyPatch -from nomic import embed +from nomic import embed # type: ignore def create_embedding(texts: list[str], model: str, **kwargs: Any) -> dict: diff --git a/api/tests/integration_tests/model_runtime/__mock/xinference.py b/api/tests/integration_tests/model_runtime/__mock/xinference.py index 794f4b0585..e2abaa52b9 100644 --- a/api/tests/integration_tests/model_runtime/__mock/xinference.py +++ b/api/tests/integration_tests/model_runtime/__mock/xinference.py @@ -6,14 +6,14 @@ import pytest from _pytest.monkeypatch import MonkeyPatch from requests import Response from requests.sessions import Session -from xinference_client.client.restful.restful_client import ( +from xinference_client.client.restful.restful_client import ( # type: ignore Client, RESTfulChatModelHandle, RESTfulEmbeddingModelHandle, RESTfulGenerateModelHandle, RESTfulRerankModelHandle, ) -from xinference_client.types import Embedding, EmbeddingData, EmbeddingUsage +from xinference_client.types import Embedding, EmbeddingData, EmbeddingUsage # type: ignore class MockXinferenceClass: diff --git a/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py b/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py index 2dcfb92c63..d37fcf897f 100644 --- a/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py +++ b/api/tests/integration_tests/model_runtime/tongyi/test_rerank.py @@ -1,6 +1,6 @@ import os -import dashscope +import dashscope # type: ignore import pytest from core.model_runtime.entities.rerank_entities import RerankResult diff --git a/api/tests/integration_tests/tools/__mock_server/openapi_todo.py b/api/tests/integration_tests/tools/__mock_server/openapi_todo.py index 83f4d70ce9..2860739f0e 100644 --- a/api/tests/integration_tests/tools/__mock_server/openapi_todo.py +++ b/api/tests/integration_tests/tools/__mock_server/openapi_todo.py @@ -1,5 +1,5 @@ from flask import Flask, request -from flask_restful import Api, Resource +from flask_restful import Api, Resource # type: ignore app = Flask(__name__) api = Api(app) diff --git a/api/tests/integration_tests/vdb/__mock/baiduvectordb.py b/api/tests/integration_tests/vdb/__mock/baiduvectordb.py index 0ea61369c0..4af35a8bef 100644 --- a/api/tests/integration_tests/vdb/__mock/baiduvectordb.py +++ b/api/tests/integration_tests/vdb/__mock/baiduvectordb.py @@ -4,11 +4,11 @@ from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -from pymochow import MochowClient -from pymochow.model.database import Database -from pymochow.model.enum import IndexState, IndexType, MetricType, ReadConsistency, TableState -from pymochow.model.schema import HNSWParams, VectorIndex -from pymochow.model.table import Table +from pymochow import MochowClient # type: ignore +from pymochow.model.database import Database # type: ignore +from pymochow.model.enum import IndexState, IndexType, MetricType, ReadConsistency, TableState # type: ignore +from pymochow.model.schema import HNSWParams, VectorIndex # type: ignore +from pymochow.model.table import Table # type: ignore from requests.adapters import HTTPAdapter diff --git a/api/tests/integration_tests/vdb/__mock/tcvectordb.py b/api/tests/integration_tests/vdb/__mock/tcvectordb.py index 61d6ed1656..68a1e290ad 100644 --- a/api/tests/integration_tests/vdb/__mock/tcvectordb.py +++ b/api/tests/integration_tests/vdb/__mock/tcvectordb.py @@ -4,12 +4,12 @@ from typing import Optional import pytest from _pytest.monkeypatch import MonkeyPatch from requests.adapters import HTTPAdapter -from tcvectordb import VectorDBClient -from tcvectordb.model.database import Collection, Database -from tcvectordb.model.document import Document, Filter -from tcvectordb.model.enum import ReadConsistency -from tcvectordb.model.index import Index -from xinference_client.types import Embedding +from tcvectordb import VectorDBClient # type: ignore +from tcvectordb.model.database import Collection, Database # type: ignore +from tcvectordb.model.document import Document, Filter # type: ignore +from tcvectordb.model.enum import ReadConsistency # type: ignore +from tcvectordb.model.index import Index # type: ignore +from xinference_client.types import Embedding # type: ignore class MockTcvectordbClass: diff --git a/api/tests/integration_tests/vdb/__mock/vikingdb.py b/api/tests/integration_tests/vdb/__mock/vikingdb.py index 0f40337feb..3ad72e5550 100644 --- a/api/tests/integration_tests/vdb/__mock/vikingdb.py +++ b/api/tests/integration_tests/vdb/__mock/vikingdb.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -from volcengine.viking_db import ( +from volcengine.viking_db import ( # type: ignore Collection, Data, DistanceType, diff --git a/api/tests/unit_tests/oss/__mock/aliyun_oss.py b/api/tests/unit_tests/oss/__mock/aliyun_oss.py index 27e1c0ad85..4f6d8a2f54 100644 --- a/api/tests/unit_tests/oss/__mock/aliyun_oss.py +++ b/api/tests/unit_tests/oss/__mock/aliyun_oss.py @@ -4,8 +4,8 @@ from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -from oss2 import Bucket -from oss2.models import GetObjectResult, PutObjectResult +from oss2 import Bucket # type: ignore +from oss2.models import GetObjectResult, PutObjectResult # type: ignore from tests.unit_tests.oss.__mock.base import ( get_example_bucket, diff --git a/api/tests/unit_tests/oss/__mock/tencent_cos.py b/api/tests/unit_tests/oss/__mock/tencent_cos.py index 5189b68e87..c77c5b08f3 100644 --- a/api/tests/unit_tests/oss/__mock/tencent_cos.py +++ b/api/tests/unit_tests/oss/__mock/tencent_cos.py @@ -3,8 +3,8 @@ from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -from qcloud_cos import CosS3Client -from qcloud_cos.streambody import StreamBody +from qcloud_cos import CosS3Client # type: ignore +from qcloud_cos.streambody import StreamBody # type: ignore from tests.unit_tests.oss.__mock.base import ( get_example_bucket, diff --git a/api/tests/unit_tests/oss/__mock/volcengine_tos.py b/api/tests/unit_tests/oss/__mock/volcengine_tos.py index 649d93a202..88df59f91c 100644 --- a/api/tests/unit_tests/oss/__mock/volcengine_tos.py +++ b/api/tests/unit_tests/oss/__mock/volcengine_tos.py @@ -4,8 +4,8 @@ from unittest.mock import MagicMock import pytest from _pytest.monkeypatch import MonkeyPatch -from tos import TosClientV2 -from tos.clientv2 import DeleteObjectOutput, GetObjectOutput, HeadObjectOutput, PutObjectOutput +from tos import TosClientV2 # type: ignore +from tos.clientv2 import DeleteObjectOutput, GetObjectOutput, HeadObjectOutput, PutObjectOutput # type: ignore from tests.unit_tests.oss.__mock.base import ( get_example_bucket, diff --git a/api/tests/unit_tests/oss/aliyun_oss/aliyun_oss/test_aliyun_oss.py b/api/tests/unit_tests/oss/aliyun_oss/aliyun_oss/test_aliyun_oss.py index 65d31352bd..380134bc46 100644 --- a/api/tests/unit_tests/oss/aliyun_oss/aliyun_oss/test_aliyun_oss.py +++ b/api/tests/unit_tests/oss/aliyun_oss/aliyun_oss/test_aliyun_oss.py @@ -1,7 +1,7 @@ from unittest.mock import MagicMock, patch import pytest -from oss2 import Auth +from oss2 import Auth # type: ignore from extensions.storage.aliyun_oss_storage import AliyunOssStorage from tests.unit_tests.oss.__mock.aliyun_oss import setup_aliyun_oss_mock diff --git a/api/tests/unit_tests/oss/tencent_cos/test_tencent_cos.py b/api/tests/unit_tests/oss/tencent_cos/test_tencent_cos.py index 303f0493bd..d289751800 100644 --- a/api/tests/unit_tests/oss/tencent_cos/test_tencent_cos.py +++ b/api/tests/unit_tests/oss/tencent_cos/test_tencent_cos.py @@ -1,7 +1,7 @@ from unittest.mock import patch import pytest -from qcloud_cos import CosConfig +from qcloud_cos import CosConfig # type: ignore from extensions.storage.tencent_cos_storage import TencentCosStorage from tests.unit_tests.oss.__mock.base import ( diff --git a/api/tests/unit_tests/oss/volcengine_tos/test_volcengine_tos.py b/api/tests/unit_tests/oss/volcengine_tos/test_volcengine_tos.py index 5afbc9e8b4..04988e85d8 100644 --- a/api/tests/unit_tests/oss/volcengine_tos/test_volcengine_tos.py +++ b/api/tests/unit_tests/oss/volcengine_tos/test_volcengine_tos.py @@ -1,5 +1,5 @@ import pytest -from tos import TosClientV2 +from tos import TosClientV2 # type: ignore from extensions.storage.volcengine_tos_storage import VolcengineTosStorage from tests.unit_tests.oss.__mock.base import ( diff --git a/api/tests/unit_tests/utils/yaml/test_yaml_utils.py b/api/tests/unit_tests/utils/yaml/test_yaml_utils.py index 95b93651d5..8d64548727 100644 --- a/api/tests/unit_tests/utils/yaml/test_yaml_utils.py +++ b/api/tests/unit_tests/utils/yaml/test_yaml_utils.py @@ -1,7 +1,7 @@ from textwrap import dedent import pytest -from yaml import YAMLError +from yaml import YAMLError # type: ignore from core.tools.utils.yaml_utils import load_yaml_file diff --git a/sdks/python-client/dify_client/client.py b/sdks/python-client/dify_client/client.py index e664488301..ee1b5c57e1 100644 --- a/sdks/python-client/dify_client/client.py +++ b/sdks/python-client/dify_client/client.py @@ -160,7 +160,10 @@ class WorkflowClient(DifyClient): class KnowledgeBaseClient(DifyClient): def __init__( - self, api_key, base_url: str = "https://api.dify.ai/v1", dataset_id: str = None + self, + api_key, + base_url: str = "https://api.dify.ai/v1", + dataset_id: str | None = None, ): """ Construct a KnowledgeBaseClient object. @@ -187,7 +190,9 @@ class KnowledgeBaseClient(DifyClient): "GET", f"/datasets?page={page}&limit={page_size}", **kwargs ) - def create_document_by_text(self, name, text, extra_params: dict = None, **kwargs): + def create_document_by_text( + self, name, text, extra_params: dict | None = None, **kwargs + ): """ Create a document by text. @@ -225,7 +230,7 @@ class KnowledgeBaseClient(DifyClient): return self._send_request("POST", url, json=data, **kwargs) def update_document_by_text( - self, document_id, name, text, extra_params: dict = None, **kwargs + self, document_id, name, text, extra_params: dict | None = None, **kwargs ): """ Update a document by text. @@ -262,7 +267,7 @@ class KnowledgeBaseClient(DifyClient): return self._send_request("POST", url, json=data, **kwargs) def create_document_by_file( - self, file_path, original_document_id=None, extra_params: dict = None + self, file_path, original_document_id=None, extra_params: dict | None = None ): """ Create a document by file. @@ -304,7 +309,7 @@ class KnowledgeBaseClient(DifyClient): ) def update_document_by_file( - self, document_id, file_path, extra_params: dict = None + self, document_id, file_path, extra_params: dict | None = None ): """ Update a document by file. @@ -372,7 +377,11 @@ class KnowledgeBaseClient(DifyClient): return self._send_request("DELETE", url) def list_documents( - self, page: int = None, page_size: int = None, keyword: str = None, **kwargs + self, + page: int | None = None, + page_size: int | None = None, + keyword: str | None = None, + **kwargs, ): """ Get a list of documents in this dataset. @@ -402,7 +411,11 @@ class KnowledgeBaseClient(DifyClient): return self._send_request("POST", url, json=data, **kwargs) def query_segments( - self, document_id, keyword: str = None, status: str = None, **kwargs + self, + document_id, + keyword: str | None = None, + status: str | None = None, + **kwargs, ): """ Query segments in this document. From cdaef30cc9e0ae5d944b95603842f41fe257b9d0 Mon Sep 17 00:00:00 2001 From: TinsFox Date: Tue, 24 Dec 2024 19:13:24 +0800 Subject: [PATCH 138/138] refactor: replace div with button for better accessibility (#12046) --- web/app/(commonLayout)/apps/NewAppCard.tsx | 23 +++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/web/app/(commonLayout)/apps/NewAppCard.tsx b/web/app/(commonLayout)/apps/NewAppCard.tsx index d353cf2394..a90af4ea85 100644 --- a/web/app/(commonLayout)/apps/NewAppCard.tsx +++ b/web/app/(commonLayout)/apps/NewAppCard.tsx @@ -18,7 +18,6 @@ export type CreateAppCardProps = { onSuccess?: () => void } -// eslint-disable-next-line react/display-name const CreateAppCard = forwardRef(({ className, onSuccess }, ref) => { const { t } = useTranslation() const { onPlanInfoChanged } = useProviderContext() @@ -44,24 +43,22 @@ const CreateAppCard = forwardRef(({ classNam >
{t('app.createApp')}
-
setShowNewAppModal(true)}> +
-
setShowNewAppTemplateDialog(true)}> + +
-
-
setShowCreateFromDSLModal(true)} - > -
+ +
+
+ setShowNewAppModal(false)} @@ -108,4 +105,6 @@ const CreateAppCard = forwardRef(({ classNam ) }) +CreateAppCard.displayName = 'CreateAppCard' export default CreateAppCard +export { CreateAppCard }

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 HweR2|!* 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 diff --git a/web/public/screenshots/Light/Workflow.png b/web/public/screenshots/Light/Workflow.png deleted file mode 100644 index a82c9a6a4d04b39653afb3497c0ddcbe912368b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22110 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 N002ovPDHLkV1g{>eUbnG diff --git a/web/public/screenshots/Light/Workflow@3x.png b/web/public/screenshots/Light/Workflow@3x.png deleted file mode 100644 index 914ce45003eb35bc60ef959705e47133d5a87b11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 147073 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