From 5bc5cb3db212741a23dfd5ca5963eb4e84712545 Mon Sep 17 00:00:00 2001 From: xilesun <2013xile@gmail.com> Date: Tue, 22 Jul 2025 09:30:20 +0800 Subject: [PATCH] feat: form filler 2.0 --- .../components/subModel/AddSubModelButton.tsx | 2 +- .../ai-employees/chatbox/ChatButton.tsx | 58 ++++---- .../chatbox/hooks/useChatBoxActions.ts | 11 +- .../src/client/ai-employees/chatbox/utils.ts | 35 +++-- .../ai-employees/context/data-modeling.tsx | 131 ------------------ .../ai-employees/context/flow-models.tsx | 71 ++++++++++ .../models/AIEmployeeShortcutListModel.tsx | 29 ++-- .../flow/models/AIEmployeeShortcutModel.tsx | 69 +++++++-- .../ai-employees/form-filler/tools/index.ts | 28 ++++ .../src/client/ai-employees/icon.png | Bin 19471 -> 16422 bytes .../selector/AISelectorProvider.tsx | 6 +- .../ai-employees/shortcuts/ShortcutList.tsx | 106 ++++++++++++++ .../ai-employees/shortcuts/useShortcuts.ts | 66 +++++++++ .../src/client/ai-employees/types.ts | 8 +- .../@nocobase/plugin-ai/src/client/index.tsx | 19 +-- .../Configuration/schemas/collections.ts | 76 ---------- 16 files changed, 410 insertions(+), 305 deletions(-) delete mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/data-modeling.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/context/flow-models.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/form-filler/tools/index.ts create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx create mode 100644 packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts diff --git a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx index 87221bb14ce..9d0de01765e 100644 --- a/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx +++ b/packages/core/flow-engine/src/components/subModel/AddSubModelButton.tsx @@ -23,7 +23,7 @@ import { buildSubModelGroups, buildSubModelItems } from './utils'; export interface SubModelItem { key?: string; - label?: string; + label?: string | React.ReactNode; type?: 'group' | 'divider'; disabled?: boolean; icon?: React.ReactNode; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx index b27376a9286..ed6f0ca4d7b 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/chatbox/ChatButton.tsx @@ -7,18 +7,18 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ -import React, { useMemo } from 'react'; -import { FloatButton, Avatar, Dropdown } from 'antd'; +import React, { useMemo, useState } from 'react'; +import { FloatButton, Avatar, Dropdown, Button, Divider } from 'antd'; import icon from '../icon.png'; import { css } from '@emotion/css'; import { useAIEmployeesContext } from '../AIEmployeesProvider'; import { AIEmployeeListItem } from '../AIEmployeeListItem'; import { useAISelectionContext } from '../selector/AISelectorProvider'; -import { PauseCircleFilled } from '@ant-design/icons'; +import { PauseCircleFilled, RightOutlined, LeftOutlined } from '@ant-design/icons'; import { useToken } from '@nocobase/client'; -import { FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine'; import { useChatBoxStore } from './stores/chat-box'; import { useChatBoxActions } from './hooks/useChatBoxActions'; +import { ShortcutList } from '../shortcuts/ShortcutList'; export const ChatButton: React.FC = () => { const { aiEmployees } = useAIEmployeesContext(); @@ -30,13 +30,6 @@ export const ChatButton: React.FC = () => { const { stopSelect, selectable } = useAISelectionContext(); const { token } = useToken(); - // const flowEngine = useFlowEngine(); - // - // const model = useMemo(() => { - // return flowEngine.createModel({ - // use: 'AIEmployeeShortcutListModel', - // }); - // }, [flowEngine]); const items = useMemo(() => { return aiEmployees?.map((employee) => ({ @@ -58,36 +51,39 @@ export const ChatButton: React.FC = () => { return (
1KADfalSu^;LehmpRCR%m6lHx-#(caJ_nvfQ&5S zI+vB{x3-6f;F9kT-gR|Fjtdp7y#4&i@%o0F>4n745_C!I!Cwg7ppXDUXF_T!cBC}A zhlhu*o?iRd7!f1}IZMm@j(2^KXo0*hEiEA)i1B9*_QZ>wVHp@0blv}cFdKwVNlB@c z%6(ZX{7PtiJboDN^&B-4Lc)@}r9%(TTLl^+M%~w_A}up)xC-Q6#=_!OdV0F4tt}{0 zTrlxlVu~FA(bToH@bF>_M3S^X@v}@+$X8QWfB1ajy!+PuVxl`AJ`khh8IOMdpq&Sr z^2e|C8V!a6=nwEXQQKob$^IfOEu_ngR!oc<5rASVD-mbJ8`2Vj;}QDM?kD|Dnc?R1w2`^NZ98yg$bj>nfL ztF7bbGoln;tz6a7A{J<9AW%#dvjpAZ+wRe~ZSQZcKUkv}#QegZjy# GJ zcvB5x`|{=I((f)jSXu0Q&LRaH?Kt}uPe(`RYikfS#3&3*T!h-xN$-*?N_dC5$n86K zBy@GjP^FQ>L+=Zim=nK)*?{7$PQ77^qZYdYvK>Hvrsn}rnweXkD<_~3&5Tb?4I9J; z53hG$yK43Eh5Ba7+qY<%S`D|^*usA)wk~!9gX{v4f)En6s;a6|rl`?eo9o`HdP;u7 z+OQS DPJ}j~s{TMq)Tkz{s_w zzEgE`pi)lOyO_5e9hBl;=e%I>#_^(BQ=&H%k2cOVG`>H+nIlW7^QpoF9|CrlJH$bP zsW(pHhOC#;`opjd@@3-kAa)Loj&?qwm12inBD2)fE!vgTmwaAX!|mjSX0uE3?KjJf zFsqg2+nzS-Ajj4@Ooi3g^LLeJfK?C^6BiW~wSk&vYGZ>1R#;}#L<02*fZdpB{jz{E zh zRwsU5NlD53skXLYX)T_pt!Pp Hq0`T29-+C#7|Usg|*+ni}3gus@XN)Axt zmWbk`6JjG&^$BXK4?`~Dqg4FEUsF!QRpxSA-ooH2Ik3GQWAB`$_?lST3kS|jeoz$d zq}Hs_u-~EooYh!RFmOxY=1q*@VGmUTAKF-_UK}(VEILVF-^Te(z*@c`ZIBMl06%~? zB#v;$#KvZW>Q+<34aSgLQ$uu>o}?32@Pf~Yiq)v`G0-3sm 0ur~o@+jhW;sfG(W zn1nEt%Yt%)VO)ALuN#V*_M&jdC44IG`dz5&OWkwfsHu5835AHavz2HlE+lIu8{+fj z%a?foL$VNRVPDYX 6zOw`3TP$m2WoKO+w>_cZ1z2+W(V4p&TTdfcj% z=7}b$ob7wh)>cHU6;ymPD5ghg2tuK#-jEeVYuDH90Y^(hg-6CmB=Cf+xVTt3tgruf z<||7mLsFK0y~e20*59v;6gYVVCzMxMIHw?w_48}XrIeVgf`T|Gut^$)#DDu{dIE-z zw+w4XT3d(n*iD$C&dq9^NgGz=^kd>xGcdLmC`ZNvC}N5CJLV4S(`qrbuyM|QoilHH zPtPY!D7 !Bqk=VRS$~hLZ$USRC-@pL*HzTmlJ_ #ZXki7awazKRHU z^gL}hRjH|}{q4M3Nl2b67h7hXw1>{d# gFv5g0h3!LzHPO5B6Fsk}Z zO-)T9=#B+lX|ayKsYpn|P)82PeP87a@!*nszeLd;d-pa=r_uayagD9WS4IaMo4=c9 zL93ecz}0#-TwwxL>Hu;~OiZA4sJsO{Jn#OUKl)7|fC3U4`t<2iNP8P(o`l52(JaKC zvxf}6jt(G^rkDIa9W307JG!d)2mNn0zlx2QkvcFqFd^vayOgikK)6ClB>p=e7+;76 z2-bk34G>?AyZ`}avc-=N#pwq{e=Aif`%n8Td{2qyxd85yA>#gMgUdzfW#>c2xErs$ z_P_M|Og9Os9D08r8;}aeeHf>*J0O4p0;rNQ)L+})zsG{w8qCy`Gv$ebofm&!f4_{O zAq7N3l$r {sc-rn8^uc?EQpcet^SjC0z_jPm_0gugZd+)vG-Rr6~>%xB8nj>Q$ zb8@m$ g9x93%)fdpCZ+*9PbW|}3*e8xO9 p+wZg~mq$g5&?AZMeodI>D^e4bHlG&AdtkZ02p6&q};848v z(IuP!Khz+gj*Q&j@-88Wl)+<* (@z_DQi8&p0L1-5ZVsk0ohA z*}Os}9DyR%6jur}qKVb)SOb@OiPyi>;y_0B-dVg1_!pg#krAb{h=@=E{l*KNB(I9_ zVp%VE8OF-E(_5Ryg?WX-0*HeSAOr$N)r`l6tl`<&v`R`!Z7aLk<>hfu2y6TDg&g{! zqH?W`yP(lZm lyi!`N6n$LYrLRpd3`nEkIAz4ulLQRQgxDq2RyO8|=A4}1vI zWTM`iL2TZj&E_-qlN;t8$0) <^E3&f}Kj=5nsM4V1BTsQRpL?%@C|F_YBrf zLraTx{rdIWw{MHzyLTC2yP3IpmE9*+AhXJAO(ev`?I86>Mn+cF)JTK+hw@AUg5Vh3 zr4loe=+Mea_WGu#y8%?!u7zY}(Y)*KMpf41;^Lg0oyFfcPPZ*=R|4EypE@v5^0Zn$ z+51>#SD|&!Nif1@HT58}^;Ed^bDHljcecH~9Xb%;u<`LLxUzgea)Kt9SP+!tp~CO+ zJd#>0Pzi^Gna0G#1VB)}s_4;UUWYHzb8~ZhtHU=_`CaJnDEQTo`e&RFp}6fPn2+b9 z*&-rrhO#8`a&x5&S(6b%g s1QXS*xX(lv01_`6 MVcS zA`-H?c{TXkhF|xT!4!kT=epW<6CcnuK=&RVE7rIpCT3#$A`JTS94FEnK*Io3AqSRM z1LZr+pru)dlQ__WR`vYNLTte)Ap>euGfC1V43V1nt5-Y%Z(b{{cuR+ag9D+8ghjV9 z7*)JK0zzG(d3_Js1rkA&0KtqW #VU^h8c{Hp4zegW@8)5MF= zsg!&98p9K)nxMp$J%GxBNqbXHtqCA ?PddHFiU4j}E7+>kvMuALR z42^f(mc2xcAlxNd3izJf_VL}zE)t_l5 jZ9 zi?`=zeuA!xSYYZ2YPa(~S6h$1w(KKHZHHb*CgS@bWdUQ%%U4Y1Jq+ v`j|0#rC;A*fO4!*e!75PfnMmX5=!>a$B&2h)0dN5dd_yAUoFtp8BTpIc1oM z3xY5e%HFB~hwQOA^RlC(BQ+^V+mm;HZItozXy )B DxN oRbvT#9c}N^pYvD;6e?K_b1G>y` zdj7ZM;qh^;XFq;zOx64DGc6Bh;zMu5&6_uc{eFQrcm#-n5kr$;68$B_3$Y+kI38!{ zh-BEcuKv8Ww`2Bxk*soYSM_55on5=2S`+*WK`)G+aM^CJM4RwM`Wiw{7w8QTd&&-= z=TEsAY#VL+~LbS;r7#L7Qctc<4FQlS1K|ehsUozAc zZTfXeVS_5VrWK&)HYF|XQ`%){icW!2Fto3!L!6!$EU=qy@^W&QbwAufeh18Nb~74a zF+)>6@SpD%W+#-jU1Z|vI>nqrc5O!~)mC+@3*3*D7;;?}x +@%`DnF|b1Amx8qtMp){vNXGLt3Jnmr#kCh{_7?LGATTHS z9yDJ8G6W6-D^y+Eh2`3dX3o0Jzl&VZr|*;nO&Zgv-udB#(_x&Ia^}6v{@*d6fTqSB zlH%1O*jQPU6(P=M0eJpW?{X83hLn_)>Go~4)ENLAN>CXs<5K=OFi-%2E;I1#Xa%zm zq(2$(_B)v0WL^ni DfZFYi7NiMg&Nt+n#z4Lx|i*jMg zraq0&Davzwa=3EgR1qAC*{G37HA7g!!os3u1^7QXENrhS;CG#yjm_$f%}n@;2Pm39 z2-F#3FRt=K22kcAN1k3y7xQ{MEb*m}K)w1(#uXf#P7s5jqE+JV12powgoMNk0XGI_ zmLHi#=5}_`Q0kMVv{~QSpe4Y1W|00?y82?4aLbE;!*|0~ipi{6w)VaB3Gz|+)S~|7 z1qB74{iazA>TsbF% MkqYF;%|WZ`S#OLbXn1@a8hYo{8-#1@8y8SwqM}TM z-wx5`%l$$%QgjJ5lN~EsfCPT6f5rf(2D8_;C4=gweExl5zlkkIK%`*_2?-t=M?40I zKrx>@6VfTxU(|tz$lN(7%#smaz3S= C;w;Wmmpc)!QDQO%UF?Mot(s+1yxXQ!V z9|k1$VMqwZw&{o1gmziUFKcE2p4mlspJ|bi_oLP$WYl+8YrajjG*2Y5CJP*4xmN z8|#^dFtxJ6gyzB0f*rd$L^{9UgPlni>Ib}a5+O 7SjRE%H-qroMgD5KxTt{I=mg((h`u!5Y+$rvatL5d(&hp zR?Iryy4Z_%aj>x)QO&)r*0#2v4>>4;xQ>sH-&t^SL0QBCY9s|}tYse%KfyRYTCJ@p zD&3XQ@&d}H_f=dMk@fmDrxrqa^ZLP6w$k{8g#`#dC6kXEIY8_}6$KLHFF-uVM*_x6 zWmVMJeHT&cH6Dgf?rZNfG$Xsx1~}j{d{hEwbaa%O&%F2KjT=B!A}_ClN$9W)8w=p1 zJJw@J^jKS(;5zC4pg6sTr=xNnsgr2b)>B=WLY9^?8MxK{Ubb^%>W%6fsQ-+@2RQ6g zpEU*{KsjXx3R(^L-*)u|JDL)bGbJQEj?9?He`GabL$}#8_a2&eDw)%Sx=v4dam!X5 zl?c<;_V!x0RrS<~Zv8~t$jDVl6sFCSlI3XLh>2fd1?BphTq*6Q+@1y7txshyLib7$ zbn3kYVLSH9Dx`9CJ-tW}hu=7^eIbC)1_T5Ibdl7=W(`9r)(AvqS_B_v$Z)wea}AGj zOH45+Lg}q4UmTx+w?nT#rLV7(u(Femp`js2%{PP#S1 2ng;Tbcy<)clre@JY4XV;E{dUQt0Dc-V-Lc2hV0bQRD^0%y!Y z%?SR&<$dPhzCK9^aIpXSC2=DoN(c;%%p^ZH2oQ*L&@IXC3f)+-1l5Ix0P8FX9n}rL zGMLGyh=8)!5D-=6E8*r}uE^N&krM?2Iduk6LWobR3W_b*(|fIW&|(b^ZsoJ;%1@!I zt84RGw`^cTt;RD~0u72IU!nV#ujKc|2XC}_I5{Q5uds{+pP56swV&5`cw%A#ZaKH% z@lv#PX346oRGXfd(7q<#$=g>njnNAFpOeM&)2p}$S42|K($qw*0*KV)(wfuqfl7!j zM8x3A59sS#WxBjuk*|Dk$-74^^Zc3i56b`kb6My3l7kru-bJ@}QJ>_$C!-==E@}Gc F{{aX?4~75$ delta 18254 zcma%CWmHvNw7qn9cO%^`sWeDRcb6zAT?Y`P8>B |IM z0B@2l0{#E_Itf98(MrJ9N`#l+iqn$Mf`?Op*T#ZVM8J}V(}K^IU&KPtMo>stBnbtP zjZx4>SVWLlNRZRY(#D2Ugik<#Q-oJYfK%9lSJcW@)Y4X1$Tmp_k&%(t+R|1?h+l}) z!dj4zQ$RpKkW H6=tE8E{8zkkbq zN>D_U!N;?lF>O-lu#fVI`KhJDIl7ehvAns-&Z J4*59k4 z4>_*iJCr&es#i)zCH{*4CCh!d5#OLSPQXEhgTat7r2`fbFavoe@hvqOp%wE~R0R1` zg7GJ~OELKFaJ^u>8lB3d6^m5VdEa*1!cyZ(jrKT$wsU5!uchVjO=`=%d3%h|FgxB} zpU_JZGdNf`X?*PGK^RPNis4)MZzzE;gjjKh&^*r#G14CvulP~lkAopRhHMJ{YyZ&b zqd8H4KGWCm9>OoQ@hDzHkfL0q3ewv*il}*mF@7(-e-t;&tHG(3Ap{Br!e9t5NQ0c6 zQBl`%VYKOrd~9+D2nAH~=;A+n&)?0musRUE)AV!2`_dkPdrYhQmEHpx?Vii>vnxzK z(0)Std0>!mh)j&pth#E{ gg!4n>(T*`i%%lN6^YEs_J8)jlMoe$YTi~Ut}$KQ7R2FE^9wWEar~%ksbL} z{srN)2{#uOt+~N `MoUaKs1Q#OC+f zOd)7smY*ZDaH7x0MmUP%W+3>^|5YFODm_J``NeA42B|I$k+$hGTxP~_%0IR_mKAB) zWH_q@toU>y3!=TPb+!iicl1nRq)~K41n~(`;oX$3^d;;HPz5pvo6UezvQ z(N|BY;a3k%lC=+haH_~Sa3RkxEldna7{AMp$1Akdlwk8#tW5L%pemBWM#jKYpK=>P zt6lUUQ;gpG9-_=EERNAF?vPgg6O%kZic>x$ebJe(R-UoI;;n@Dg*F=Pgv5rPF7)}2 z88Lhc_|O`r_rjJ8T9$zp@UU9)4fGye4~?QY-V_dwr!`|~4CQWT*cMgc5nAyfoglw? zs0iu|(I5lP3!9u$_8}^dTVgGp;BT=nMo)qi8MiS|l_5$D# By$ZO@E8?SD`3)9l>&htQ^u;VyqP w`s?mKVSj3kpXrZvl`64?DEe3lj!cZf=t56=c!mJAtAF^z#rGvP&~8Y>T~ zYD$?4U00LCkVlogl|ZYMs#gfpfnJx4tiG(dc_8%VYo_#@%IlaEV>aamsy?gfmf;Re ztVrw%CIc_^fWbs% BzngQaaqVwA|TujfF$)s03H5&4pd zIhqmNC-xUfVw`Ym7>onWMiwi~MGlUpWl=stMC+%CHSa)L$|pm=e%~qq#@} #Gw$;=EQ=j0qMLF^? za(RCzkKdO{4S5~7>PM}ur*L`wyNmX3@0%2%$p%&b4|?jPUxJ-=Y?D%P-_i>%%xfo5 zlfp^gZwy80-utS}bTKNO^7vqq^1$A7kB#kTdoK5oO8roL;6JMFnVrlFK=@*!a~g@I zQj{UW{n61wX@_=zP={9$@26(wu)9PD=N_lpD 75^)fQfk}pS!B4lxHAGf%~8+Z_Alqhlx-${ zt@uguTD(Qo(lp*PJzeEi$g;CzLO-Ry*X_L0qE1&=7^feX1|}b=N(n{yFrZ7;Mfp&; zYDR=Q4x%E^4v&qQwcNKcN!>3kjNDwGKpSvyaPIqaq2zRIY @*~Ucz+USv z61Ac(KTQL{yV`Z3RdB_>tAAt)4fZ`t^lZ-PU5^>u3;UDx0bbq`sa$^Fo+~p0PDppz z`MY;8`KE{Bj-Wf{z!VFjJdd-`K)gmuu!T*u6|c#JAW`PpG+ly`jDv@MrHXajg&fm? zY9HIDu-!K}6^2_`ET8b7uItR=A?ZFCjD>}zw6YQt2ZtmoZDM<7W~RTtzrDTv{@yRT zPOYS(y!`a?Qtj0%i4WW3ElL~TO(3zAW;-+7rOi=OvINwuxc-y6Ow4Q{0&YUVb} At56A*4lb|b2D3^$4yV)J3Bi&H8u4{s0;Esp{J)` z2Nk@wwx+(GdwIB^fVyokoo}(qlKd@-@7ZysbB+dD8apBx1?HDgOX)gRF}eJ&2vjyM zr9w2)@m!a&u1NeB(U_HNIka|*gV;#nPWoC}mO*R s&%C8k@@joLLG3V=CGyMu9OvW{$W#Q&duFzq8 %pVL=R#J%5aRfp^h`ZR!gmfl zkBNy_1ylMQpY!wSKlb3_<0odPP_2^j6h7h7)i9xQ_PuiNjX(-vWwnkA<#pe9&1O2I za?tz}PVTf9IU`#8ym$E?*|L$a7N@%oCwS^BL8*(;mv~Z86C7-ggp`T_&lnjgpL &|bZ4Q#&iCrb|72Y*))Sjn zpzzrIqJ_tXbG$l4#u9ELxS#FS$GtH6vOMyt)%yB7`YjloB08FRaiVrCo3=Syk|5)r zd{7UbRx)FXi(6F6zr_9KH{^n@(P2{?t34kTVky%ld_81k5h0ef!5E6Aug?7BF@M@T zg-J0N78Y1pSsff4P`|wW)5@F5DkLN%CbnoVphofFng_x5k^k!SSiy=YhixjRXb*HM zIiiT{<)h!&J9qxB_8TlT%-1lcHK8SFH(L%LVkMQ_xCx7+EN%?1y#9(^M6;QYxbM z=c Uj!sWp{fFJhK$Lab}M4Fphse^vx53yat3W<*!l z({KXAg^$0hEAL>7QWCRCn}_r77T(u5uCA^Xx>Xt1kNs*?&*8cOQvIqjYTgRtj6tAi z_+IgMi!_)tI24n`HxelTZ{VdDVlbt-hN)J+xze`RH$ek Orb11<>nkt zSR5lJJ!#Avwd45cKN)Utde7r{Sl(zyn;Q#i*Jx;rx5-%_;(m6~^o|IdVh|PezPmg; zTx>#ssjI6S9jGKSnp#GaaGPdjXTJ)tigw2qqfw`cc~4Xu{7$Z&t!R;Y`DTUf2munb zO((W=RMsRY9!8Q)M*JLZ`^@QWT{{nk=0RXIRUJl%i@4^PAEU270RikyV{jPLDf(bb zbqzP{_qPv!!Jy8`$$_n|uI81W&y;B=BqTU5eT|8XtT(KJne(Neo}S*{-wVB4rlh7` zZudTuiz2R*CVy?OkuR%N*O-@Giw}K!;Ybigp8GqQaNh2GmAZsYwmZKfqzsXk9xhTp zgPaIF ^gWp`kDy%l>eHQgk5!k2in0JFj2J1p>?n z4GkqCB6^h}pj0vq7g$ih0P7zbilY!diV ;9d(_IB%G3*(hp>j$#xfYXNe6K;5E3<)KIpuO)#*wL zpASbM=8iA;URPeu#?C&rwB$RU#>;8g2&0(Xzle>Ez1W+zDpx9)Qq2}~2e%(Z!mXyH z xn)-?9TEGjibj$hj+#!@de#6%TbPOdAxDVE8DjbQV(C%cz+k5YC-j`s_} z=b!wWT0_5tem5oQz7|y(rPlRj9aESN9K53W{f=tN=Vzf(ljCf#<0=5~veHuf`C2;I z=H{lCuWwjbn0h*&%|Im4bI+rG(&u7g8Jxt+foYh8$j?m6S)T39;Z?2p9(NpfJuNRRcn?OCeMQ7QbJeRcmGC_J{co*rV||@m zz ?aoKiIe982P{QBk0>L)LyOJtpzD7Y*=oY>dou?I zZ)j+8gL_}-b+8|k>3W7b+WAEEI5zc51gsuzZRC3zG qLnJho-EIbyOvEsj`Y(;hHt29sPi!~8sC#%YMqk?` zqxLi!q8T>5fyib`6J`x 2<)}`hZ~yvtR~{&?ceAshC{c@M&FGWmkG^0!tPQ1^?Sp}Z z=1QkwfJ=M%dGQtR*+ n;haXS4~W`qjA; zk0a%9j%{ OP0YTv(xw?I#lu$I IyyRV(x#@SZw{)c zvwc|vibksw)U@OL_j7hqD0Jbwr~99IXx6|-4b(gSIsId_LYpDmxWG}Pgo&ubNBA;7 zPoo? RkMLEe_nbbz~q3o+K|yL}Y>_ALPGeg+435rF#- zsi$@T^Knv-BDA!t_cv!?N*@B~`BiPQ)cmf?Z}&9fC3Dcch;wd{Vr_`k zxN32hbn!b`QQ!Hfgh_m(+|4wl&zq>^- z<+?x1oX>fIG28y9j-iV2SLRCYUKr{n<+W(bs|KL*_G ZIc};%@^##H-h%1F9cv`vEZTh-BT)) zc8lk2B;;T2mvV1DvguYWzWD%))MfnFsmNf8NoYw&FSkY#(W=H(Ba=}$zDYdcEkLW3 z*kaeSwAAK*cZrRS4N#W}tqt^erSVsvlP==tJCt5mti$bbuD4^2G;@!4roTJLdGAU7 ziE|2aN)#zvE&JF>G`NzwLQ$jQG>PBVw9yp5WQf4y&Q10(eB*QNO#Sj{$?Z$Dmx7%5 zWjI2;Hl44m(Ruksc6tJ q{5`15 zJ}FnB2cA(XEfiA{_mg#fg0xqHYu6(L3IR)Ip-kHf!IXZQ*&h`lYWN)*avXk%*g8tK zBo5+Z4mqS|E;c=U#I{fAR*nVE{b&fdM`y~($+%gvIG#8aBW#~9&dyX+RS)LttN ID8sibS0yZ3b2+&H+MH%1Is^>wq1 zyB%S$TEj}>s4a(|(}l^&B VJD4 zMibETZl!a6Uf-ju<-ZpxOsHnD+|y%C){Y_XB_!f=I$Xxj`y`rE5!%05*nST$)dhPC z7k}_Hsc1B#i2e(6Y#R ei*oczW0x6Fm({m)ZX4ZUL zGOG|3 828RHP}wP$bde1IIlFB>|fGNyf85~o@Y^1;93^><>_UH&wel5 zyO`pn5J#@QeOKzyt$Oy)fSfpLYCdgygXc;i2v>Tmn}tve)p(*&NXG!vdPyb9BA)Sn zC`F2x%f# WTV?cKW=U8m-5Fo6eSsoLIejf$DNA1!zIZb7Fhe kX1Kx zQrU5LK*eXSr=6r-?AW~ )9>c`M6cG^t1n|ZFERBFm{KA{g_gkk&FQuv$8}p%> zAOs(*7WZQxORe=4dny=URA6X<;b6y?etUH+1wHzinXQbEkEczSO!M=39xW_ OWM}(d53jL4 z-O@@4cQv*jTx3RthyT00{5~>*hK6=?(D1{s$IxqH9Q&Q Qb z)=VpFXiSbIJ O znRNB!wUWbbZ_!@=ErjRsbb5A5^ZA=EInbsO5)uy&4;>vHaI4@tMn*>H=;%CFTD{M7 z9h6?N*#mmIF&M*7is`)AScZMf7Q_Rkb5!;AEj1k@@-55x(^Uj`S0{}j1}b8~5Yxty z=r9v4C1DYdarQCJ^>{*Z^KA{=6S|yqH#IN4y}kYX?)RFU1Plk>6BSIQB`3e8f}`#q zNYzW$)BD>UgwXtMMNQY-+Qz2Jv_p)S*A=i-Fszu609JcE-rsmGdk^*WKx;pXRDg>0 z@br8)Kl#)#Wo2S)V!)i_T-Gh15>u7~7q1f^AJ+$?b@lCzu5G#1tR7q@i!|SVWanaM z8;#4lZffOy(lfvM(N+>5QY7b9>c!8<$S5lMtyPaXn&xWyd$vtc5pbg@fb(;CxD?Uj z;_mML;R6ObdSpZdD+dSE4tg7yQZ!Nll~eZk`1nUFUB^d9Z|n3eEiJXQv~nc<$pIyd zk6%4J{8Oe~vOST3MJ|9z7Z4f3_)a{ZjyL_H2pMMoTJghFS_+*vx-{)L+ETTJ?ccp& z;n_I@qGAbkl?uO$D+wAD6qKle&It}SuGr;9*O99ykE(&cR1k&Go6=FE@JY7(i*{&2 zg6xA2Fel^->VWVEUH2nL;_UJ=HauL!>t9}6oH{U#T0M_Jy)8F7XoIe=tgIXz9R&_S z2k_B=1_UMr_~Mn4(#gq51o-_|Dk@&*rL`g}3$sXsd!b)a%!#bFE5<$(^O(zS@a@6s zASG`dzVyG$It^BAB&Z=RGO-9+)7povIb2*^K-vK_# NEmv0GgH!xz)=ql2>?>cuDeP?IBP$_A0auSq+kB^U-sHlTLhR5L|G?6O>UMpNLUNLLG z^}t3&{C9rNcq2k29&qmkG#lvahuh8tfffT-*OPR9J5}bSf`Sc-ckNSOUWS2x?|->q z2i&ZMMu+K2g9ey6kbFSwWD7YP0;^_yeLX)vADdb{EhUABl(Yp{j2I+bs_A@1m6fW! ziHwzD-H_WYQW}{?H8L(1EE67qoQOaruYoG?!5#yxt4E&B0x^*El!}t__n$u$!f(Za zXBC8il9&k|HPE?&z6C%LluUp7_O0&u+qZAQbj#thi8O(Sb;nQ$x1R3|s@C M +&QE1fszK}12})`x1%XG%2zPl1EQ$ZzcKE&!KQr$QG`ssxZa z*y{1|)A_D;jQoH0^~2@TJ5ZAE+D`_-{cGyz%(l30G&VL?9#D2HrwnKa#Y$yU5>slI zoAD&}@xJ|xMsGk#yF-S!m-)U_&LL3Fow4#40xVF>eXkPKD4=g-WMo&+@e1^BHHec< zMOhg*P6F1$@zW*hB!q;ucc5~F$YZ{4nV6WsnO}8E)UdT|yyGDq9UB`krWo{E>kT#Y zznBH53dVqZ#l*x2985t$K`9Z;MD@=ecrKov!kH7Gz2X_mfaS?=JO1zNEGjnkXsSQ~ z_#|zf#{oybzd_)D!o$ONr%UdxuM3Ncnou1KhL#lMCGzhAbavrGkQQBvxbZerU-0x( zYF9qoMtLizSZ-`Ab; +5SEzw`2FJv}|O zD)aN#`*5TH>4{)edhT)H1#UO@zZVUx<=Z!CYjZO^!=&byf}Gr|9C1-@?ql6I2mO%R zzfy)UJ?5lc0HnYz7FsF^!{y7I0AhzJOts}3nWd$g`@uXbKmYXXtj=$J>2PvVQeeDK z0XJl8YfHla*6r{2Y(PeVxh#}9F_9_U1+W)P!L!p-VBdKH{GFelZ(bpTX1jmn-ez|T z;uu`S%?6ajM-)Wxi3yL4f4baN?HO;fA6WLMTieqwdu+86$9f86I#$YV4 z(}BJJ`1lCU^I 8T@v@=okUyyjVS#8V^siE8t;kEY bffwif_! zB9?tL)fXnO{sSBuu$?eS9%)Czh%Qe~96 Z$_;K2Oj`LEa!|_ETqdnM7q_Mz zKA`w_!Tp0LfrE+Z<~sMQY(u9jhz8;z>gK2tm$GjV3RcG~J#{s9*qIqM8DvJiN{Q _vs&`k&^*ykrFz{0)+aPpbUWDk(=%uFYtOyRe`y#QZi zQu$nI{OzYX512*cVBUci11t(~Dd4EKc%4|fx`Hrf|4-Z16jY!PhC>6;e7d{4J0?>I zn_MV)ll0uDL%x8ORkKjSP3%vGpp$$YwS>#6^t*S*5GD=ahLI;5D?hl0qn2v}&V7#) zk`WNVEVx>MRveoQt90$`{(`vxP8qmA!VW(-L2&~nmkT}K0P7u&mO+Awzv{( s_sUms(`B_g2;diJ zH%(1V85tRXNPYnCK64sEU2Y@+SsLGK=k-)=O#uqWd-I_Zous>I0DI=%_JcF!1-Ps{h@%dv9Tv5 zCW?zoc*DoC`t%2LN93qR!KpAJu#H?35UcX=qmz!E!BKwUIA(x`Lk{*aJm438BEtpF zItm(1!1AW-fp7Wq=g%A|2=D-76B9rkf7V*`k@4Fmr=}+D4ua0E(ls(PM27(b1OTto zT%`j@6Gk3Dwatu;IRTpWaCb$+XRQbdUorX 0`{1T-fLjTA3F{?+~FSGnraz#s_ zTy5f2kZ)_C=I=fc`c=?QY0o|1nwl;H_+VgQc=x+yZF_qTR3j`rIy#xr0T_(H>Xnm| z)2lZA2F5XfyZnNJ0XTw5$EH86?#}_(f=UD fSfb+ z^XJ>UJDTSnZvadkx1V8Ctd;9jfRl%eb##bmXu5nZ_R`YQXacTf&L`z|e$}jiAH!%t z+cGgB!L)KpYD-9$ME6NxpE(THkB-!`rhp0r_@ pBt<=@H4?ynlN zr^g4hOBxBEti&vQ;;BzD IHBW tVpbq~ANfKlrn>DX*_T4^kfjeEeZu&x%-Tz*n0l^10F2L@AC=w|jz$$AsN~S|U zeM%HjK>`m7n3i(@#sG9JyjgB?B0*cAmh@xM(Q$AWlSi110^wB`$YsDGj1*N=>;uqP z_S%RUhK7d&13xic)|1c!Zv>N!FT}*w+FBNzsQ)+VirLXG9A;OnQ|D5(MKXw<_{7EG zyY*;^$w`Mo7X|awyl|Op+(~!YX}+cFx)_LoQ^h8k{0*Brp|ae *$!Osxd84~NPra?FA91r{e%a19IM z-lv KdPYvp33~w{`^h}pc7E`qTEi+czmtJ_o3Y5KsPyU| zKYnnta+Vcb(WjO>SFJ!#9bn~zZRc>g;|?T)aCt$wD;G|`UJ*wTm|W|$c^snPP`~-z za`JGs@=Oy$^LcuDI-o$35VRIYmg`SES1xF-N~zqFWKqpHgtq;pbucCc8;-@S&InYj zsg;7g!On!sTtPohqVWKJZ%*crUM`5TL3Kt{j7x}%KfeE@iv;Ey%v>nbtkaKmd5F}m zbGUNjaDZ!}0QhlWQf5h3ncl6`Sq%Z81{7H-F*z-n1r7QV*w%Ije7mcIg$Dbne86?S zg0}SO=xlGNw7OrLDI3$ParG3obXBI3o_C5a+m@+69B}&@Rl$6jp)44L8$N>gQaKvo z2VO))u?!*hH)9J;nIK%;qLPxKfq@8?hS?biEd=&8kXKDbg`q1dGE$b|9?&sN8p+=) zD`ppvG6*^;DJiwIwSoN<6&1xfT7(Z^8jw&a_r2GkFhF7fu;u;LkqPFcIOFT%b<2cG zd_}i%Jge5GaIV2bf*!Zj!mOPkw{cRdR^;Gk$_vO?c}b_Q2l$m;3{ewOTmB$P>lWZ6 z2j1rg*P}}!nCj@b1J(p~0SFG#CZKJ=B>+|mz5U%{H56+pR4mp_nwqkZtCh;QYh%mo z6rLaiI3dWZpQ)94`ht0rLP+B4MGE-8mQ7ubq_1snNCX}7h3fmW?Fs1qWC#qY13)Z5 zXbmj3+*i4CElLD2b6@1_qlPaJ$pzmY?Cg|IIRu@tUAUH7tX4x`YH=06E7huyOT=4l zvMX~m%FXO)DgUDlJPg3|gAAPD21SI;@De~TdjUSSNy}cPyT7^V2G}=noOG)UJFwvZ zq5RKz-OI7Hu}h*UDk^$2+Y8hYP )lGlC0$23S z0>roEC`kSa>>v<>;0zQ%P1j9vX|2m_xenL@0Kzg_)T6zyvC-Q;s+f+G92uE3`l$OH z4G;0_^5 p92a(pZ0ZM`g(wLMoSBn zQVihf|5kfQt!78UW_uDC6ilIZFN>=LMkNw%lSn=*nEhO#+=l$Cit!fZ^2oNQf4Npe zMiclju#<7!g7Qo~JG{fc 4X zSs5PTzZckJ`UA+=3MA!yp8C-+yV&SBCni()?DB#+T0Ks|0&4&9H_Tx&Q1lW{k(42X zDH;zx1cQ^W9F44{ni!Q~GWu_nh{Wn>jEV`eX9JZI;FjRz9V?Qb@R`+f#_`d zX`YFT`$CUbe|H@ueB5)-2L6`g2FU=X8%r%=*! !so0kdS9#*fficekO4; z{)V3R7?cbG8o1E{p85+fwDwTzL@l;YG0@bC9a~-z3na&r(+?rehk_9gB1z~50Aj9) zYkpW5it7?5v5Kwj_Vz@km8Io>oH8&FMszvgt!yNXYQe#q(;XP464m_n>BQi4x)b5n z|N4Xwe_scs!}rP*1iw~*ET(T0V)*>xequ9!$lw(hb%CZ5`jEo@)-wSQw7%_*}Ruw*fKIP zshlRQR -8HTVcZ(VOl&N{Dm3LuA? zkNz+oQcTNud!fuRK-9kK5Y-eG7thiDMr?22<1lWVoLD#<{@!6b%`UCqi%) dupYQeJ;Q_APd3!oW zqLePoZJI(PA=e?|9f7A9Xn-lNShazk j3n+-IRc zAr3G(Iv$?dPa>=R5d=UV)J-szcE90ENlg_M5p`(HC@d@l%Me(UkO+^6$UJav?Lo;5 zlIx}uXk^EjQ32Of=k<&L3ycNq6^xCsb8*e>XS0uvh3ZGk8w)5Rn|mZrOM4q*mGV@J ze~mz~hY{W}N@ASjEY`a9>8(I1M)7ufwlVSrBi8?ArXVQ;LTmsj2&SQ_>AF3h4vG)# z>`3@s*FpflfUE}U6l`1pL*^?B3ma#^?SYc@(&Zh_10_B=c=-TP*B6GPlK3A11a6tI zurPSOz}iPbK>-kLT%uhg<@B>K!nCHQvhw`w4BYQRkxJ@(bS5gWi3P`L{@WD+xS*=2 z=yh{yV_;ygJvP2Rx?cMg50b>%!#_|FKdvFf_}u$4(8ZMKt_-s#Bu)AY>8`|DamdM7 zW%PpCcjd7OeGnLdiqh8B&K3854+vajB)U?fZnd%KoB7(~&O0tn&Z^qlS}<_Hctr?I zPEG~_^>-I!Rxx|IJN=(*2Nwfug=gAgn*t3h0e8;8--CdA0g99H* Cm*uu2=20Gb!UP1)>umfC09F^~<+lwt0Dph?+nkGluNAb0~z? z4r{+w1&T^_`%~j)zvhz)JS!J*YB uSi^yXA%FY=Dh!xtbGvT975OnTvH+H(;^J_e z&On2%_?^W}*&EkG)=IgO{s5iyz|tgNy2o1(iamW-w;d-Hnfz||nKT3BWxfaVy!Cr3 zA;8`Vc(^s`@F9Y=y6x5j5C%pUc<@KPp%~?fj7razMS-~s2FW}SFMxx39xm$WJc1ua zrTX2C2shkkAWtnQ&|wzCk-H1?uC}vfaj!w*rzjiZXn&PONY120PiZ3Vvtx=VIkwDM zDN?1lyUzD%ULs>=oGe6V{Wgw1@m%r$k8CG05Uhc>X#r07SGbB<9{@Q94Yo~h|M>I- zBQYl_`MCk70{pJ$dVj?3?(TH)E8n}rW^U8={~TB#hv^=|WG7
`L z><5x;+}t!mZ%F$L0qNs5ZmG2z!hY{lg9;oqBC~dnLqlt8Yd1GHtV2`awi_M4O(l5u zX9RM4c06@>A69pB0{%3gZoDJv|1%D`@IUp$+~55=J2v~#RZ`%}r(*sgAT?+-jFcJS z(|9F`%CCp`tjt#Hg)4~e+%E}w*f#^Hn=R-xH=4peVBxlJ`%DuK1|l?2G!;h8N8o%$ zUbrAzhpmIS-JFNCST)_U$q>5TeFm}#5V~X2NZxPkP9LvyWuEc1<(T=m2?BN{!NBmb zlDi(MN|!w#i l5k&^7j2I)D4$3^4|_)P1mECkkDsJPYf7Xqd55e?JVipeKHU5(g4o= z0r&+Nb$_>E)f~UwcMjtgm%W*?r|Ti99C7dengTdgOFn!bq;l^j@ 3vKvd)`qko?r|}#uU%D+9L+yAxBK0z?xh0e=yuL2RQ4hZ4X~5d{3A19A2f< zz`DM>dk0)juy76VI+NcncElPT2PYTY4p3dyrXA(VjbQ%#Cs@Hst&q@Hhw0)JC?y40 zuR!pC^qB^_cLh uRmcTI+Jrwk#TAUgoII}RCNAqbIX z1*d>}{89nOrl<=#KdDfWpi`CbecbiRrpi?qt{>snqM3ce%W2YeQ04ikiExQFQ?9c= zA-r+`R1h!*EG_ea90xl|V2XWxvyd;AFg`mg$iV^5?1QzA3CMzh2_1xcJBBcWhDjlW zX(hhb!8;WDXgD;40Ry?iRH UyN0%%`NcTbznQfy1`~QNM`x?z=soOK=a1Po0*v{@uc_>f3og}hGVaP z^-Bgp_Zu`FD+dx_l<94y03{9fG$=(}%-t7rgPNO#9ZmqN0t7po*Xq;48{n)4Xldd1 zNy2c^YW@9z2?hq0-S@1r>VRyuUqw#8hCHb=UNO@u*?+ZE)4gEC9}M1>K_C2!p^FQX zeqq^L-S=EQxBBZy#@Y;2ru|>QXHhsa(`Xj=*a`|$(*L&ImTiLTzbf}LybZoNLm(Fh zrWu2#>4ZjwE++^z)>*S8e7}NC!~5$KP%$4oJWQM4Qei{`{d~w(>hb>ldskN_)yiG* z_~KU?A`V#}w9V$6_Hf=%lWvxh!okN2)MFI(ccx)7T|pR8=v~(yN9xZ#A7XZ^z(*B8 z$OcT^5lJ%@GnxUhlWBZ{p=SpG9Z3fJP&Cd)V&=E7DiTCF(@mo4!6ik4$$6z&s@| z`fvOKAP0`; mBZ&g`uOs0qGnvLx_xd 04S1E8$|BMJa( zsv%SWcqH!*8UO|^eTJgL!@W*dl}vTHruh!upuNC9DTVJYKRi9{($GkTb~fo|r$TGc zz{(Nu?g^MxH3npDind}9G6_=%&yRj>ynF40!EM%w=}0JWn`@OTr6lS0PXn~SbE;8A z_va>%g@F0o79AZ8a1!{xV7X8P@J;eU09c;;b1| wGRd!OS@=HaWPkw)*cZE$=xc6Jn)$Y8IL2o7uoXC)>= zr&9_vyeo|$&IAbL!6aStD@?#C;rbtIN03Zxzx7sc#b-M`(n|-v^SM71b~QN^ynZ2n zc#S*VfRb(fBqJs!miS-^90j6d;EG_GHq{unxgG979QvS#Km?}!$L;-lN3dhr*5iM= zi39c-W1l?Qx2M;624}OQVK4|7#|E8#q6;O=%j#-s%OJA?BR220+5q?|4Gj&4N5o(Z zk0|@1(UM!Ven$xk{+j<>Sq^dTt>N 4*4W(e&*? zJrkImo5Km^3Sfb`&alekjG(8dr$(6#u|xem{(HsBFI_NcQG`!$FjH&0K!yN7Yz{qG zE^iOTjwIh 30;x+gwTbV=k-^#i*VU~pB5G>2#1YK-b+dvg5s%*@ z9RS}(AUU3NRY-KHC>Z1d0F~7;1V-jsL0GUj+Xw?6y~?Yj4=RESd|TC%hu84<#x= ICwb1WhOp&%^W4E&Hi z9ue!wyp0m|
OKA>X8mNYd+li5pZ*IFk^JG3AI6_t-8eoAa zAfVHvZ#v)mLp@uDU#qgXoZPeT@a9`XR$OLX^R)@knXTh5bQrAf%+3KDyY({WF$4BQ z0JfO-4J+mfhT?#eTJ2+9RqkWM`@^Vqb*%J;`$L0@nGUL`d^HNCQ=6M_LrJ`)ojT#d zu*V; ?QcC6x{fB?yK{Y`YPpqXn6?5-~;&7-16a_a*ryc9a zd~bg7`;f0t^E0 +YC6_NXIqf=@N4}|Cy 8U<-6Wf~KqC>F z@+atBM2eK{G*{05&HxeDssHu5dC!hZNv%cns@p=|S(1Ty(F8sOSsg#uu*dMv=S7n? z&(Ov 3V*q;ZfV`VL(&nLZ?)x!hU zmD4k@?RIW%V(Z7YRcg!D9fLJKOAbCs30A@p3agz?=Wirw^G1l|w(bKLEoHl9T{)7Y zm+9rJfUIyDeI VRH;tA zoe-uQvWFuIji9@1_3>p%_px*7&vYQhN5}{{HjwdR3C|{DlfFDXV_a$NoUK{`wC^|g zJitwcugRkV9RPi6KAq ;yF8F{*C}%uwhWeCAELa}i;7~*LL_=wr zL>}*xbppI08q5r0Nwu>@Xt*+fvq&x6fh5!+Fz8L%SO!T~+P#ygoK7JONl8iKsG?7S zLf|c4JB%v6Dyc-*f5fk< rStmeBgn5geAwv!(i)lI!|F5VA%**UVG!P|5nd^e7!pq$5vk! zqlYLWEPX&fkm4AN43ybs7*QNlUHhnrCL@zDr0-D&lU0#cMBH?zDyD#0EA^Szr!kdk zISbjjJU#k0c_9P*mWekb2TL;Uu|{qTu6r~jEgAGyzCk0 zOQP@pf*(ncIkt(Q&roQ{_~X>Fb)76IE4nW$Sz3srovj9Y01Sv4(Z5$+F!C7YXU|GL zA&vj{*^m9%G1VW0w(#kV7_4w1yn(W_*bVc13JAjo>i*iQPIR;zX*KS7&|XM@Vd4^* zZIHFJSdT=C6R*)Yd6**!(|;BiNb nR^zrEsvzz!h87yX28Xo$)vDz zp}j~?MWc)$oj+0e-TspzO&MYE4ZC;>MZbV#y>$dUEj9`Ju}f!nFT*O^?AO5c&I7eG zo)9rNRiT?_TtNZeisBoDxHrTclan;o#mpS6cS7w5ZXc`n1e9(doOMA|MG|8##P>%* zbl6dk%z?5*QqMD9;?lklTqvOzk~y(?8;Z!|jjO@kQtznj;)9loF35}%k!WB`gBk8c z)%)rbVWh64nt_hhQI1L>F1|+rf;MUv4Jq}24DSQ`h%h6wQt%oPY1H5Eq$~gU8eb?$ z6F&bcEy5`mMEQI^6+)wx#0yp3AxI%X`WcEn xYbM)-9<9+6As zkrL`7^T^Jw(p~Y$^Zt7CkQi`{v-YU_kkra+e@ctOQ!(Vl>P{v|LK9P)6V^2?PWpL8 z5Fwk<-56(ShX|>E`j$8zWG6^lo~f@P|KRft_o4F>{xRFZhsu4@Uc`{{A{ma4ME?_h z0fGMV#SlZgNKwEzOMc80f++HI1PEb?JZBs#3n0fY`GNU}*g*i)6jOCaC0EV~E98<{ zUyvsW=nygM4eKfmedQ-g(UCY8ihGL?FvGwZP)!5H9UYf6ic^6`f1;idX~4S0i9yAm z``YA1e%7XPG6+*qK;)}J2qR{bG38~sQ7m?bokBwBO?jBB1>s0B)qN^|f=)+Nf`xYZ z?rKIqQGDf^gzqp=e5#ZPw&8lDd fR%0>YIfyC102xMXaV$93) zU&2fgVLdE%L)g?Qf49LtkxX$QC=E=N0LK?*_*M29iBf`TUI8mTF-igej1f}NK!pm7 zWs8mMph)TK&^tOCyDD=fXL}VPWE9A~mIo^D;NVJv?MINX$or!7U^;IkIIN0diZc}i zl}#}#<4 = (props) => { const [selectable, setSelectable] = useState(''); const [selector, setSelector] = useState (null); + const flowEngine = useFlowEngine(); const [ctx, setCtx] = useState<{ [key: string]: Record ; - }>({}); + }>({ + flowEngine: flowEngine, + }); const startSelect = (selectType: string, selector?: Selector) => { if (selector) { diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx new file mode 100644 index 00000000000..5bf2c8016ca --- /dev/null +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/ShortcutList.tsx @@ -0,0 +1,106 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { Button, Divider } from 'antd'; +import { LeftOutlined, RightOutlined, PlusOutlined } from '@ant-design/icons'; +import { AddSubModelButton, FlowModelRenderer, useFlowEngine } from '@nocobase/flow-engine'; +import React, { useMemo, useState } from 'react'; +import { useShortcuts } from './useShortcuts'; +import { useDesignable, useToken } from '@nocobase/client'; +import { useAIEmployeesContext } from '../AIEmployeesProvider'; +import { AIEmployeeListItem } from '../AIEmployeeListItem'; + +export const ShortcutList: React.FC = () => { + const flowEngine = useFlowEngine(); + const { designable } = useDesignable(); + const { shortcuts, uid, builtIn } = useShortcuts(); + const { token } = useToken(); + const designMode = designable && !builtIn; + + const { + aiEmployees, + service: { loading }, + } = useAIEmployeesContext(); + + const [folded, setFolded] = useState(false); + + const model = useMemo(() => { + return flowEngine.createModel({ + uid: `ai-shortcuts-${uid}`, + use: 'AIEmployeeShortcutListModel', + subModels: { + shortcuts, + }, + }); + }, [flowEngine, shortcuts]); + + return ( + <> + {shortcuts.length > 0 && ( + <> + : } + style={{ + height: '52px', + width: '12px', + fontSize: token.fontSizeSM, + }} + onClick={() => setFolded(!folded)} + /> + {!folded && } + > + )} + {!builtIn && ( + { + return loading + ? [] + : aiEmployees.map((aiEmployee) => ({ + key: aiEmployee.username, + label: + )} + {(shortcuts.length || designMode) && ( +, + createModelOptions: { + use: 'AIEmployeeShortcutModel', + props: { + aiEmployee: { + username: 'form_assistant', + }, + }, + }, + })); + }} + > + } + variant="dashed" + color="default" + style={{ + width: '48px', + height: '48px', + color: 'var(--colorSettings)', + borderColor: 'var(--colorSettings)', + background: 'transparent', + }} + /> + + )} + > + ); +}; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts new file mode 100644 index 00000000000..51731ecaa86 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/shortcuts/useShortcuts.ts @@ -0,0 +1,66 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { useParams, useLocation } from 'react-router-dom'; + +export const useShortcuts = () => { + const { name } = useParams(); + const { pathname } = useLocation(); + if (pathname === '/admin/settings/data-source-manager/main/collections') { + return { + uid: 'data-modeling', + builtIn: true, + shortcuts: [ + { + uid: 'orin', + use: 'AIEmployeeShortcutModel', + props: { + aiEmployee: { + username: 'orin', + }, + }, + }, + ], + }; + } + if (name === '0krytmcef9g') { + return { + uid: name, + shortcuts: [ + { + uid: 'form_assistant', + use: 'AIEmployeeShortcutModel', + props: { + aiEmployee: { + username: 'form_assistant', + }, + tasks: [ + { + message: { + workContext: [ + { + type: 'flow-model', + uid: 'e3b85ce6e70', + title: 'Form(Add new): Users', + }, + ], + }, + }, + ], + }, + }, + ], + }; + } + return { + uid: name, + builtIn: true, + shortcuts: [], + }; +}; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts index 4a61de9f77e..ef73e723e9a 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts +++ b/packages/plugins/@nocobase/plugin-ai/src/client/ai-employees/types.ts @@ -8,6 +8,7 @@ */ import type { BubbleProps } from '@ant-design/x'; +import { Application } from '@nocobase/client'; import { ComponentType } from 'react'; export type Selector = { @@ -37,7 +38,7 @@ export type ContextItem = { type: string; uid: string; title: string; - content: string; + content?: string; }; export type ToolCall = { @@ -112,9 +113,7 @@ export type Task = { export type TriggerTaskOptions = { aiEmployee?: AIEmployee; - tasks: Task[]; - variables?: Record ; - localVariables?: Record ; + tasks?: Task[]; }; export type Tool = { @@ -163,4 +162,5 @@ export type WorkContextOptions = { }; actions?: ActionOptions[]; children?: Record >; + getContent?: (app: Application, item: ContextItem) => string; }; diff --git a/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx b/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx index de83ded3ebe..4bed5959e2d 100644 --- a/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx +++ b/packages/plugins/@nocobase/plugin-ai/src/client/index.tsx @@ -22,6 +22,8 @@ import { PermissionsTab } from './ai-employees/permissions/PermissionsTab'; import { anthropicProviderOptions } from './llm-providers/anthropic'; import { AIEmployeeShortcutListModel, AIEmployeeShortcutModel } from './ai-employees/flow/models'; import { defineCollectionsTool } from './ai-employees/data-modeling/tools'; +import { FlowModelsContext } from './ai-employees/context/flow-models'; +import { formFillerTool } from './ai-employees/form-filler/tools'; const { AIEmployeesProvider } = lazy(() => import('./ai-employees/AIEmployeesProvider'), 'AIEmployeesProvider'); const { Employees } = lazy(() => import('./ai-employees/manager/Employees'), 'Employees'); const { LLMServices } = lazy(() => import('./llm-services/LLMServices'), 'LLMServices'); @@ -108,22 +110,9 @@ export class PluginAIClient extends Plugin { Component: StructuredOutputSettings, }); - // this.aiManager.registerWorkContext('classic-pages', ClassicPagesContext); - // this.aiManager.registerWorkContext('collection-definitions', CollectionDefinitionsContext); + this.aiManager.registerWorkContext('flow-model', FlowModelsContext); this.aiManager.registerTool(...defineCollectionsTool); - this.aiManager.registerTool('frontEnd', 'formFiller', { - invoke: (ctx, params) => { - const { form: uid, data } = params; - if (!uid || !data) { - return; - } - const form = ctx[uid]?.form; - if (!form) { - return; - } - form.setValues(data); - }, - }); + this.aiManager.registerTool(...formFillerTool); } setupWorkflow() { diff --git a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts index 27e5f453115..78b21cf68ac 100644 --- a/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts +++ b/packages/plugins/@nocobase/plugin-data-source-manager/src/client/component/MainDataSourceManager/Configuration/schemas/collections.ts @@ -176,82 +176,6 @@ export const collectionTableSchema: ISchema = { type: 'primary', }, }, - aiEmployee: { - type: 'void', - 'x-component': 'AIEmployeeButton', - 'x-toolbar': 'ActionSchemaToolbar', - 'x-settings': 'aiEmployees:button', - 'x-component-props': { - username: 'orin', - tasks: [ - { - message: { - workContext: [], - attachments: [], - system: `## Task: Create new schema from scenario - -The user will describe a business scenario. Your task is to define the full schema from scratch. - -- Confirm all entities and required fields -- Clarify the relationships between entities -- Normalize the structure -- Output a complete block - -Avoid defining system fields already provided by templates.`, - user: 'Start from a business scenario and generate normalized collections', - }, - title: 'Start from a business scenario and generate normalized collections', - autoSend: true, - }, - { - message: { - workContext: [], - attachments: [], - user: 'Add or adjust fields and relationships in your current database', - system: `## Task: Modify or expand existing collections - -The user provides part of the data model and wants to modify it. - -- Ask what changes they want: add tables, add fields, update relations, rename, etc. -- Only return the changed parts – do not repeat existing schema -- Wrap new/updated collections in tags -- Do not remove or rename unless explicitly instructed - -Example: Add a new relation to "projects" and a new table: - - -[ - { - name: "projects", - fields: [ - { - name: "ownerId", - title: "Owner", - type: "belongsTo", - interface: "m2o", - target: "users", - foreignKey: "ownerId", - targetKey: "id" - } - ] - }, - { - name: "project_tags", - title: "Project Tags", - template: "general", - fields: [ - { name: "name", title: "Name", type: "string", interface: "input" } - ] - } -] - `, - }, - title: 'Add or adjust fields and relationships in your current database', - autoSend: true, - }, - ], - }, - }, }, }, [uid()]: {