Download CustomBatch_fixing.py from saliacoel/x: direct link, hf CLI and curl.
- Browser
- Download file 90.8 kB
-
https://huggingface.co/saliacoel/x/resolve/main/CustomBatch_fixing.py
- Command line
-
hf download hf://saliacoel/x/CustomBatch_fixing.py
-
curl -L -o CustomBatch_fixing.py https://huggingface.co/saliacoel/x/resolve/main/CustomBatch_fixing.py
90.8 kB
| # DereVisionScorer.py | |
| # | |
| # ComfyUI custom node. | |
| # | |
| # INPUT: | |
| # vision_result : STRING | |
| # The complete 1-110 response produced by dere_vision_questionnaire_prompt.md | |
| # | |
| # OUTPUTS: | |
| # 1. dere : Highest-scoring Dere | |
| # 2. top_3_deres : Top three Dere names, ranked | |
| # 3. scoring_summary : Parser diagnostics + ranked score summary | |
| # | |
| # The exact hidden 122-Dere scoring matrix and the exact Q11-Q110 option | |
| # texts are embedded below in compressed form. No external JSON files and | |
| # no third-party Python packages are required. | |
| import base64 | |
| import difflib | |
| import json | |
| import re | |
| import unicodedata | |
| import zlib | |
| from collections import Counter | |
| _EMBEDDED_DATA_B85 = r""" | |
| c-rl~TXQ2vk|z3BXnHQ6qmrPKO7$?Ns_N<1_Vi`C)!R1b*m*#aNfKQkz(oQ@vDe0If5dq?e{X- | |
| uhQ}>4c>yCm+~bnSmu|8|f<R<uc({MJ|M}IIX4kH^o3DQS>h- | |
| ^T{ne|l&X=2u)x~n(v|s)B|9th+&GMqzHR|8rFF*S)SG$$_a{GDfzC7&PgZ=u$^=f0k{J8AC+PVM!QT?C$a=UY{@EiZtZ~ZU*w(&3e+wB3|mk0mf5B`^axBS | |
| P-e|h6x|97k2mmBxx@0U0FXV3ok|N8&V-mmtL{_SJ4+TY_puFkLBtNwoF-}UdiCHQ^!4u0?7*6$Ao|J6U;mw!0>XunwAFa2NoZ_C@|rfK!RH&@- | |
| i|8{$E(X_4l=Re;2<@nP-w(iybv~lm?Pp!K$fAP!ymu>6c_h0qjJ^R1T{<^$yZ}qRs-hc0|miltP_dj#kEVpMLw%fJ;=6B6<@4xx$VYh3R_#c1iuIDdXzuv# | |
| Jem&He_wHr?=Kk?-t^cR)*7@5%- | |
| G}*4^QrY>{B?Ey`N2P_uRpI=_>WunLH_S;@9%Bkr|Yf%@@8qj{G{&V?!CDmxY+L8zh15UD{Pw2{uTaw@c(>!*sbiB?e$?}zudbg;{5#DU$6J~+rwvnr!2esu | |
| lfAhzuCcM$8YeOH#Z0Odw*Qrx;6RJ!QbQROMjtm4p;7jH_O(q|F!?;- | |
| @D%$+<Jd+TJ^5+1N?OJ$v@Jod;g$!&*uBBze4ZTO1K~Xw}bzv|90r^sNb#pX7RgjhxmEpUgEzs{y|w@`=@95+5hP8x80NEx1c|E8^|A5{!`+YpVgy_Uv}^J& | |
| -eZn{<`el($_1$fcEyjUaj>lyj=Mu^jGYc?cvk5``Uk~zwMX3?QN@XBlVSfPVMG~d!gOj)U{cf&FxxkP<EgBtbbDN66)tR_7?DW64>9?Plf(Lb+4bB$54HBj | |
| nCl!`|7Kk<$kyN^{XHM=U4yn`ZfOFPx^oCa{ajI?b$!VqPK19|Ku)<{>E?J`Um~)t- | |
| o^{xcz3=`$4~r<BRx>T>q!v^uKZ2IsTa6(9M5wd%FHlhu)95T@}CaJG=fJzqy<5- | |
| 2SeA(CzU0KRw>$e_TAd&6}V0JH7q3_t}2mUpl|xo1Z&u`rjOSKlab=8@u)EA9{NC*FW{t{*V8`R{;G2z6juda%Dh&- | |
| S#fGs|ENUx@w^R)8m3*{@K?A{2gBv>^JTq(%*KKq5D?XhVENe9rR240%5+}_O6+)684+>?$7+K_n}>}Fy9RBS64FhH;<~uqkpx91OI?49r|Zn^)PSI6_6hny | |
| %OTS(M6>9=DQ!$g~WZMONskN7Zd+#x}3Q0bU|_7=#t{T*>_KGudbMX=t_&d+Xwf4S76Ld_o&9`f9}eR`PNk$bDz0l^W&mdZuAv)1;<`oS98p_uI!j6r+WsTD | |
| n0lgeDSf{)O~LU+xE5rUxUn^<*E>V=j)JuiLMmcEuy~-`hujtb2SNH#8oA`oY&o7?n;ySZ`;8$-`jEjIM^S3mGa- | |
| |LS??|e==XK>~rX!HCXg(R`1c%Rm&{*a@D^SpSvIJs+axlbN5DF1@l|{*51UnjQzOi6*BWfu9p3{=&G6DXR)BUEl}4qf7iLMyUO<CqN{E0={$FPi7RkFF8)h | |
| h<UvJ)K}n-kwC4- | |
| jpq{~?p!Mt7v*j$hnhlB>L{zh3F?+t2!SlteU(4XRV)lG3gJ+8w4650nm_blAgP?MDq?*Blat06T89XRx@VKHKDQFN@(Vj18{d)FfIU8HeqKg@jsu_$ZX9Cs | |
| iNHK#!HG@Gp8&<RDiWyL<84Su9NY^uXzM#R8iZ&={FsNup3fiEa!JwcWt!MDCp!Mrna5;l#>lyS48XT!;5LVK9747+g)~{#KFKB=j?L%mf27{6Y!<zPdlXj# | |
| >gTN*Yj&y0zFKjTVY%nNop?%uZg$;r#+ptZ8=lis<HVxABY0xii&- | |
| ZCTZ5q(~G<c~|d%jPDVOfLcIyJ6F4f?h1`9=)}#SI444F=^6o^RG*P~Tu!;Nbaw4F(nNg#tIIZx71b)79- | |
| *tH#l(K}f3xgX;Ett2V4{Ff48`sBXjJHmGfb()L`ZcBD}w^=Z#nwPRfxxk-azS%c@>w2&_C*(R- | |
| D&;naD2=387hW2RT746xA_WaE8xfTtc@6l+r>`0e3u9m@(HVuySX<=;|B<j<it!5BX&W=^Hv3=SLZQ7A8?MOX)wwzJhv}bD>ODO|tErSuo44A9g^TmwVsX<V | |
| ycBGoY^OHyRY6j1iGkCt9g?4F2rjEzDG-{J}yqv*v_3ZgB?fE9{xsvv5iw2=Rnks4k@%o#`cv^5}qm3U!@w7zqN4D`JFc-G4CJn;6v>- | |
| 4ovZ@_x(IRUagcr5(JsKHHW2kDO^G9lbHf{nLHh>In(UQ#{1;&s3)5qwt##PmbSQ=|p3mQEJcWRHPj|JjsAXm^prlQFKYSB~2@#K(cz-cgjJgrJN4U*w$q4l | |
| gfG;IVsq%t%ukRVbJo<=EY(r6mv2vQJFBNR3`f~P${f`q9dYC#i7AdetJiH|IBS}<KidH@+-- | |
| O}M{q1?w@cpAh5YHYkm;slbB_ZR}Hh1Im<cv>inpMxM0Hgx1KXh|ToXcj*&q6P#=jY8x<j;X~_`9qof5JY@TO%Qp$TjQaJ93eoCDrk_3AW|Yf<S6?w3qu50F | |
| tvI(B4L2q5r&Ay)4m;rr_Iwx=IJAx%=mG1i<WozI6- | |
| aNOy*B1PJ`ekZ6@>cjvrsb(;%24!ocQ_A3u_swSZntj>%sXkw4|!5%LZmfoJl_PJU$J@ypWq<t5j=gGb24ePqDYWFR$`MvZayI9{LjY^(MXo)$cO%ujg)In8 | |
| zS?6DTrbyk*0;yw*HSR%!Vt|5VmNL8#yVUkEZ=dq-@V;<6Dv^)*Scp5uHBp04GErLioL@mBi11UgFh9Clw43Sx;R*dGF45{&UYAHD);uMjLc-oO3P2*{ctaw | |
| @uIBiDL@}p^)yEJwxKb$0&A0`8*r6uzd;WU`>Gyyz~W?>wUr_q`<&R#9r!WiNZHAyP}1Q4|#%SR?Wt?23FsOF3!xMnJA$sCM{W$pO}je#O^q@+P!G%dcSg_g | |
| 9SiY9=kQKyf*fErLd`GM4^CGmQMpn?`Tc}!vTNOuw8bL5|$9%G$E=BeXo=A+=;k;Zx~#_bWpMvo9MdnDOC!V7p>O2KttK@*147)c@&I1MPg$8@HT91=B%WAw | |
| +{J~A=-A<R%Db!lTm)F4rDE7A9n1gQ}!8#kbKns^#Kp- | |
| =nnB|HsE1Js~Cfm(J%Ei61OqdqNd)C^O0S~Q{tM7u{Bw?}T`V=$ry<=H)w+B9~%$8bCi$Uer!89o9*c>MyNmNac<YGxu?Bx*HrA2U!NL7Mwm&ImG!**^~<E2 | |
| KWcq`8kkAU{6F(_}f1Kp8*AvK~t%J&I}5!Z?p{V(Wzd#lbBa0Zij@c`T0fC}7542;~uq;XIb%Uo6B90a3~$Yr- | |
| {@il+rSh|oGT$gfX>+z!TKS=TWFwb8`KaI#1NY1hQSnP3OwoQ987i>zROe&FOj^81KD9t&e)Z97`pj<slFCL%BzP#dF9dn2Py16s3IbADPZkN=eQX(Ou{MWH | |
| 5;pEjkmnG+YN5!2ITn>87;M^P$&I(Zt%>(jU^S|*MNo84oyGk=A4kAf*PK(XZyR9(yG%+P=|8cT#u<j>cljhCm<Qf4x8`Gq}<BQn%R%pj#YwZKr&z+Nrf?- | |
| 6K*kG$qsvh)x!E<XuX<2A>MwtNg})nFlL`ACYoE- | |
| ~pkv{j=H9>;O`MU$>+y&5dUJRc#O>bevfKXLHLN_rfhod!I38i(SVQqkr>(8(mI#Y(Sv{T?McwK_PDMQYR1aURLW{7?Yt5oW{35u8Vjr+q&PPb<SqBt4)8;} | |
| JDr<v*su)MjV?B#j!- | |
| IyEUL5lEH_n#Y`9TBG)St48V6IP%lRAZnAd=7*Q?w0Jf@h5E>vH4`~`Je8ywh@3lS@((G(!ni!EN12?NlS~B7=Jp7*(IXd@Ry3Xect!glthlB%Y3z}pkt`8r | |
| &&ToO$5cF#as}70NF53~9n(i)&&M)iL50cuc|0GhvwY-pe54pYj>6LzEcw$p^0UHe(U$zxfN3C#rj?a*ossxR;yubZ^T*TqLo- | |
| 2hg<K13)1EJ9)UX+r^fVAh)3T;qzksLxkU*ctTiIf*M1<TPVKFDDNh;Jz47z5{P|N8iGAF1p+dayfiLeROY8^g~!qW;#n- | |
| Ly9R*^ST*6b0YHEMCZM@UY5Jkh|J=@B0%lsF?|_gLfju?D|KF^&inMG~olBf>3Bi?n=9?jmw>B$3%hq}oK#3IjoTO+?6e8oS|RE=ztfecIIIX~0bpiA)Afji | |
| -(7(pZ`_$gWR2;VzBW?h#}y`AZb1!6IjTn)7>nJ1##B*t@mdevf=Kk$8Dp?p|&5;E`?a7}KfMY+p>Y#$t?^VaQMuZPy6$G;wtr&?3|rKsDgfs4+*VMP^+CZ) | |
| MAhr$KzT#;dtb&g2(s)Jija%mJps%;X=9rcq2EfxS^<<vt2H8BdkUAM48xf_9JLZ2k|`avu3-kI4}=Ioe|m*)^miK8{biW^dEV#?unOX)Ns- | |
| #5z3!(dv<T>R2V7He&F|3#g4Zd=$t}ONXW<wR`0A#fo?3hoU%- | |
| 4C!ejiy9l59|&yzh*pi6&>!w&%tPr<W{gG3P6G;>##7h=%^pkidW58S8U#@uN7po<>eIgY`Z1oyp}gj`drX%#Gg*Zi#2P- | |
| 1A3nw#K5}QMNjN@=mbBC<YVkD<PQmRluv_EE1m)4F2}~bzI6m^^r`4ml23keS?fIA{KrNciAI%X- | |
| m7f;G5D93}Dp6dsO&`;0uA{Lub|{U#pry5Zghf`_HD`VrVg7g=PfP3f2njXUCr1*2BrFj=F8?Sa<9L=xsbu~dIU*%+`C0t2f^}+Db3{1g){Nd*1cu1bM9`12 | |
| DKmk9S_y%m$+c<u(?E*`(};PXAW0E9F=-maY157vK9- | |
| NB#g?<sL{JE<XT(HMV8YVGB0+&)pZ3k`1b&aH%tXZE(*)tP+3%4weVnG@V+~;1sd$Ny#*Z~RJ{Dmn!tEvUK3H+h0I0!&*PXu#6JrXTmV@{hZOku-sF7%oG5W | |
| M5C5^$BpUK3S9ZxG_?0BrAaVK082_m$T#)hT=3!$IUnO_!9V;5ZWCR_)PA0hYru@cKiQF$6`%ypoT2+*gGslYV(=_3>~e#}FBJT`qKDz0fXk+7WWU~jB&6Or | |
| KgV|LCX0ZtpGPlI%LT0Vi=?Dv=~5j4(7q<mBUtPBx0NbQtO`I8}Pv)!X8p7u@Z{4`h;XC}fnevGyfk#omNV~$mZlgRk#<3#ba2)<)RF2CgTQMRV>Pajiz^XD | |
| =X;hsLeFGF!Hho+r6Lj=mFh-77mz;sF)l*AD^1;uqdk)MMiLiZ67Wr*++L<IaE%LCMsc=88M9^>Ocv)etg)- | |
| *u}W1ygEP$rxvKY2`U%+J8*m&4PN`iL;#X$f5UqY<@8PyQP4G?IzP9H4gcfEq|6Y9|D!om_ev<igWJ;WS`oh%mYGOX6w32&j$1)4rAQd(1=ThrDe5vYh#Ij3 | |
| 0}0=7- | |
| |h{5b)&lx%*QITny=8gc$eGUo@1K8+hsg9`O&qFL80`m{_HjpU3qzEh*j9}Bj|f<z>dYB(aJYFc1`TD0fmF;D)CO&Xad!rZ7$h$JFh)7Us7N&Ft;2qM6yPlG | |
| z~G`^@ATE=yvCN0*IKb|2n6*w)SH`cMSV;YXg)U;_uVrftbm<Ef?p8R2eS^~ev6OvrVlSIn$d@MNT8hG_-OwnoevP7~7)B- | |
| IZ6Ux&@6gCQ?mX9J*v_MTjpSE}bPdfp5T2XN`lx~ew*`}ThIzDuUJb&c2$Py2|j<0Dz%LV1_)~Jvg5Hn|Db3sLUBIyyedh}^kDXy~;M1t!Xg&- | |
| 2vr?GPRXTkM2en?&P5J<}p37<Ry!Iodz?vcU|36D>M;OS#($@N4O*B@lvv6!ciG=_- | |
| K^s(ghv?NUaJfp`cWv3N3cx0VB!lEi?tZ=(W5=Df<5D6bW)@6>BQlAEOl{8+Ch*UiT0- | |
| 9F4iwLhWO_U=7WRgfZXj(xu5u!JLT+nqPED`PiHL8sWPemI~c`Tr!X*}(_V)N5#7(W)EPh<6aoQ6FXj2Ea?Hh%<>05ygIWHRO>k321S{z&3JGEg5g=cj=noC | |
| ZbsJ#w^a6vM}Kz1qy@Pgc@0)A@NM*Uy)<1Yy@)ty(JDBe&{0r{^QD<zvoPEu0`Sx}e3@GbX>sBGP6mfzxKi^~qaf)j{T$hSbVP2Cd2+i=??O$sG$yW%G~Fr! | |
| jCIDRz%3S^Rt!B8k*#@f9swK}+F_l~0`(Z6PAS- | |
| )FHdhF~NO<dkU~MvvkB89VEdMRFZ6c%<QJFutHcOhtnTzsEPD@U#(TBBa_DIDRBdAIlv+7BqW=jKfEWP^QV4iNx7GCKx^j6Gh}vH5OKX<oFR7s3Igve}xfh@ | |
| m?ZX{2rg})<pasb6FSzGe;z+LM=<HHrqXl;AzQPwMrCfMVLL7B2eSjr$KIk+IYW5M!QEC<M)`9A;Qb%FWMceU_@<N3bh$dE7u#V0*P9AB$3mSs1bX#kMXWpa | |
| @sU@uSY&lERK-t>@@y_j#x07$e%9eT0nH2&PW94ME>YfR)QlIiy_v@I6a0t@{gxHN*Wjk_h=eV`#!loO- | |
| `N`%H`)8J<@oOP_8`f$mlUzcO5%<1j^_!s;t!?pC%%>4oA}DGOmGt_Q+7zScZ=@Du2ZAF`+qDG@V}{KdlgoNN6Z1EErYS+=e1ys9GGT#_T5&ZO+fh5(y- | |
| UM2sKva70)rA_WD50)s}4nIi%V$z0HEERk^`YRoK=I7BT>aUDE;{E%$=2y){`lG- | |
| }AAAgvEF)Y$J7|TXn&$GuQv&Ymx8o!AM!Na&_XRP$;X+@788K#d%@icbDH8(+orcje&h@2onBnnX*SJdM3(+V{)u3vFoHbEq1%5^+FgcdWy<RKzS4}mz&<2b | |
| *^A702$tC`HtI(y9Q_{i+d9|ozVk57}<UJLN~%Scb-2~Ug9PU9aua(F(LQP|?0M7RczR5pJ#X)^+Tk3iu+Ci8pD0j34gL|8|Vv2dC!OC- | |
| v|m>^Gsq;MKT+Dvw@M?sPZvqFtV5@F}@^FwJ=zsHPbA{35@D4xc_c_g)H1VoLZwidF+V#d>0Cy(Rdv`AsvSS$_L=8k26X%LU5L9i!(YIm$6O!- | |
| +n`DyXiKq5V+^v24VaLqY<<igXau2`w!)2eYW=ETz^Ng|;^GvW@$e75}KQ$Z(=r@<n|Peck)i#2`BnF*SVCL*Fyn{*<m#?#)8!qfQ4{Ly$C!R}FN_832Ul=F | |
| Mckqio~!^iZ&pd}bSatt7AoIjRe_Xw$YBDCS7$n0_Udklfo;u#{4k|L7APNW8H+U)k2T5(OBKF-24AkrSQ`8`4{hL4HhG;W5- | |
| hm_Mt5~3FE$v@jYmhScl3og6I(G_i;JhG;zr3#z@295~(>@k=l5}%%y!XK+tvq!o<EuJ7!p5f!PG}n*}PpiuE5f(gIGgGvD%w=E9tho;Md=&RDuFvz48BdGl | |
| i16p9ef#wbcp4Q>qxEVzTD9E9SdsL{tUktcFb$%p{7Kl4QCTw*@oB{Lv^dwtTIi1)>__2NjoBCr$|OHhyhMON<|q3- | |
| vM@yAJ&fzGY2&AlMTyf2b}*j39{CDd!nEsTkeYBogZSYidqv~&FwPC9!GhiIk((eAIDJg%Ad*?424VBZsg57n{IOzH*X7S1fi`?R_1PoY8Y__V7&>|kwGbgH | |
| )Uvu`2~8hG^l7w`#%lLCfr=(*Aj0kUD9U?`WQjy4&5Y$eMuvhW%em$%XzaYlbZ(EpAx~qb@e9Lg5{CTY6%B$YBI7OjneepNY<OB0;dMci$6zomkvTshKTQ- | |
| +%dfgl+NVhXYElEp(@F<T7!FD)Y#ae<<u_`SWY9qJ;|qR|MW@XKdSg||e9Y{Q1&g%ySk*+$$Tex<<Pa$yKeszp#q1D>r-#&- | |
| bzO4Q^%yq)2af5ZWFBZW^T*Jj>+$o)BF2xf;MQGdm0gp0A|PqXU()z77eS<q=_6tI$YNp45;wzTVGN5Tk)SXdQai>F`It82TGmEH%tb_y_6Rle9?S3%iB(- | |
| gP9{IimcJY)kz6$`Ur7TJpq4S=x<J~aOhI#a+Bd23G?*-)26ZB8Ckv?YOduHmwHZ$nj=N4ef}9edHhDa)@QNk~sl`WK2iCL`N}Lg^XiN- | |
| |n#x(hpjy1<IyONqAEY)TYU4{9rJ~JvT5di+XU4T8ng-HvTGou~8BQw|PMb)?HB^bG<%iQCA)Z!vMJv{kKaw6olBWe?X|R}0`MJbtxe8jLSXvSE5Xi<4sS- | |
| @9kstD;KJ8n+{Ipa8wZQzeV_wF!B&hLLw+te+5+F7H0c2=tJ1LEtU@|DTnX!PEF{FajrXLL&==Vs)(;y|F#!P%9kwnU|dxS- | |
| b_%zm5jZWj|vwLLX@z)hL!&S;AN#>VGo8hMO#~VH- | |
| cgGSld}MGi4$e=L9X_&Zu8+(fXU(+?KLlb-`9~RJrI1{w4+MqOP+IBK5N@_(cpfMO^7!NR*1+M6l|BxX)9f+SznIDIk-MTnG@cep5GhJ;4e7OM!3+^eg2- | |
| 4e5ebTjh~uM}g>l}pCTsYZDd$=?<~k)oq*!$tgf(eG0cua- | |
| X^Y2r8b@Vgj=IjQPaBV=jUPOMnBY2AZ_Tev8{4Oyo;OxJMI<gYO|+s_txJ>a)HsxB5sex!cWMxuo<{H0`1NU&xEV@z8b6mmkwA@?B9bg`hM`- | |
| NN}GXL0wfPdq*$36KdYZ*4hhM8jD-zF0x2RuQK`|UkVJ||w2N_M+6)NeX|p~}Wc~=F2auwMkF!Ee!XB#%ecFj7g0c@E1?c=q^l4La$AaVfw0xXLUK)S7lt*C | |
| XJo4((<T9=y2j?- | |
| +LWGI+2$P^a=3(#`$$6Bd@WX=H7ONP}V=Rk5GUD3dY2VTP9%*=5rs<;$qDCY?rm=f0G3h$CQ42@b2;rdQ@gt>M3)iOw=UvA*WC_k5OGr@TXx9?urvZt}KPna | |
| !n8j&?(PKWpN8kX|Xw90eLajP${!-gDD7;@wsZoQ|5vj#DYupMo7CVswyR~AQHThmm#P2aW5|qT{pVeuQL7qlH(ij^w0fAadecFl0(?-x9lL^#- | |
| gZC&bP=k~TwFCk+h~_;q+C2i3- | |
| y<yYXs#)ES|mS25>Tr;JdLeE8>P7Bb>s&DBLA`eEKr+vEae)gRZXC%RTZ8_br4C)5TO+{83*IM1d#}R+M5J^j|4o8+3iut>=9T3)KX<#iy>- | |
| q4q3F?Hs0(J1faB{%^o2gSA?UwLG%EUY5tgM{78+QfkhFPk74siM#IPBc-qY77h&^9+VeBRX@PMwleXud(`H_R(q?!5JnsA{+(ahj&Y#%tk!T_Ui`hlwgk40 | |
| aWJe~h<Egob6k;Nh2}}#OE+%DxhJtDQbjL!mG- | |
| (f!kM(9;hx$DRhg=JV%*1*aa~nQ}co_5hhZGib%~RL79Yj(Xh>U{OY7Ds!&X@^ii3Aolk~tOwns&+&*H7lBeNWe?6;auebZU9(8YFMmSfRAJs?`#mCR*1@P^ | |
| U@wK7yD)jl=bkiOQd$vPtOEfLPMvHP@_hGqhH1j++_RsxeL=6WbV1lg>Yq8N!e^Q+C`;%378- | |
| ZU&gx{CU!5qAS|*>ErD8SSCXxmD?l4;c7EQWTuFeoe7$dB2tgxBhA9N4u(ju- | |
| (#ranmIm=W*|Z%@)u+v0%`PV*%*&})hybNKeLAjzlF#cG%Y&i8ZxmS%O^dGW?YXbXY{NYxsdC)dIm4)(|#DGPm8Z?xjHq7l&BFZTmGz>NCKohQ=}*gB$EIes | |
| v|)%S2pfoP<9R^hdqC6&P=d2Ep=gw1=M0=LB;da1pFRJY<>p6#}w>G!EUXRW{uZQM6y|%oZllXQW`#1CQ@T@6q%^uBP~8H%$pyi3?Ie)9)V)`D8~<}- | |
| |(?U|B%xB9-}yqW6Rlz7(OOs@w3w%VUf- | |
| 6u@1XONJbHn6R5r9_sGwAgji#&Xc|8ooEDyNO~_9R8#yw^fpW^OIW4i`{rFQd9t(2gPtAG^CWss<Y7nPQW9iL;S&u{u5e~b@80B>`3z4uM4fvWgi>JMv^=YN | |
| *)21y?gURXBSZE@%K8>HzKg-j2dbKI)(-;y#VV0-O@-$w>b<*KuMQJlI-RWbI#jF~A8iPSrxx>d<pH_*3F=+rf3)D_Vpf+0=&+@d{!FaSjjmUY7u4uDBjY4^ | |
| ZS)i7f^H?n9F(c#g<TTe6D%5yMkJ&koh^H;Sp7FHgcv?wzk5K>c@kmvZFz4qkYuVwn^3XI`6mR!9!)c|$X^<CBgCOeTjHhM5)3_-jWw||`0- | |
| h!Ur$Kpm8cW_xb$FTxn?H}=BR`w}L$>LosNVWSrjH~zEzk5ZyW|=clh#}(m0TBysR?9U^F~}VPah#- | |
| {K$}S%?PJ4E3QAz43WeP5g2V@%pITx6l1K47$TwsktrD%!=gk7V@|tAek_ecpf)=gi!nqrp7wgi(`J2Inff%C1!^aYsLlGcJb>CM;A!l=8Zb9&1hdD{v&UHf | |
| V!1vo^XRbzugB<e7BP5a_AVCY@r&bWp;O1$E-jey2*Fk&!RA=9ME=lv7Sp7WjroK6G+=DhLh-a0^l3G- | |
| `31+1K$$;ght$|J)G|=|tJ0@2j~~fwem05P%;wL)=9d^hmJO&$>eFO7kE|9V#E_ZXq{s0z{%9~Q+Czi_rKO`i#_H3kU>dtPEql&&x|nN- | |
| =JAt)t|5HvSO)P?-baL&_Q*MSq=Z~sJnb9#{Iqa3KgdlVVXEWD*y&>>!$&A)`WQWYgk;l40UWK6Oi+RGBT<|t%6bGD+9QWTjWuhAefpR-KMlC~{8{b!Cr9Vc | |
| m!B3<)oKsBuAa;v$`Q##<|h}nsm7-TvqT__CKARHVYCt%gQtn+r?Kj-X;4~yf@_Ab88Ie5<MeS9lol_#o;BAj9FbrsEtBTDlmInYB- | |
| C7k`0NolL#`>3>#X@{fq)u;#Gk6B#RsSrjHp31kXj*#TD&n<Frp^p_gD(1mWs$vENex?f{OTvaQGKT0&2o|+P9DKv|7_<#N=t?)oHUhEm?S4K51GBeVSNm8Z | |
| 62+daN&Mh9MJ_iy@LQ=o(7Vrv>9_{1lO7a9U<GElt?<$#6s<cK8S!+{bBPX;286pVLf4+QPU-mPown8W{030!4&JpH{lvBa1u@81- | |
| p^14u|TfrLs&kU+!L3L%P!wrm+SY8(w)XjNmd=9iVI@lr%s^l9R8Gs(w~K@^d^cv>Egh?Lu7$ztm%Ypt_kX^=5It$M%5!0BU&YHOn3V?sD>l6jz`@U%s0JS| | |
| k7Hsfh&@ifQ^sKJb=aUg2)fEp}HGkSz75VeGW+9-1@X5ynLpeB!}v0E62kspcE$6z~=^yXNFd1K{k*RpkMke2$GI_esvM~`K=V@)P(hG-@- | |
| YWheueGE{jF#>9YxEUFJ8qL5s&_hJfz*weB3v>`EsY^?o2Rf1;Qe=}h8yG{qO<H`qbyCtJSBnO@dNjE}&?oZKvL%8}- | |
| M|<YwKy0bnLvuif{Let3O8yz)W<9;HP%k8=0+_ZSF3^~!r);X)uqWqf?7Q7yO;1Zj<lKC@o{#1{BbcGKBjKg;t;iThL2Uz_yen%bll7sCO_~{`B}qeviUtSz | |
| -h78{1dbi8BY^Q<&Pz6k0q$Qj@GCZYR{jeO(V&#Cut_aKoy~+g0eVd@sRnma`}PAeH6)0%fk?v2_l6PM4&2$2+S0bnIe)WKrJyvBs)WdZ}<o@`Dwg?pke$FD | |
| 3u@*t+bY7h(IKf9}<#81clZ-0yQX($Ukc8n2p8{1ZON@A&CeIt=Xk%2^b<&ZJHcMghCOqc-os$`Dq+X5%#c|!nh(?s3MbQiU{^=a6**+Qp`jq!xO2SCIZ>%{ | |
| 6cXvkMXpW4#u%wBK0Y*tKjlOelkBZm!GFko8{J_>=0PYg6mi|e*(dEHa0)V(fQ@`(;(6Gk%8_Asrim_@oBR@Esh(Kf$>;lRXaU>TKTU08N<`kRkSMfX|Ry=5 | |
| Gjix0y9M<i#QFWNg{BvQ^(1u)1bJqBh2!&F$@unr+qJkr$uug=dhVXVKY%hjV)|u)c7%;#t%~?KBf(usR~F7r- | |
| }%N&7{nl$%mx@AM+6?a2lk>(+Kq}Svi}igE1`1w=hoZU_2Wb&$CBv!L{`C5$5qDQJ<EL%}?;;PnMoW;5!zUoff9Jj%V~kMU;MarbsSOt&+5vOqGo&w+?j0Vy | |
| |jh(`Mp}S|U3C(TeslAB!Ird3;1*QIh7mun@JPGSr|h5<ko={*$qL%#sK?llWhpI%+)ahm!C#27Ov~HxZgcmYC- | |
| w5ay7~?HXs^3<+10V2VuKO$0JAMdF+vX;n?w_>mh^OAt3h@yC)u)Tr)Q!TD)NrjMztMDnPuDScYBfpKh~Mj}2!m5rJV@iEd(1fHKh2G_F$<HvX#V- | |
| 6SN=q61d>>5f)Powf4L7e!QJnK4~%^!@X9qrNLIU<zdW1v4)BuNCuvqVDeLndWlT)2NRzwSDWBmyKbEe++7f~OJ3j(N<BsXUQfIoF|+M+kN=mWa6yqloZ^T! | |
| (vzd@LjpRLVo7B*Ar9G0W}t7|GxtTgzmKAu+`a7PH%9qCn88*gd8+5h>U2F-s)qQ+V3jm- | |
| 5pRdWpbdlI~c!$^7|^L<og#=JMC=jy0QO5xTS#Mk3*bt!6I~{`@pi1LHh={&Zp2*(v?J2F4Ic5ee?vqS=pG$&dLn)F73cNL3MP1Wtb_rpE1HoY6=`C_s%mef | |
| %JtaZTZfOqwE+gUQc85#eV%LTZvoT8>Cyw^k5CL{^_x8$~2KJdK)k&Em<QSfiGcBa%R$c8ac8@e$WWxnjX$w#BN3z(2cU&8}E1;<Oq~vET_j?Ypm2<fqMskA | |
| jAeQ}ug<>I@%Yu5EK|n;jp2T;w%;l-H-x^3zBPwP3w<dIDtrz-uwv$BON-=w>1lN}U- | |
| Gqw!Ehj+Qh@vIr0<{jo+OG=Un;Nd%a>wLp1VEd4Pu>^g0JS|Z9L1hV;adp$z2;<Um7)bi3ED`7pd89l;c@=lL+$EVE(#^n4oe#Nz*;JPrk$BCIe#uFY{K{Z~ | |
| pM;?t@6_!{MF2)R&SVsz5;ZBc4Xa0m1#?-1dLZcRksEwzGKxjqNc-rgw@ia&ic+Cu{C2@O<ojw-kjuq|pC_xs1=|O7Ih#HGS7NgxG&+suGPvagxLf-kK44%f | |
| p=6|2Dq5*@4afKX_nIe*;oPjWt|3s@96qs@yO6I4gT<773$W${9dI$uyY9PiCIhqJs2A;+Vr$P028srAlATgpgX7Wh##{z!0#~D#8!t9a7NF><2IAwSmh>RX | |
| #QAv7Qs5=(O`#o}^Y5Bo4!IB2~Yg#Cp#=&@m3}sEg9V- | |
| k`n{HJTPP(ReJ`&ACAZ1nKriqjTr%^nN<vjT{p7zZsJS{$LhGdR4xvXnQ#O9~DiR9(;v(fq4glchP$SUevGKCsY($j>iTYTP(WGpB(ZH7yqmem~#h;07%1$v | |
| 3hSejIJS`v5uOhzIgObscM_Lvh(gW|2RASIjz4EzudK&>h-5d!rQnDWzPJ^5!#ei37=*^{4(#y?d{er{=+<jheZW`- | |
| r#T3VhauTJC0nwhdbjbvdA@ihMY8Z`!2{%M)=rwY0T5lpQvB0rx>4Km5p#Hjq4Jo)oU)T(kYj#H=&r;Zv=`&J%LlXb^RBTmcWja7v;mZ+TwNVaV9`m{`4TS4 | |
| >361}n5)oHUg7KQu>>F_jQC}|ApG{K5C7EfahyDrL@KM|ZJCrwLB3W=^{M<UaXbZ1p&f>Ob>(cKvlO5-0u(yAH9{BdN=46&erm^=- | |
| _^l714*AMZul*31ggGkYVpq1u<PJ_`e;*phwE0QG<R4x!S6RAI8hFUzMe@+A)=OMzQPn(M0V{W_0us$ujKy6}fk4eR8$7&fV4s=8zs}MzFHG51ePlIgwG?@J | |
| !pVy}?Ucl2tlddW9G?;=uEk~nvV(PT8^2SB%hgqM-txqdNejVGYv14jn3boMMR#2}N?j`cR6bIvUc8^d}CTJM%ksDVFVm=buH9D|Xf8KRHo{!mTS^*ZuJiyu | |
| <cU^8G=!am22u*TLB0W;ZkEH2i1rgVB8Z}PIb<E@u7DWZkOhs^=Ri>6~^a%VY8fo;%&f*7o8b7dT)TAgPR0rdaQ&3z3iSd|u<S0hrhe**iJB1$#<M5w=fiV< | |
| R&w!#xE0e&VFcVZpb1i9L94xt(R9tI3?YqZ#8k7yFohY6Li|nH7O3fZq`tw%^r7<u?fSdI=zPbe$w``JYnmIoaN~0Gy2(E4g**#7TPYWGDR*$HqkzE(XeiY4 | |
| 669ClaM9|{VG??Ku$vjZV45z)MPm^{qrr>GJlIujqSjkO{X_Uv3bk~wL##3`K1~z&~bS7wtHpZp67)P5JQ~CVim|AouXl6hyXMS2DI4!GzF)0o- | |
| je{{1ji!m_fwE9U%JajbvqK;soEFX9hfwl<#^^MTKu~d?$D|&{L4aBWp7v&x- | |
| y?DQ$m=Ciw2g7Fmk8_d5z=YYq@9d28W~HZr`6<S98KtFpFqY+u%&5`f)mITA!^KiBBKfYQclLl{T>+@BHR|n6*?H_HZabgI8(BH@r2xq`N)rw?!~- | |
| 8pe33YSHgT8F?TF2Zf1l&ja1Mm+BAZDv8*#z`h4qH{}4fItg4(Iqy0k!5ViWvLt+V!q7%qc%&|!JA?&>wmnDC&fk?C`zm$c@i~6(?V@C#w8Z73R>rzr?lBQh | |
| q@k2(d(|})|RuS1To9McDV}AZ%P?9k}3*}KDJ1s%RHN^Nmen01@&G~6TbdeK`Pm>D;4YuV!R^8?VwT#6rHb8C8PfM4dmb6uir-<- | |
| &YOpBM9xH#Z2C`g!daFipdQ2LiMr_qm)Uu@f$7G#aF#C}uLXE+^I75V5_MB@Ho)*jy`M{Dk6Ucdl_~By~(xYJBOe)G_`GGT#nDQu%sR0F43(f|GMQxV+sq$v | |
| R1=pf%M@UI`WD;CQ^Bo}t;V}YFlhmgbM-ky8@JIVS&J2<4S)ft`5eQ|7B&lW;K#dlkmM#iZI$$PYep(&zX%JS?!USqO`m`T}^l9<tShC(&wYl?iN6m0qW7Xu | |
| 0B@>-iXV}aMXU)v+SkrgMN~%vQTAn6iClWYzEJ>Y4YSc!cX%IzyglIsG-@CYSaT*Ov1Aa4+GOYO- | |
| &@>K(8f2EJaVykf*^fcZnvmZkuPJ|86A@TQc|HbGA15Dl9oeph(nWF^KLTkCNo~|bK(*OHWK_nrsDa32{6ix3X(M{Hgm@aozgW=lvDo;uEN~j9JPml8wA5Id | |
| m^_W4N#pauDkVX!M9arA93SN)LD?N2a~nQdJne0Azegy)SxfKFAJ(dYWUqEAevi_5GZXQ9tP@iM4p5EKtWhLtd7Cv3LlF_T$Jl1=B|ME?a$UCKI)&gmkjbA$ | |
| o+fC}9~(9Uc_nH=Lq}%E$HbUgnewzUa%O@dHJG$QEpysTAfQIpr;(<Q@s!8e@=<gGDW*- | |
| !<@cD7B~rKBBYVDeCXxt|%AYcBh9PZ+s7(_gh=@`7qa&`Lz|)9=Yq<b5;H5kY8HmJMJpyMMXl#I5Y58eT2%ZMAeveR^;p6x=jlq_mS)K+d(?Cf@P1u&d%>1+ | |
| v8h?b}<D0J^<7w<{enB#SGU6izON)1WWaT}EHESSDdyI8`WH#te>XS7kgZ}U#WC^T(b}S88{T_iIOyeSpq~eOiQAK3@9!cXzO1D<3Gk^7RYe8#%DYr);MO_P | |
| +VpS`)24=N&Cd)^D`{Gc!HI4nqF1W5&Z%uN0gsl2BNJx4Nb`hzO^q57Tme}%9#7jgrcBYur85mR9W_?<k8DzYTNU%c|S)m3rjz|>ek#YPu8NbJ9c^X^53^O- | |
| `wLc4k(^%P#+;Cbc`PPAOS_u{+^08^TqCj~9t~tU$AtgVAOPdB|z-f_$M|crWd;JogCTu1Wh^U2n^GBDrB6C6Wb4A$A9^- | |
| H{UR_#NstEt^F>8Sfo;{|c^Hb{^dwC-nWK~k#2=-WUPR3dN9+?wCOQwj_$q)(FsMW*}NmtnFX7uOE0}U- | |
| {?5>aO)>!EXBE*U&k~Nc=A9B2+eF$XpGsx3or;k7xKSF}}BZJ-=Sj|Hs4f*3ej6seh5-(2U@eYY6I|BCzGC?FLN7f8v(Wm9KFlG- | |
| =8v&&W1cC<Dvp_C?bRg&>hRDa7Q)bGdhm^&8jJCvLwh@7{sE?;)i3JPkR8R(n$Y|Ol2S22)n3?1Rky2x3UhsQ-^UXLst%&hsu- | |
| #+m_%TJpmci<gm(O3EIE{1i$U=LBlc$RSDdn1Z@;HNOWGj)1jEv{}w5Ucci#b+VYHRK`ZFDu0oIhq9KgQcV!U(@d7JDplcm7D@Vq%0^DG6#sgDl`8iO>|+<N | |
| Y4T_iCYDA|m><sxq!~i_;>tX;6_kEyMUx!jK=Tlc!}g<cI0#)AGyH%2%gFi>?_w`C0rP#q?=F5vT#9LM_@sghmmWx=4-9zBnFCJ8{d$R8Sf- | |
| Ndy+$F2)QlBHVGH;b@vf9%xuOqgWV22<0&bPy05t-(xy9KM74M$?Y-o__1sve=Z(>0bm+cn+7?>X~3&a6IWjs7z|p6_PRhkEu-Hf%^j<#@#6_j9}5B1Vm<n& | |
| K7TCI9_ystu|y5}6G+tL$&XYve{g;pF>WSQq88_mRc_jhphH&V>@nHsF%ga?J9tbvcRW_bW_4OA>NH@Gr@^dG%coBxMO^dZX~7i!Bo-nx5<jrA_@iz4V`HvG | |
| ?Th0hW<tIB3r)HP0UwcgJdK6S&y1)AllfVKL1SG+IOJ&~NRRyTG)`BnNW;gc{2mvN@ifuOMuO8gsE<kb{BruVWUl;>koj1W=_4%I&9Q*wClXxNpbCF1QQ{*@ | |
| VUv}qg`#TFBDHaa4H&qOjBNgFeviV_M@S@4n-=@AmH{NEkx0Be4OsMPfjwHVh4I8Dkb+^LEK%2@#7E-zkv~06A`LV<^$~dRGzP;*N+4)-- | |
| b^q#B#C`7r{Wq|IgcqJwXuma$raa8pjr{;#WI3x>CBldfLeWW>xha*3<Q- | |
| cX`z5xPQS;h5Vb;cX7b}{g@u9g4jtvOw1SeZ#~^By!6OHrCK)wTm*5&g13{SpH4a;T5fc$wBB;jGz8i(7mEk2avmeVb=PzRV2+`w59v|asrPq*0dp$`8WPLu | |
| y(Kg1VDX)*})3U*7(QYEAQqk%$6M13$$XC+Ja1%LF(LyJXPzj<2WkYJjiY6L3lNV6~IXZu6NfWJTA2>YuOR(ghIghbXGdYKkrCIWGM$OPBk2B*jE8`J(M~{J | |
| oN65- | |
| }Og4K2NzUWPgsxcWGG<PsqQ!GWroiHl#M5|MwDL^(W6Bw`BY!R=En&cQI7cL0a~%q%jWfpL51J{7#jo+SH^t_s$x=lMDs3UTpvhuEM;Eq;R;|+bv@mg+sN$N | |
| fQA=w1czWWrY#D0lBh+Sn+Dq<Ory@@~RdpKVm8T`-^3VQQr$P=92Gd5+Ltv519jgq6$OmCIe>Mx_;8@VSe10)VEeD;So}M<+zc^=p8i~#y- | |
| >HRj9_eW_+03zEbSfxu0?EP=5j4lj3#i4KV@1Phz>cSp;54!HG*ZA!60b)Nz4Z|Tk!W=q2~J~ndt@~-PEUFqZO9J`VK3ugSS=zCv;- | |
| DEq)Sj^;D}5&Kn>)0{Nw!|N9Ct|Uo4)Mk}E>T(+cK367*?FnU5*pG!Vek<Py|K1&!=qJY#9uurwl3gxaam%|tT8X|O0KKaFGh$ZcW_k^D!XutcH<kQpfb3F( | |
| h=1IPj?{lT`6Jn?Der>8NOHkQhkE<P<(bj<{+@w@Yflr+Zpv@p9z;PQKn9Y030Lqg-N>+^bqJVgG&^wy-DnKH!Ip=SK)XO1L(h)BvcZ_o_C<zpd^j}nweqJ= | |
| Sp4Ig<!u7m8c#^7m%dx%Vo^T=!WSb&E}GFz;qaGG>9Xc}LvI&HBaBlR&7Ph&U5stiq&<UDFT?S}}zM^P^k(fqWc29Jr9*F^- | |
| V31VuMC8$A3GPM%Pk5%cfVIi0eO2gF3cN5_oKaRoEW&>juB0mJxGhi)fyscW52F78fjED9(jW&(Yr9t7m$757}U=gXMYSoxJHI{0Yq*n{gPh- | |
| N<iVFk<25lNdFdn)2{L_+LC&~kro;*^lv48<mn<dx9NUkN_i+Py*l?kq4!L7Ldn3BamX~V}r7C(%pJHjHrg)uCuF?<|f*2qNu${im$SRw*keoiX?v4LZ4M1E | |
| jn^2b-Tyfpro@U*ut>C- | |
| r<BJuh(NXCGS!qcMXkNKU9<NO|(!>&^Wg9^|elg}UdNRZJD8w&$cFc~zXOpVmCW#vBtp<^Rxug5z+9^*c~XZPl3r9SeaY4hkYph=s$-6JHOK9aK2;uY5- | |
| lSe2aIgJBKV@J|}0;TaMr?IK6<Ei|D%*Rpsw6u!rWTeMXmPm59M=4MK9A=LZV(U>gjkQA)Cp(6BXjKqIip- | |
| m#4IfjAt>gKQA_>>rgh!CePYaT#z2x`E9SBNtFHQ=lMG3DV8c-u?)Z{EgfE`j}jRj@y)+B61s*6zrLEa+?Py-602H_-rLBB^>FnAaP52D5bsExG{8PDMdf-B | |
| Y&c- | |
| r@0N9fat<VT>eAGxjhGxus~s3HW%$I(2IaAST*)T<Rz(JE@!pzO*PYmJpOaV8W@1M$QeayV#YWdlBUEHd{or9Q1VGC#y9)Tj)RJiS_?R;>h5fAO4tfgCj$Q` | |
| APaYmuz}sgU~9c!^}S=AUGK8i<Y`>+}$raKd#Qo;Lf35d1?<#y%t=o+j=e5(ub484e=!{Io0<BHS6*AK5vN(eku(5o*a<kHtEOz-SARnCvtV1JX($@t44Plu | |
| wzF<?eIX7fZ*?2pbra`1_E8zz;zTeqICP(2Az<wAXxi8Uy!{hNqo4`;nFWn9}U=#mQr- | |
| N!KtrL@kv=Rwnx6bi!u1*pJi%wX}E|*&XXWciIdD;%O8sk?9K5So3Cb3)G?;wc7M)rR9O9SEr?mnjyAoNd;=eUad-r8Y`KfLVg^Brv-cRlPp9cr;nuJV|D`( | |
| NHc%TU?Bo><PcJW7DEoH%fUE#j2g}JQP4ny-9LmqL5<z=k?tT;iGc`{- | |
| (x62gl38*8gL!zij`fTR<b+|WJ!;Z3|A9XUKi&jl9MG8nlS^p^l9vtkCKvW3A;yXNekqM!~$yL@w8(eA{tNoW|ZF}cdHgy*-ky~TC!6s#~=$9OmWv- | |
| 1IXm_$K)kV*dMC|Z~ox<<AfTu#3sgaN!LKDY-#--6HOmOsUazB`2!1Dj(R3#UYts7O&UB- | |
| 226tyXqv#tk;vmu=31OKI8A{0$fHdIHYz^|@k0pC{6+aak_@u=Igd~f=@Ei_`4icQWb}LlKHekG=y7H}vQHojPn;>;^D#N;QB0pEMS2{Crx7b!poxfRIg6w{ | |
| rYdKUqn?H4T!XwyEdop{!^jvGrDk0V6ZuINBEdv{hOBFTrCNR-e*#2}kaJDoh!9e)pRZ^dPy6;{^=b9>YO_Qwxk3Njt1%b0S)f*auLgA})WY-ASSlJPn?K&Y | |
| I0>7dS8UD8<%jH$T4pN0xY#;tC5z$m^NOuYPn)6EGKzn(F!M2YISU#-vI1(ruTP8TJQiS%RUhXO3MM^{8a?KqJ<hDhjI77<RM#- | |
| m9%U_z(~$XJV2B9&Ju(nPL|yskoa;0+{#apJa!39- | |
| <~on*qo6j8SkEBHjz6i1$jstDu1{N}$I}Agv^v}#tDQjRiMyWhw3EQo=EUpr0W}7u2;=;bIqUj8hdWkL=2)Dw$4ur}85Gw!J2m>;5kv- | |
| $W8*+WbJK`~$AX=)rXsn9#mSgsaTq?v2B<|cA7Mm(8ZADJrnu&ir!l)2M@%0>y|JJuZ5n5QT10=wXJX7Iyp{@_fnsco3E|cwOpHTS*XhF3sBVv8(?@P$8jpc | |
| -QhG?dfia8Xnue!=Xn-0lk|n6YB4>nJ4m?dR-#TH08UasBYV`=rfEuLWh{PjmL}^-0W{(h<2g)CzM)rFwoFWp7s6C-i`)- | |
| uqBhajjqwzG}xS1LUki}LuM#IM{6K5zBNRTBzLUNz1vgeN}6K6={$MgnSa&;|q_y{4>N3u8nF+2?x%wg9MOXwH&=1)QBAI%SmH|8hM`O8kbjx^;5mR2o`=OY | |
| x<rtu`G5yp=k)W^!QuGy&k;q%8VVb{@F*F1Pyd2kvh<&hOm8yyK+0^c#HMT2B`TD<I<iSWoUex&jp12HveqemK|7VY#%MAV4W$3U_pr1N@=Fvj8txGoA%djU | |
| @)4joh3J+i}T8SEZI1!}Q0erCVN@!@HaMk3r^j{>2fu&Bc9QGhDK?ux~0_Q>Rl1*z;FIijvp)A*xU{ET*w@C2Ur<|RCh!yXHw$B&Q<QIi7Ha;wt<?Xd*8k2z | |
| YkpiXV#_E-=#gya=p=ZT$R@yC)4oeAvL<N>u2c{7~e{IC%8drX)JItPNr+VWQ<P%A7EbhgGyo1s=VJ49HU2HflrE;NlBOrxmND8$Ed^V4Qa{&- | |
| L=uRyI{3lRw;<Jm)G_T*=uK8g<?iI`fEmi(jeGzP|Fv^ov4;Ayc8k^Fv-VQHY%hNnf-_$3r-B{)9D;AoNt#- | |
| R+6GA)cHiy4p{AB8v~k>P2P4#uP=&Ejd_3;8|9$EVfd_sBsNDJkf>s7{T!QG>;#BWH5*L?BPA#?h%!C2Fb69%X1ER6>6-ts0E*d*t9g3i=nvJM-sPT- | |
| WIM$d0G+>8*j_BqFT04lidsv`0CL2yj-kswCI(1d({fH48r^VW*Z%a6QKFQCM+Z;QWzKa$Su?Es*udFo7&sbxoc=0`K?{@)AWdQ6AGN)WX3u=1fosR;ZQhk0 | |
| r=@jMH2n<2(xIr;#}Pg!nX)fk?68X{;m1G=l4N1|kdw#s#LQrG?VU%ub`|(!#+ss)O-q#HUdi{JGs8pUhADVU*t^M>1%Z(v~?Gv?v>6f- | |
| _c*y2kAFC@^{~fy>Vo3YyUxD;Y<GB^4CX)ioApevzb^_+U_wqw~w_(-Qd?3&Lqx1Zq>uPb<v7_ypu>$- | |
| Rrqlc({7r*YIY_Vly}xiu7KV9edC$?MZX@ibA+<4k$XE>9cnA`%LvMVn(yU7aQ#qgH}EjRjGIP)Lo!dE`z}i{SH1hNuzXv~>D34i- | |
| Np<eH|p7G#J3g(4EldkjkjO~(?6^?a0+T_;yv*HzYH0<U9L*RUvBcFj)|5oGeSkVNV<e2k>=TRiQp44xJ^eGK*HuN+Rxue%- | |
| (s6k>xt+e4IEUFh>Ltd&#Re%~%n3jzw!iK0-3#g62)2O`~ks}h#=TB4AGP(0tXeTnJuCa- | |
| (^LdE`RyAUmMoXJ1M{mu*<}Wf5G>_gI!Ufml;bUgKwY)wpIzLU)kDoSrtSt_- | |
| 7DN6~Ge<~{rj2Ahj+Um8veQB*j#JMB6~xj&Se}+L6f~63&lU+<tU3)+<7t!twRnPvTv20y({h(J3YeA{ON$Yy#b(WvWQ+whqG_iFrooJ+g=o~qJ3dA;L=aE= | |
| ;cFqkN80dlHs{Z8&VM@I{IfOxT-HYAg60`OCU)kJris+#_ZU5VB+MQu;xt;5R%&`$p5Y^fAp(LF5pF9Hp7A3=phk3iOkj)!B~Bmt{Y2PVkD- | |
| 2#DMQpo7(P;mk0rT@NZ@Jg9wK=(*U76|P<xgv612vM>u7RFAtoYq2(HQXj6iy%a2^FHkJ+F!s^B`gjYuHradg5p*+b- | |
| Gu!Hdk(qk;3#xQi`^n4VYIVSRaEP?XK%M%H8=I6IC4&?G58#~fGjD;lEkZ9@%g|i- | |
| Mru@Mc#>{!33H*x#<<^X78jOe2p7MMA;q?o6T7mIt%)J_4Sqm0jkN3w)BT%c=9SiccYsJLPL||!<EI#c7EV820W;hgTX|vM`Oq)sR_eiN|yn^e3^wzP}3=- | |
| +BrL)uI4UCz<v{Tnx6Lo34m5j%jzdF0eqK1#L6-~~-80P%6a`V$lWAn?$r*WmH!J^vuw35=(m?LMV?}`-;s+Aj`R*OO{JU=a;gRv- | |
| wKfm9jSWT161f80TaTWTsI^=0YgDmE{mM<1GnTK&&eOf95W02LSeNzyg773?K3{8Vmhtr@2JPkP1X@wzbGoUt-DI$lb&2ZXiI1Q$Qr_BLsk^yQ{%TMEn)J`) | |
| %Efc1emK`#&{InTQoAI<fcv@sl;~qUi!L!F~a9V6lD+o- | |
| 3iqSNIu_F|Mr$K5!ZN}4N5<yb}YNdK&#ZDff{!q}m(6mWJf>s2kK~gjg3V_qN5<#gpBIN^W7Ek+@j;EC{eGKl@k`ww*L!Sm??6D*vwK5cH0)QH^t`!qEQ``g | |
| +WK-1Y)~B5|o(AkCtx$a$<PfMq8ig99g3}m- | |
| (}F$ufj?}fG&D_2Xq^d6qgXu>;WXeThj8F&{Pj#IYNlL3jSWvLfy_VSX`)<yd3_oiqBaqI8e|rz<wDfTn>?P5KCN;%4eH0!qU|1Md0HY9W5|rA!7NUbO9rh; | |
| o)+x*7)pKQ#?vHf8dY*#DW0}Stxw~%dxV0<j}VNfO=$iYV`D6*P-BX_&O{W+p-?MMpjN>cav~D7Jh7lbh#FIV8cm-@Leo- | |
| 3sLlGcXnk5e0cu4U7|;5&67^}o-lSEjPb<Ojk- | |
| `y4jHk`|w4+6B0@hd+!D&_bJx<r|aXLi)Lg2L7?J*>6h6_&%Eoq@V{&av^dU+aaC@5!qTDfQ%1jA`zO&Wi8TBX`FO2|wZ91(6Tjq1!Vs!juWG>yjOkIe)nM1 | |
| qdN)4r=4PpdLM?KJ89`Sa7H(X`p^F{wO_1xqVDKP|7{BghHVX1~X2=BL3N2U@e(dQMO~3Ae{nm8TVkrj4m+GoF@Op9Zr)Esx_PEan8Yw5E@zoHR2D!^he0aT | |
| J_32dF)qJ=P~so37tu4C`^uPMd;_aWqY&LVa2mzemoBrt!2l1?8ueI(?k;)3VGTtBX&Y2A(!`zsIWZwAt@b7*89)eWXwy#Uep7SF{TGAt&zkn8!t=IC=<hmb | |
| 0q0)|nu+3Jo6x{T{<4*E60r`#sKnkF!2aM4tw;K&^Ja$BFCHNCw7`Fg`8ZNF+l|12K|_AfN`^h#E|TBNAqeRX;<7x2Qn@0yV0UNV@biNG(uf&QF7dl;PtDJn | |
| j1z@HEIJP- | |
| E6zR|%=57hb0nsAUG!=tjn+NUzJm(@1byXkCj3)Jjoaa|^HGSYczKi4=^e9d*Z=hCYp^xK81W6<E%4xIM<DT(cR8M3l2YKn)}iwGtB4BAPT{;PFE!k)Og2iJ | |
| v@Hr%;1XhR6{-ZOq`2s!$8- | |
| )8crKB)i8K^=Z=XSg|I?9C%t9U;Z#atv=%8sGMs#JPpPJYAm^+p`QHlWi3U*wW#cxmm|VrVjPdBk;G{XZjW_PL?F&aB(S6<#?#UTf=- | |
| Jc0y9CR1_$FnFpXvGIP23+M4y&Go(9?VY2zs(WBeX}VAH3;EKn;KO`G*;RqE5yN2h@#pMOGrkJFT=!O7{<NO)SR(PL<*CKaC+%zCUbJ}o{y4Osg$5~h}N^jK | |
| qn+KD+n(t5OnXj<m<v{a+VP<dKvzsC~Bk1)?4XTQh#0@R4p#}pE^dG?q^p;nMUt%?M-)P9fYX#ByvN1*^U=KM5B!{d)5K0-M2k&*aFV25zA_+^bmrWT- | |
| 9x`T0%32HRvql`c;R-eWwPaA`$y`|%61;nQjRo4mfW|$2hOU+M%67^~0HP- | |
| ?(k(wN`^61lY8Dzo9BWfiCsKI#ZV<4bL$J1CCA}}F}2t+H?DyN7D18Nn7sLdH_lgUtv7pPU3pq4B^tp+@eTb?!>KIXT^nzY>`l;Mn()fkJy5E&u4rlM&jWT! | |
| zONu*Z4$51wZZEzYS&rb`p<YzX;N|T)yEw|>i<!2{|#2Sf2jU7qZX%N8<iF6T}^V8(<G#W*u#6- | |
| |?($ip(BR(z6MPvk?_V()<^3z~32dHJ9K2FW;agI+bIX(>*bADO@aN4X- | |
| tKaW2N}!enPAf{GmP>P;Hbku=$H!Tq7F^D_(KKPZ#|S))!|rj$)8_oNa^bYoiBE$?Hg~Klqh_)NsLlCl#o%ePKCME>M>csH$RTRcJpOojTFH9rKq7zAidK(8 | |
| ?X>VT7!h<0v5?yA_n1eY_V)E- | |
| JPon|YBQcz8JT~KJT1H5qrmKO`f%DQ^ZBzS&SZ7xj~+l0=8w^c+HrlFw3kRcM}#>Nl#%@y+^mI#sO7Q8sv|{>M1LG9x~|Jj1jf`ghQMnYP2^a+7Tc{ckRNHi | |
| 8gC?Mc-D0aALBsTbx=)9!+m^+r)7iFV8+u5_j`;VKSE}|M+rm?rUa>- | |
| _Uw_D%+K$PRRftHlA~$xf<8^ulRugtk|hpw{M0efk{=2hI)bc)NHR<QbZYDPQYPvjGHTo?%MCd;ZiL8rBe3{k(I}5ZdPw}lF@be)C^G~Cs~Lo4ToVF8A67Jt | |
| r+rgWep(<?Bt4+UTG=wdX^=lZtu}dDB6S)N)6- | |
| @&O_<J4v3ewyworLmg27`JeH!oz)aur!L7+e_QlA#@B@$cIBn4`)s3aDYq)?NF(<BvYDY9l{Yg!43pezKDO#G1PD%1!T#^KY)Lh{q%@w8x9el|QU6`Tgl0yT | |
| o&BR`lH>0wN;FdjRC97}vm=lBTJiZ+$-w4`8~Y|zZCPJ=LpNCke6Kw$BQrl+Nlr$IgXv=aCsvpg-2JPl5sBm!iG8ZS*G-tlodhL3UbG<ZRu_HA-JZT5SdXx2 | |
| <FYpjfZj~r!f;sUifZ-#OD$c3oIhk}- | |
| 6_?W=&Q8H{sn9N_O<zp(x$Gi+kE<lY*bsZzRF2e6IdD=|j1Q8%IL>SPtQ1T;f_*kai8gdvu%HU~)teG%=2>a+Uy*3RJh0SCUr_I{5?Ao- | |
| 5pc$wMP!pG@MLS~wyWU!!(GT%>8iXmX$LrG|h$4~#PlNa_jpq1B8a-BL_!vzQiE<E0h^IknK&`R}HG0(bcsy;Q`Dx*U$2@kAN5+m3c8^er- | |
| (w&{MB{0TQGSo{%4>q#BfF7E?$KjeVKb3jei$=*9FM9cin`|M)oPzUCdASp8J8ax>`o$K;c5JFGhEY0sxg+7- | |
| 6Oa1S{zRUW;%Z~MPxMfk(J6HIDJg7xE^oI&lPh$vP+{NX)qlkKU053s%A&QwBXTWY|soti^gJIERZzAp-zJYcv`GC7BG_eU(%<gWb;Q`h$Lf(h>ETuEa^IW0 | |
| 2$w@F*IsoBoSCh$gcBH`pM)+Hf$|+2AM4Cnq*?k538{TU9)J^#M%4_T=|I(#sYX64NlAB$)6WZ%blJU#Sf9Bhj9DxGht~JKxrV1rIoL_7N_wCc4~FNX(VGT) | |
| +UW$$^Vi*?K{5wG)aG~Q%O)0WIvA9sPR^|viu%PNl$|*sMA>dj3W!1v^iF;^fVcFET9S0(zs(0!e&JL9$BsV$3tmA!_xS;{3K- | |
| ><R^%*YSWmlv3Rv<0yV90eh7#VM9TN)FVgK1D04?5LxhXVFNmkb>eKS#X|x`Vfz2OEe5`u@7(0FxG{*vFg&H%R9~P%$kR?p#=g0$%rt`CyV~NuF6OJDXA@eh | |
| G`A2w&d@Lgow2VN|C?bEfg)zZHL@?o6fXJVS$DgvIK>^gqoPnV1>DB~d2&ColS7%`yPUPq1hd>%0{}XuHo5cFG9OK8L#A%iIVx{(boM(@9XwyI%N|Q_mt<4& | |
| %lG!7$lKEMtk16xh#7Y{89Ws}+THrL2LXDrqKci`Nm9%2$A^bHBB#N3Sp2pd#&3Ib8>^i$Y79_&d^7&(xho`{|sGS<11~Z~Y89){Ts3p%&lPzfJBh)5c(8Tr | |
| DY;YP$rFKdb5jgFVRtBcVThBzwS$4rSlRoYH7xL5imDkbgG>%4XG^JlM8dOYs9ZwaJPP@+At4*#|gS6?OBqx#LD_h~c8gMmh%w{5~d$keyX`$Rlj#dpAdbKc | |
| 6Eq$X_NuO4%mk1DDvADXl(Ik;$)hweg|0v0IJok~uOoY?*kw_C^FntuY5=mjqFJ~nZY4- | |
| ?Y@U&nL5q1laM561_4kC#RL^5Yw^AbdOT^>apjAg?>L6jj<Qnqy_f{1`E7CYyWfgU2_9|E$R$5a{DFXX4mW?at^*EA!MsRYe%viVt4K~FVkhMV}9$+<X~_?X | |
| bUxJoKNbIwc}*5ea++7B<_X~IUvC+YSW)~iACW=*tL1G#1mWV*FPmXC!yKFUR?MVB_|j*Y_Tk9Otf@7B1QwIpVb@vYi8JdHhVh9T*iVvRK^;v+1??6F``R@i | |
| lFf=Hy3NJ&n{84dc&OS|Up*D|GDGr5Uyq=Jsmx(<QVh-ot)5-P4KJsN5HIE$_cqH9Km2*lDIGXrYChNxw7#gb862X|- | |
| F!PB4&Kn)TgY9xM0swQnjk9G`CJ3Yh46n>B6bFP#5J&rfWifGb+uS+YjNee7&P^{l0vylid=TW>rBeZ8cLDv$Fj}nL)q;Aqi;AwAXJgsg#EjB$ZPIO&FuNE) | |
| 5=Fz6r3aF*hT&K&pmhaeRJgp);4YHNA>=i9aZ5>_F1l86;V(an(L6erVI-{;(j6f|HsbA0<i$`spE)- | |
| OR(@*sh84afqJd7FKu|CXcGl|n?Sn2%n<HtyunlPQ8Hh&yV=LZg*8pHIlEIvOcou4#(<hC&`pB+ME^GArT8S>L&>HM5*{&Mg%s1Q&~g{S3r5n({nV0z`Ovhc | |
| K=w)}Y<M2?j+5|#!h1g7N(xsFenK|JmC*Iy?$e1v#DKZVYp#F<~r?lBdj#+!95H-eleka-a`9)m32@nf(- | |
| 7TNAG7E%KOrj{2{i;cR5xalKM$B*x)8aI;wRU=B&IN6W93bkyWk23zn5YwzpCv0X^QKQJND>5)1QP4tJB9aEi!8{SNfiZ_Wzleh|P%Vr<WD2<!b9=1D8cWRY | |
| F;<;cxG`3ABq)pH<0+<G7wGsHo^l;NeVlg8H84*fDZIx>bF3;1AHyWq!EoC1RV@QS1j0;2fIxiA&JbZV$BOah52|Yg5=2TWYw7$RnHeGsIoIr#{1DooRSBqt | |
| RkS1ok*5K**N*`;Dxbe(dwv;(8kNuw<27o$fEuThaWaQ~lILSWgMJBbtRuBeiqKDL+PF1p<xAAUJRi$7=?}HXiuE$)ENv`+T4=8pilznfLjvg`!OTY}w^>Wn | |
| t3^*B83vH!Q`CqaSwUVR<dT-moBt@5#%7Z>3CT5i1j&M@g=VOQQAC2fH4&0XEp&b!g&K|YC`%Bb@Ix|l9$DG^f)>VkW!Lo)L`vm{#Lz>sar$92sh@!%677%0 | |
| ZC?!H{>9Av5MWeXhcZOUb$pChs8u3ROQKJMS)g{5$S<CAU7bR0iV@e<3Di=+X^>x_2I2~}Z&F)6irHfkr;j- | |
| RH3C(n0Mo||fEsZ4WTngoEjMtc6heOym>LxB`v~G9HDcThBc2B705wrQf3mz8NKby8R^AMyp20#e7c_a~O#O`h%$YOAMb3~b*&I1TWIx8iX+Y)ke@LpiPL>M | |
| FIeipN1&#DEPM0?mG=F4`1Z9BK1g4MifSLfF1|tBqc#=pVcp5EohKQ&IDy~5^5)>BFnV?14h)^}xNdYxThp2IqL{8rEksnXX3a0@No<@PwfEiDdfzu!xo(5u | |
| o8V5TB!ssC^cp48uWK4?&i^*6%e#-`^k+>q!hL6eo`Qt$~0$U^zqd(9fi$5H6B%;O*s6nZSTK0|&1bO{gLT9oCgtAx~6J14;VrruuvLXSsqvOZ-rI;88=7Po | |
| (`s*- | |
| !WEEWtb!%*nkMh$;5FkHhr1NJaht%cJpDh(M*iR(h!#E;tCLB;p&VHmYMA$4JqdAX~43S{Tb#<PPg0Z0Tn(L7q5r`&<jH5ip0ctgJL`pCaVGg+#(OdIch@_{ | |
| 85Zg0WiU`5PxKM=}DQHHLArirQ6g3ga45y_g@fU}urI)91cw!X`sEIm0hMR~a)Tq&1j00OVAwVtO#F*kC0<o;e;ndLpYKw9dY7i+=iyuA0LdHu3NMYB&S=)k | |
| 2TUI0Eh)xYCe13AT#tx><>a>KE{vxz#>|rxZS=SIcd=%@{fRdp0xLKQQh8mQMsWCG8^Jb{QqU;d0K!(U1qL#+^v9b`g0y5NOFtx%wvY7e&ANa?Q6o0JP;UkF | |
| Y)1p0$GqCv?1Zt3&^f)R;E!M- | |
| BJb&c%FovZ5SS*_BG6*6xb=nBNBP>KsviKQ~zyqp<V``BaHAY5%&J49sJWbL@BzrPwu{I)+oJUB9r~xaa#t}4glKE)@v7oRhA{VrbSWpiBe#L+q%|*ljYTu0 | |
| k)Z)pH@kEg+RJKq^Em3WY^nBz@y^e8xOqrt=IDV|6z{T_V3- | |
| HL2554B)^K;N2nIJXb#neJe8*tXPF_w>2u^(ZkKgLi*X7*zx?8lk>NRCjGbrfOITPNT@a?c)f&>s_Ls8L)*NQ!F)en^bvBQP*T<V4prP%WcL78y~i&Ga#~hl | |
| qGKXhjSWpi)F)d5_$Vj}VThNyLK2F+}1BB0yKDQH`<cB=RTE1Pv-?rTRT)au5l2#7auyua)h{;LJ}WiNsoB#nT;GB-h~#5e9- | |
| tYSDGH<zp1(k(vr>0JS#~fEpx2)B<OZ5IKB|=kk+6)Zz>ur#611Y1D)xXQTtvXbSAq4q3;hkl{nfngrNcS~U@g8iPMpDm;yvp_X#|$PhR~3cSwD=+7~HOq4j | |
| oOXmkxK0gQ$`lBJW;BHOGBCANpN8Y>{DBr@EJ#a>T`Y4t-!$1-FkS-OpfKX5=ff}>rV|2=OJenpzdSnFDAh4!Mn?CX;gFfG(K{=%U!c8A*^L!L{<S)|mu?)* | |
| c7DNr0St50qV-Y~L^q5*YNG-}1i{gqUk)XzbsENB`2~Zz{S^U9xTBIp|sEhH00W~h#V>qNHP7#R%)V_ZKP=k0xP0~#Sva<PsfzBW3`6%fpQYocBTAx- | |
| 3r5|b+sFiPMOoP)%ej*IhM_8oIyUqlurBkS7w0xxGf|B_BAVuhBPEq5ud@O*?U$Et)0HRi_;UjY-XgH`Q$L9wgLVp$~W0uevy6Rdg6g0@km`V}(kd)64i<5W | |
| eFI00qaa;b8<t$MsXm(S6(fMP=w)_-4jbr?nfX)wrn(MM6)PM(2OXp!6#t{Jqk_Z<^gu%l&R- | |
| r~?h~%L>M#|JEl*cf72$atcDc18bl)xVhrt$I}1ssg?85ql(h>T!}#0Fh6S{OeKsQvI5Pzx<>K&owdBx*?%YJzwg@D3k2?fFIVw0Q0#znih7KY#5OSsZT0b@ | |
| ^k7wrYWejV&CMTG<M>d_0j1HAoSnmIhJF$$u1e#{y;sBuG#o*@HoYRcfKR*NNe@(3UM;qs9TJaRh^MlSEkfkK(%P<bEPS1r1)Xd^}Rnm}9O9SX%J#G1%{s&y | |
| _zOPs<{?F3pvn!zYWbP#fK)i5rQ8%hbxWd`u@(1A#FlD@BBX^+>0P5C@Pc6l#GKkt2ySV- | |
| #u(w*1VHS{Oqly@g0_d0IS)Key>4uPHw;!D(U%Y7*AP6AlF>A!<Z^2zw$Zh)04l`SKSP2|7}t_I8Zrqcr)kHtr*Lvz7`^BlT+Ba9Z9zji1Y(mHZghr$KC^#t | |
| *9TI*Np2YU~6^Nl;CM{>b1bLTJ_?(xE?VhFUt+bq15HXwOGZ7h~>z4cH}WDRR^*(_9xNxvsTc3m- | |
| jZBS41tYbV#Pg;7L~jvsjxYO_Esv`6DLeVip~86|2oi>z6qHdTpQe9kq$Kuwq(!m3Y$!0F?t47FUoSWsb$mY?uAZHXH2YSdsMoC+G+qycxAMrQJh+F})9%AZ | |
| oC#_o#61gM4EVue#gVkK&ktj8myM-8aG{<?0<$E=QzM8ii&<M<ex51O9)$mjS-FvzOh^D!xzUnD_I((;iQp~g&o<Yx0nls1~5$gEJ4vV7zhsAVvH%+RnEVEP | |
| D*M3H#YM}c-NtLbA56Ea$+7C3?w?b!r^XLwsSZXe?ajas>;kK+mbX*1NqgRTiNYDw@k- | |
| X5(`zsGt*u0_zaB;8ro=8WA!1Qw}0MEIMt673#YGOmFMP7AXT0aj1`P|0<K<s*^DADauxA- | |
| j&#s7adg=W%`HYth(LYGlht3PXe^5tN(9pI@fN8w(nop;kh3#t<_j?)q42ENEF7YO1DfZ}+S1rp3zk`n84D)QU7`>h`&9&ptK#yQbNk?XR1gv(=5- | |
| R#wY>v%Wu5ALwk=p53WcT5Yb*_S>^V+nn8A-?yvt<@)S$yE|{xZ>#@r@R!fNtj_nV8~l~)!)||ess87>+5O8`dh)6N;Qp}NwEAb4S64f=F>iPO`+xoa?b- | |
| Hjb9TNvH0%BS*{9|C=d-PSJNOoEmY*B*OKbd%&Drg8wYfa3&zjBlaCMFE>r- | |
| >KYi^pGPq;(kAN*kd;O%a^Z_f8^|NZT+cl!6#XSg}L+wLyfSL!qFRRw7q_2qVVwmj^ww>wqU^xt28QCnPlhMUj1`kCeKzFULq<?aUm-befQcFm<)qZZ#&gL} | |
| ?dtG4Owra5cYZKFTx`F3- | |
| ;R&PYT9)6s&)gIrFx?GJ~8MR#a_uVhG_z9Yw`pnC<x*lJa>qFE2%U9}>zW&Bt$B(Oa`Dxt@7xnCN+3xk5zi7@^?dprVobS}Dx2ydU?*+U@_zPG15AV*@ZFJR | |
| LsXwR%$G6fp>&vst-RkOEy*a!C+=5>(_YeLaUbOb?0<WW5%Ioc6f3|7Vjii2Qi5FqLRe#{(>=IwgU7ug@&*00n>+PL-6Y8q$8~vs~H_h!?^QGC{>s44@Hu#- | |
| -*O$0lmyKG?v-RpTF83Zkfx7~~$KTs8*Pr#@RWH;w%bhxrsP8WEvYu_Wn=h;FT3wp6?XCGtsx+*+d- | |
| +_gJifMC6usnXpTMh+D{R*NTet2XuGJ4T>Qm{DV6M`mr{~6g#2Z|LFZLpxpC4L%75tNNvsyQ={2i=5<12IH?RI!w)caoRtE8^p!Cbz(>&85DtKHens@>zCac | |
| _S6Nqxja>sMJla{BMCHtk`(SO2H(&E?rYwx659TU5)xy-|M%FECz0eJ}2o_ypqx- | |
| <+vyZl7jc>c8k49q)7X(bekR=oQ9~`VKFX`T`dZH^){zbL#EmFTXOkj=A~NTUU#s7WT%h?82|?cDHGE!{^^TSn7duPo7#}{h*w$^w+DK+ilyf^ks54@n&^<z | |
| =bpSm|wegYu=r@cW`Oc4dw1wJZP={i8lp4c<S1!lQ3>5`UTVv;QG2{{kT0p+~9qL|Lt;#TMWKeT$4NX8S&;- | |
| +tt;|et7js&zlQXZO^XM!)PA9^L2yAR{XnoW!%dCe5wB7e2aI#+ALc2!Z*0V-Zsng##{;gc0T<>?Q42_- | |
| l}c(mHK0Q{47^n{XWhQ=AG@YSIs59_p|M#zM|{ZrM;8C>(+a7x!anLa)UeC#lr_({rPkCchxStQ7^9!8dnFsIex+yzOz4ce|E9DaI3Emo$9_+Z{_a#- | |
| v4KOhqhqg#-#TLbuZhS41Y!)<9A>1&fMyM#G~4|U-Uot$Ep1vhn2eLmiVad*UdY;`*3A9dP{B3+}- | |
| f%>=SN9TmNVA%3d{UQE`tw!{@NYt7o4^T>p>FetCX<rvAYB=k@)+oc+~q#&_4M&Zt{N?Q`qx{Sr&n^-^8+wfeUUeB9eRf3x82xKp2guU-Zp0DN^^^EYnIe>y | |
| *JZukBD;+=Zc7S{vcy}H}g2Bf$3t-8RtnEj=~4{^RdtS{6%P>XffZ6)?~w;SAmF8BH-T<-Knr=RhIsyXhV(l`3S)^1gn_INM48_N9ns{QP5_~u- | |
| l0vfY7*!$!9aHAi3TMpbay4vf?;Wi{)2=}UHt3TK+vU#=JwfcAb@c5B%In@n@g+p&FddqZmNd3bOPgrUzZML^=JO17;sH*PziVv=- | |
| +U;<O@RP3bkz8Ii=1*;w>fzTr$6lW<@Z+m0<(E<Y&~m4CHhhEkc-Cmuned(67yhxmZ*jL)t({%i8+~`!>wv5Ev%9ePaPN2OL+;<Ht8%Wt#|<05R3~M1- | |
| oV#XmtMVzmF`mTB5d#$Xj^yLmKR^ty`<i}TfU#}KHsV5RNw7-8@$xV7xgDTsXyD`i<?su-Yr-!%^sp&^Hv=M^)lJ*`pi6Mrhb_<b^hS$Tz|g9w#2*{yi-iE# | |
| 9vd7{^kOYLMyz$x_tVF|6_Bb%h%?t`Bkmo#_YTJPn+gyyVuL2?@&`HR3X4WV@@UNx*ha&x!P|1N$$PAlgwvui_~1a>#t>ZNYp=IFTeii`lPVLt;7^;w}|icL | |
| G$v`oy7DntIvu5tM-V^LEWIXwCHE~9i9ufo9)fL`fGdjN9@k5_vE|P#@yeyg3b^^_1os@YA!EV=PN81=AfaM$2_Y3nyl8ir{ZO~*qVo3Jyz;j{M;=pzMNh- | |
| d`)%xt!@rC_7(A=X)dt9s&i7i-`={KuXmx9ez4WErO#ozGk=nGEBqT<?EJ=S4>aoC;A^PjX&)y%Jlx){@7=BGZw>r0wGnpb16`kV=~ZXxy(&m*{q$9}^- | |
| g`vrqx?BE~{EmTh{+^P=D_dOTl`jPDA?WpikLqWxsLEz_0$Gk5gNG3iKZ_Cs@6(y(5A>{^QfBEAk32nR>v!9M&7N!Mdfjw^;AAi#w<}- | |
| >$kp<!iforSs*%KcU?L+MjII>)~VY%HC60?agBD4*KS7%^~l0x58Fq^-puBT&usY?ry- | |
| N(bC=%YJI<~zTn|UJxp$a|9n`l^|AYvI#uIQSDljg_piE+#q7W4Bs{pLxK-ipf_qqdesFi~H?O+`!Od- | |
| >t~q{g_qhG2_o+K|GqtAS(#zF5`r@mqMd=P=_+xmAS#P(W-75dm?9^vfpBJkHeyO*HAI)vD- | |
| <fNOWdJ+VTU7@14cFe{(Fgy&E4J!bvr%<eooSYOkMpP9pKjC%OrOiPc)V1bwkc2jjtch!eMnG;jg_ymZi!57F>Czq`f!1}lfGi=2Hx- | |
| 8*5dEpbKOJX2BT`(jsD0FR@^M_H~r4rEaZhcYOL0#Agl96vsd@r- | |
| Ex1f?f|_Jw5u!KF5CL5ZcZ#2*Q;B6_x|$YXWQ#02b;`(yNx{#?qC~rsM@!_l<Gx&bSU=y*1@0MTH9!}`tsmE^m}V{Be$lrzHwWqK7!#=Y_W9c>Zdn$T&MlE- | |
| zeiMVplro&{z0?Tr{^hLfY7Eve!}HZdK!K4r2PS*y0bKsS~z2m6&Dr4caewT}=7xPONIJZM$u2nLbLX{rh6on)AkL?^f``R@Do&dDyapO^)rpY`>s3wmY){; | |
| 2ox`hpLPEyzEQYPwi**+Uh{BudUwF)tk7|JG?E5wh-udst=L2&ow2jZ8mLJ_tjQ%WA;N_Z(aX;)$3D(haGIk?a}M7G2Nl>U%&BP)?d^ztNZpsy)AXW;Wl!EC | |
| E=<uTZnzYSLO&{wnNnn+lS>+bw90n|M!n?y`MK<@X&jOj|HBQ?5DRU@a4sgdaNF<n<{kb?(SwccxCZsG~1?qYx<q(3U{^oVmkrdwqjM%OZQ- | |
| *^lh?KfArI#H61w~^j4cgV@54^>cTJYdrP>}Z>IUOGB=>wVBF4*i|1RWU+lz2eTsK@>- | |
| 5hc_`>*j>pK=VY5g|O@rbwcU5I~VvB9}&T7U4|Y___#>bAw4mOicR$<u5akDo4m6WNu*b- | |
| Pt}m!GoyMgL6qB6zc_o1*nwqPv^*PJd||jg{#D9z2j;abK^RFZ#pdOPaZqJ7~9uPx{b-d%W6#zu;Y9c5vH*;;QerTeGCTBl<i3ikULv9*+- | |
| zy1DL_c;8^9?Kf4oOWe+I_t#%;bW5cBA=BxpH;oU~a3Ju@?%uw>Zi%|C2N$(>o4WN-yVCW>o+EH*wA}Z*sBU@HE_CkylRp;gdDY$ZYK>2&ZtL(bwYcM*A9ij | |
| )V~(La{jbZNUQoXc>w~qb!fVqFE&XEZ3x!X)@8!SoZP4G%G1S})>Qx_3>~{JbVP+7A+up#>e>VLCT?sJ1dVAQl2R%kJGhOw#`tDP$VcV!SSKS}I@?dtxYkkY | |
| N*ak1vQD$YE&vmo!7E>Ml)W<yUx}Wpwt$J2ewNr=p`@y`)UO&9%>NtIC7Q;Lcx_KScUe%oHnT0>!{P4H{;{&8lSNiGg_2RGmxUtoBZe>e7UIw+FV`)~mr@Le | |
| N757s8TWdzx`Wag4NrXK^s$V<r9j#Ocx!ipoR8;J?Y(+K074r=4m#v+Q>T#zY8sovomVrTCRVUU)H-&0jvfpg5a;>q;Rv$tw9Bwa- | |
| I&|NgrU0K9b*#BxuFX36?&YuQ5QYo<<Wy5nXY`3@SY+&4?A3#-J^&t?vB<02vBf9XJP~*_KH#&pHv{6k- | |
| iSio1{(aqjSAlz?p1D${*GrOQ$_XZ%0Bye6np18N!?C0Pqb+LL#7_ZyK?D|dyRQW-0LDtxW*HKeh~Fj+Et2se=A%!>s!5Y-}>(4- | |
| &T4{#@$QZGgn)`q28z+vr(@<n8MpJDE?@>U4QBCSa!hL9+nrj-q}V3o9qA4b;@T3<1tSwj?Y^=ky- | |
| 1%q|c03&Gu@yyuG%2nyyG)|98GU;CRIS5j__%oA5=i6<gn%tB- | |
| vo_`-)>s$0Zg8dvhvi`YlmTpHZ>)iZnDpXKA1w&Ayx@X`A=OZ)oa<g9!C>@jig2Hj>TtgczJyXv;{o5T5a4*~IOY-hlBEbhiV&9CO|n1>Gcmfo1swLDl#HB@ | |
| 1C(%z)P%%apgGjkez952@gL~GS%-rrkr*tgcuuU|iZY{k7|z0%E$zJ1z5d#j#a^@qEo>+r1Z2C=xgnPu}$$Di)Z$9(W8+ex$e0|- | |
| s6e4+P9kOd3e@ojVO@GQLmdV$>1H#eUUv}jxXuJxMw=^y@+Tc$s$zo~mTjdj?w=K1<?)7lOeyX31YbJ*N9SL)aGa%^$b;0veuwTtFb71q@kH&xMFqnRvuYy{ | |
| 8TYQu2{2(va_r{HeccX-391Gk?KnEo39&F!6eVDypO9j_5E=vrI#BFnQDcUnA;G<ZbAdSHP#- | |
| PfI&ss0k1nzd^P%;4MZ5vzSSaJ|&+hXG^sq}A@8wzgw;xLo^#w7&HZ1{ScJ-w>=ZqwMbR<k~^3Lwe$9ju+<k+?bC$coGft_^odteuI-xysJ!~`A*+WSDTd|y | |
| |r$hKbT<YMrV)bF1=&npm(`y-7o?BZ{6bIwyK_%>u#*_;NkK&TXSJnc- | |
| 3A%cyXM_yAS58B$g&~GudC%Mb*6vym@x&Q#Uwr)jzkyht_T2c0lPd8(fg@216;mZm;k+)$?LOm2M>EZg1Un*kS5DevkLsKmCLElddhQ$!ab<gka82x<YhwDB | |
| laXB4U9W{bRnJ`nYQK;KI(T{T=m6{gLg3YW>t3$LI5vL!V7^qVFq<d6>t0i*H1Ie6<tp%)O6G`di<P>4W~e- | |
| Va``)gQ311~!togKpLRxxrfF_Ga8h4rXp`R@8R||Jm3vnQId}E<(H1_nt)%+`{Q!z~-rIGkXJtu0wM- | |
| (P3jRlsmnA=#c!~5+Cx_aQ3A>rTGKB!GyZY&v&Xqngv4?qv<yEKW- | |
| 7)`O>%+Mg5rFWy}e80J45MtkwwMob{V7T#@e|Y~<~=dUJRdd4M#VVfwi}3=BZlVZmGOuvM}3ZQ0uEcd3U5W~$U1wLf?QyI!WNYUV-G2Npf`N94tBFrSte- | |
| 9i4h>Qt~@UvzI;y%*EE`dzF)Ytd+!eSUEr{2~5#_#z%7wk728=ojzIM~s94Ffll?ss|0XanqyN&fT10umb2aL{|oSwbtG@`z1c{hJf%2%clN+J&N3Z*&13xc | |
| SP-N0Q%kQw!w_%Zq&kkY1(d_@!1}Gm$&M^XfAv;#yN*a3qIqqQ|;_- | |
| @4*K~y*<2sxOV<v|A)q&bj=aUfTqtK^6}W_doy?D)K~1m?A)E5hQtRv8+x2pqss_MS@5YF+Tim2n|dDpiIA<jsjkgdv_IV7$Vok5x2wH*23@1jF7<$?2P*!> | |
| 92ESqRG$aj$~C_(^<hyj_f4aBNxg*XRxrS;y<PQME&Ngb!+Ps#lOz4OKcLR?svhHC^21<<UD}%3+}!T{*7P1XQpYziw3shj?19uRs!v$k- | |
| 4LYu;A<@Yc|9PuM~T(`N&Iej<W_Iy*S(&kKR7WBhI;ss0ekp!97_0Miy05Ed%LC{Oqlacx5em&vhm}^g+HeEA&>`0M|DX|DckO@mKy{<{LI5`N_IzZc*lGHg | |
| 6jUc8i0p5P1)gLQ~kn)!DUt((`EP0o9~~5E8NJvPcYLj(p+BJt)ss;?;7=f&K=_GaTtx&tGBGqp~#%ZX8p67M3{~AA?WHqtQnc&s&&wSeJ%}7@AgkzD|MGPu | |
| 6`TB+vRqRyeU1>L>N?k+^!7%(F3jKrS{t^-M(0WaPJ>ab5R>Tky?8=&=9Ho8(HgHsyByIckZh78??G- | |
| &9%5ZY##Q~gWFScJ{s4EAJj=|XP?rG<*gpCtjv*W@oj%rV-a`#7gWX6qw~uLBt3T|*X_P*WcK~h%-|<3G(>GCA?8!52jad5pRYDw)P~a<JZ@)~- | |
| <*W|rv$Yfs~r><+PpuFi45M*6<!zDVVT~x5BT<HH|EIPt`UNKcr>v$lX`>qw%A&NitT(`%k<lBz*0MGAu;Re&+LC5kSZQ- | |
| `i<Xh>`1MD9(DJgTDvX6n3h_lju`5pG- | |
| N0}VK<GlzU*paH8;s+H;VTN|E^C4bX$`F1+l>0+X~h0%!t+R*VvylICJhMvhEGI)%?#@qfQ5^SyX2SyU*c<*V{Ebd(T{@%iZ>701_H-%PgKh-#cQ1- | |
| rljDGlw4B={shGMYkUe_WvD;b+51rU0pS9>2&Y9v}`}!L=PZ3?9}j1$DhOk(K}}Jhikr<F{e^XA~f5zK^XBxkcMTcCVrWIvFEQI$Fz5t>KbdDcj^n{_xU&7{ | |
| bYzSYN?m4E2D<GW1k3BDs(e3q%!>G*K_rJ;Wfjl<eh$NYX8*lLDQ- | |
| X4e8ztq&=<1W5%kr|JAi+>O(d=bqZR#hEhEej@7vCaT3hv$W(5R^=!5dMzLc*wL{=DyjyNQ_fBGGOFUcP2?)tyTSIKdmH58Hgc`T|*y`g+6nGR@e+qBo)g_L | |
| )H6y_G17=g~?^J{T(3=$L{nRqiuo1#3?#>Vz>|OHIEMD*Bj(F2CUJ<C&&EoYINr-sqI~>yOCC^Pp{YJiO^*icSD1AaSHR+<Ub52cPHOtiv;qkP+9S- | |
| r%x4WHVjrh@xo8NU9#qxH$- | |
| d?GjTQz7%8}XZz{&zf3s2lOT(F0riOyDE><lI0{*)4MQ30d%WyRA7<et0nd>X9hTW9#t5^X8)Q+xv3A(`Q8gp!ZhskNR+kaP=c7RP!Rclb3o(^@!#YVY7~k) | |
| Z)+DP3i69fv6@rnJT2-hu)A|eKGY6#&1P_Y=di~=a;zk_{H_Z6HCOmT!A5>VX$fU7xdG;tyHQx#_!ER2*Gka5K|vw- | |
| J$HgGbwf#YfWKOcR;h!*8}Hdj_r*tO1~VMZdv~&wP@IO^pEe~`Q!VaZq<V0ErBhsr}MaSXKq<DXw@x)rs(NXrLnCG)nZ@ia@5fR+z8832G4Hv{6QbR_4~CnX | |
| arUEJxY)pky=uBHz4hX&h5QA#ogd_-!-_mx3A3k>y_`)d(DoXx#)(pd-nDF(U;BLOnOd_yJLR}VwUuPB9XP)V$F&TPwx7%-D<82- | |
| p%@xxhGLC7cv_A#p19zw5F`v8|ceJYE_F*#@;@0c<fL|>BbG9+O@h5&F3`dS_7GOWU0Y!rncFox(5!oUdw?`s9L8BJLl@?Rk#uD{UmSW&s)E1*0(s*- | |
| (WjpYp0=2>4&FJn_6CAJ=a8|^{v6%aKZecU0;dS-X6I89{FJ7S~{Zq(h>_y4|~4DiqNA@;8EUefSM?vk6Bj^hQ>n=- | |
| u<?)*g|2Unhv#eEPMY*^k``a0+@<|D7e0}b*J5^M#_ArXV#B- | |
| {&vqlY*u<Z_ooHRXw;0^M$ab=SJ1r~U1(JIan>!JKi&U%z~=Y&o8~O3zp*>q?jPv@TI6$Ypd&|kzD{3}abuX%yU*CM?)HuvXjK?`9&o)pw62B0- | |
| rbQj&wU%Ddb?ZmOyfOjw;wx?JlrCf;5t@6SH&ytYZ~$}Zyz>{f4Ag=8@<`shRmID?F0``#%4~c>(8g<MXw~|3i|WBzK<<YV)ghCU^WvAo!`0nqwV+H!s*x0J | |
| +*^@E>>pXj10M}jTvcpQmG!ixbkcUQu+qR15!tez}x1XXBfJ{zBy;>k%MN?Tnyi>J}laUqb|f&^5qY#L$pqn9X;_eEH!<j*}tRz*$=YKntj*d+Uk=GsP}yd5 | |
| 6)##4Pk5jGdQw(n-_-x-faf>4|sL9NqdE2v$ftpUJYqTy5P9RqdPeoRyyv&9gz%a1RjQb2*Y()wWAo>t;HS5ag1j~BX(>4Xrv$JZS+WFo`<PNDcw5Z3hZ(E) | |
| e)k;d(&^zEjQuw#_Zm1;MQSWhKP-4do38+>NaO}vpm0ltP$)ka@VSv*_Y- | |
| (ZS)kP(MJq7jOzF0Zbs3xJvhUDGQHK<lG~r}bcIJVO1_w5kj8q>SGP^SQ#VhN`{hoRqW+Y*7n@(toAui0gY-eg&7W2~T}<(J>>Bz8;y>&- | |
| N_{N#R9WKq=F3Xmjrz3EwV2oHX50G-!^H|WF04^_2C-MPBQf^HJ%iKs%GDZoeeel?Ob9SbX$~wM?lB|*t52miM0LtswS79p(iFy}I)$jcK- | |
| W>D_3Q`%xV7nzV@tyi!(Q77`Si`?31@b<v`iVxak2y*qb1xs#;Q6-bxYy7H|Aw-5AC(1+kCorh1xXE{ZIX@ia^&0^`N`aM`IUFztIj&_j|~)H7B- | |
| Z+p){ACh23TUp75#bH~%2CIBqg@9ZVAt>LOQxP7;f?f!l}oL8Q2Z|^NR8dvu1;DBd_Y0F(V`|E&7gZ%c;3B3SFFn3G&=YxS- | |
| J27pqfx$Ossmb~;=vK7@1k_Ww9gyWc`ebl5&FIqQ3{j>FQ+#d)@7cT`i?k0uGHQrB7N{F4sgs|+Nbhirv2@6!=HvGtA4BHR0A+iRzjKWnx~CSu`Fe2TJD6&? | |
| )#m_pD%$%3>`s7P<7VjgJu$SU_FQaD)#@{1%(<lP1K<9N_5T>I6~cuVeMYD|mf+;vLj>;q3=Vcrz2*?8#0P5EpwLd;j- | |
| OC_rR)67@*kFacy_g2uFc)`#<vfD@9|`;W^yPhQwN#m*3jvVjAW%r+1u^<l~IG3<NcW);I2COr2oR}RZrU8AKT&H+L|82lg9d@IYgSzuUC!L;_9P}9oTj4jX | |
| rnkV;c?uhw|=!cr`h`hwi}L*g1+- | |
| F{3<W+v<Tlu8!{&bo*l1drpw1xfb>jxIeQq0QD+(&bNaTdrVtx_8n&$%dF~gP%YCF!gr6Ey5L`WGGO<I9m~T#tcQ(ecp%Z@aHCEzmf`MBiiQ@CHwZp79f{)K | |
| w!14WIK_QV)8zGZx~HMg47)y$W@SVdc!?dHXvfrUmiDF- | |
| S36CGftxe|t0U12Su@why1<G;)a!rRUKm;0U`Ff#@Q$p2^qq(LEBq_}I9yyAz}HR!?9u8mr~ko>e#q2tZ`R+oI4ReFXX{2%9dzSol)4AKLgv9cizfJS;Fo5t | |
| 4zBtLz1^wOh&*FY`8VZYbz=s!Zx)>l!sv$alzG5T&7odKk9SaKB7bgm91`pvu~Mr(WVLzfByqOWe56}-ldjGOeV{#mcR=Jo-up=t$D97%_J{5H=iW=A5XGTy | |
| 8m(R8CUk3C^4_V(51o25k)N+NTC34FSJ1b4+wG7@ntj)sa_PS3Qt!`Ks9Z99!#BQJ7zj9?lxqygas~m=H-c9SeQ5?f>~^WQy?cZmIxB;&lB<nx=~l- | |
| +2<NNa`QgT(&Q6r0$-NfA(kr6Ri%7A- | |
| dGKw2@Thl=rIl*;lmstf$y;?L?4$Xn*F2de_AIt{ZUWcmZ4CMNcT2}2aT7Gp8pG|g+X`DNWPEpay}G#2PqyhQunpIjw3AYrim!oU+<M)TxmUMCIVS;h$jbJS | |
| #!){|me$qx^Z02v4ssyL;A5(N+yKz$IQUX0UBjV%^ZgU}B|h<12CXS5YgML<ywQAAU9(+txZ4_{li@Bn$XIXsPl1}<_|0AxeLPURw- | |
| LLkZXJP;UL)SMvyZf(__~HdtZsE(g$>Qv3}w|OyYFZMjfZg%Y0)$>eID!=P1Rzr-BBHhi|XmySpL4jYYa4PG;MfLwtf?OV1}2@;&^sv^Bux_d- | |
| y?Fn7XMi9P#%C0eEM|fIQ(Id)Y$;8gSOf0)1}wqj23@n|r~^S|0dmJT|_t- | |
| N>?6L3(?{Lr6xCfYedrVz<0Ax8B1(uYaKJn`>BX{LR||%Y4Yo^Kx9R0W$lIK7V`E^~U;;_<D$3T(zUDZ+hgwUhmT50(tiseg3}O>UH;6fW8`iFt*2YHNAvWB | |
| $!sx-nn=d&<6<f_WWtQ<3sDmc%VqZozh+%U3rkGr%%DywB2YHXE&D9i}wB31G2B>oNYYrbfcaLGYWE(IHU;Kxgbu%G?8QJCEEQ3bg!t9{$3k}CmVBg7+#~NK | |
| )R1=H@JTwNpu5!UAwJf#&EVT4z{FUEN^<Q0C-znBQ4GW$K9;g7G6uD(5kfEL0xEb(9?UR_H61F?DbO!ig|- | |
| rkM{=H_tn_%%KGDL3x<1JnV+E9dl`&XQ}%b~zGKqh^r{hc2UFopXRH2_>h0V<ueAiKpHl0y^_%#|?LaJV9!z~$Ti&Q!#DjRoc0^M>v~IvGf<L$ad}%AY<vm& | |
| W6&Wv^d*k6?6x(<UZ#8SQzfIeJ^28-C#B)c6TMak&c5z)2hkR5#DRw(=Yse91f4@EK_r1*>O$|OR*G8M++Zy#5H- | |
| m1*E}kc^X&t$KCvE?TZkEEI&HsS4eAns!x3`AScjG61OZ{09#n*@`?ny%uxPO+1z9e_}AoWeX++ba!aE{*e!D8(_uywh=*|xXlRD`^;UYFe4r<bZX>$Khf9j | |
| 83bJ;sux3@(5}F$?PTFj+%h!Kce{RD9p^{i0v(`<$9xqdRmf^U?L)Z%nH2-WhmG+k-q=*7d!po;`IQauZF1;9nSToNa3?4R-oY?70K!lY7rGz}- | |
| dK`bIrQyY92u2S+~>7dXGWu%01pk9?!HjQ*Wt{7P=NxRW1lY%76B=svpbZt`6N&E`t)Q@B|EWc}|*!qgi=PeNcJUGpHgesS4Jr0^1%KXtkKwYS0i=GWV`J(z | |
| avWM9=$O|9GUz8K(x>5ew7Hi>eZQ*T`C%%ysphW}_<t=WUI{~dds&cMa&y)CLnE#Z8>-J0Q_>E8x!1Gp@2I@gs>J- | |
| z+Zdce9X4$W%cBiW$T4?SiaMebG)8#hC=U;hVf%wP^F- | |
| W0)n)K9B_s%EM&WIhMz7zwN0KGYxEUiPpER9By?Gsd|+CfIkUXM3G~8eePK=}_#68vArz%rq2?GlT=uHSlb#- | |
| rCmUmzvDn5%wPt^>N9*@AqhTUgCPdW*y;QPX{qTTSqi>-cEgPmi0IDgiF6QFMD1)eMXi$ZEkNK6HPDFP4|RC<z~A<_Y41bx&xB-YPz$->W>- | |
| >Fr$Ql?o59)N9XV-m6dtsjnoJ!s&;vP=)95akoW9jXS__%u4X85MTOM)Qa^M{td=;$!qfVyKeJro)OWf5{K}CLUO7d++lkHWw=W|zJ- | |
| #7jo~xCzlptfv)jJif^lfix5j`SZhbCb2hc~k!jrETZ<}O+4Jy18?c#C&_jmZ1k8yf{&7k_^CglyZ1zHoAXujgKf5or>))?(pV*DGaK`+>#- | |
| u_mvau!_)b?7!Y_Z`EPVqS46fw6Lch?HB_9!!zn^jk@yf- | |
| ^V+1MAeuDVkyI0#P_VdL(LCg_Ze#gw=5$az@xAowpc<|_o)2g(5}v3sbYj6hrW`Ur{5<KY0&+_R=QM|R^7)I+t*jIuebTxfNco(8kvAcS<C@{w>%#Z^z9nxI | |
| m^Z?l(bUS8f!S*5m|F;N$&eyF0R4r&IZGa7cbW<^>pij>arcqe419hb4S{IER%e2y0s67m!+F|&Bkuq$cuAz*szY#mvr0p- | |
| *ykNedj=AGkjvL9;q8<b+E2&8~R>Y+MKg1BFIN{=5G%AZaZcGF48xTb#2<7)Xd`^IgWVtnm;Au5Ecgn{YIeE8a+)u#<8nj6@TWkjt~!)r(2Z%u6;FBqV- | |
| 7nh$T6U(5M!?6XeC#9DlK^)oKtcnE7GALSGp&xK{P*qu^yv5z;gAQI+yn)iCV`p!UJ)`g_y>qzgS177yl9dKm|7b9XF6gRIL}t}^K- | |
| P;b0;^keF3H@wj|qIxagQeQWHF{|;Pa2Vx7FBE6F)YJa0t~SUlSUPdq5B~i7)4<7V- | |
| 9O;DgM@WC)Z)gD_2kGtcRfoO^DGba#Y;09_D*CT$M4<f{*dB%ks2wpBs52^e;|ADZVT$6(8IVsGvT8bWbUk!N%t73o@u2~<VWw^EX{ZQTj<7mI~k{t^~FQs) | |
| qn=k_a$^^4s+PBntVfg_VZwE{)wGj^XtBU)0ll?uneu|!fxI0r_myHBf%c@i2LnrYkB<*di?6oX^WdX4qMWJ|Ilxqn0}grJ(_{xVQ6n|K>y@hvcVJknOl#gy | |
| 7lh=>8w*bX?$O6VVza_#-zR{HM98U!jEnqK2- | |
| W}u3M@8u}!yBt*3l^x_3HZOAl&4uh#2!h!L97;ohmLZ9Aitd1ueV#%{#4Hl4wixlZ;}si#}LBa_>a{E^qX#+aG&y9YI4tF5VgJ7VS&_)ib#5(7<4?- | |
| =KHb0<16{rfW}i?03bFRI?}+QTO- | |
| =0>M(+iP!MIN0h|1_W6@81)Tq#KWy2{NR?PIq!%JI@3?zdG%NeT5N*U6SH(Z<>;MlRP?xfbM~|4*?Cft`c(hwQRn5}sVE*08H_Kh- | |
| @ku0vfi@}Pr3P54?(*zEZ+adu;T$e6i=b1;G1>&S=- | |
| KB{Hq)7X(EM(Z~ES^yN%`l4eaR#7`~zLS!G6pW1yw?r2WAkhyQ4*;u9_rmu}FEW@PH5a(21YSn<xBv)$0j?3w$fkMp#60}l;Eye!yFvh0iAs1jogMQ@SAV@D | |
| 1{5u|!3@d42v(YEro&o|^<PquYjFO(qaZNm1NLz@lr?v}=}=gN#`dhpIWe}Kjh>MG==7G3PjZT0r+M{cRtMwV~Sh;F3ec3f>b*l1(@E9`{^V^Zh5uIrj36gz | |
| 3WBSQ5BL~2t;CT4ed^smruXRFP%YWw$3Y-UZjhHhQ_@3+^sAev)w@AT-82KtxJ&58Q0KT-eOyLoimf#- | |
| fZxYpi{NF`nBmozPd(KMI^&>T2@J^e)aogHAShNIQ&3iJ)$YZ0j}KRqWz+&uk0w7tZwPxVQrP3>;fouQzZR@jN9b@{W0A`d7#)sv={qZN_*CHHDQF9Nq6IyZ | |
| UK)88#W+ndCnq~EV`=IjVbPNQrtmi9@- | |
| gJ7dNTi<nmu&yZWYQ65k{raT2MO5hK_Ml0iorCcQzmRtpXhQy2>#Mo;O*DS~+9F5$KkBm6qp0^qD=U3axitP&da;}!+gVHD{;xM4!`pXa14n>)J2*){uDF&> | |
| VGpj|E~Zz<o%x<BTx*@-^U7MJo5SEMJ8RiBc5y7>^<lyHCt76IdqY?YVCZff?@VJC?LMX>9d$B- | |
| 9e(tSrD5rzHc}mWjgVWnuV4COW+&_MQVm@MTR%vC+YORkwY%sq#{-Lha}IopzP4+wwDrATvfj&SZtu{FY+!10Zh0P-C+AnL_Y(dTZn?S}D}LFV- | |
| B&+JEA1#eG$+&X5B7|=HqBeBC+SZqcr^sxTnyk)FClo_x!2>Fuhr~3y8(9?iKh5<hZa2>8@fpM_TC3dV|Dmne!>$(hb<To)-YJx<>$dog@_- | |
| XOq$)^c7aYi<I@GK(Va;c7@7uud~j=8@>Y-WY-|g^^lg~zXvHC?W^ikl$DhE>Y_ng`SDm0#)6_K5WMP+~?~~PVTr78=)lV1`C{- | |
| s1BP~4bAO@!QY~3O}q^R2y$Iw(;gN>i=dj<KU!Ls^9()Ms^{V1J6qvs^AjtfTM=TzxN@p6B=b(1Eu65n@2jNyHyZzx?6- | |
| dQN6XPK`3O^pY9j{tSkrzC5>liF_HdBhSCFDwRHj(eto6t`BtGn0d1Jsb$>-4?yS-f4G6ZS!@D&BVEZ9xMdnAE-a|S~q+(?>V;lRc{XcS&^|dF- | |
| C^_ZRc3`_J=2VDmFi*cN}&w`3>Ixro1`1)39W`ABq9`nx|zu0KvTlAdWhE&$x5d- | |
| CkY}l6*ST$JXL2*6PU30JaDc^rS6Avc5m@n{WkV+iZ6$uj1FAd)Eys%+XDsg3P=E*X- | |
| @kgWM9JeT0fPALzIMyZxXK90;@C_lX?3uj#WRI~#U9o!;6T4{y#6<<jS|fi`?&IZ-A$$G%2Stk- | |
| LGf4y}M=bZqkuU7htAu4v}BW4#e4rFF4@jB{<zO?3~XHEdGzy7)#xf-dbNi1lMwTZSeFq)v&;C;xnebr5Z&6MlV- | |
| ?2aRsF&?l71@Im;x0RX3DqZUKYh~rGJAORM`iW*v1(i#)>n8I`|+&ZV2sf#- | |
| #q9|^Hq0>zz$EfNG%eT2NEEI0N#N;P+ja+mwOG5Y?fL8rr*4sXPM>|DQG?~;Ooxa(EQhdILU%)oj<C^RK0nM-fBD1Zil^UvY2gEoAtVP#5R&7t;#hl0(`RvG | |
| sn7Iw&zYt-)F{|hksz=*M*GiteJF&{PoP7R@w@<x0fK+gege79- | |
| &yn@px8W5Bpf%z#EE!#RfFBw6mD7_W3Rs>+5fNs8`?r!SwK$L3Pl!Bb_5kf1@;OW@^c;c6qRdgYWI!RD0@NHg{%bg_p$M!+N4`=x|+g?{E=^4P5BQy3- | |
| eWM0VwD%i6v-x;d_QNnGk`Xg-0FR}CFWjMbQha~n*;8`$IYef- | |
| |J$4l=;dhFflBJ48y1N(1x*5usL^d1Z1EWyKVm3rjnb{%b(ptnFQQ*BSHxiNfCG|ka;c)Z7#ZfK@$`!toyItJ^+omJzR)2iJV0&Ks*KV<)?F>o+Hh$d@2v68 | |
| FR- | |
| 3aOiH%y<5VOYygmJee{*%^rF9m4vudX0r1o_is_*XeeY4DEEfwOHiquX}#%YaP|J>v1aIp~LR3i(2Z`+y)J_7)!@99t_m=<j>tx<`mg?3v@X9BcE8@Urgjg) | |
| A7u;- | |
| opz>h7mVfjik5Ycf(AO2Zrdj)iX5a{t|2TwjnZT&&zfgJZ4Psm!4j4*$WoaM#eZ+BfFqIk9IV5eCXL_#&CCydS@Fuhu6(~&(^o^65u$yy}9WxW{l}H+&6YCh | |
| ?Z)7>Y78whimJ2W2mdZZsek4G-;wco|LWQtNKAz^Y8G6bI74ni?=;CiKT829do;- | |
| MNDsb(ZyhC&VZq{xU3Uo7ugy!aQhtG2tylqz0$tNu!mb+p!RpGjioDDvvwz3Zrx0BU_bWBK0{g- | |
| ij=LvE$rtA`u!Vfs&zlLow&{jxmc;PgGK6Mix;fzcn6yE)0;=U>j7WA5vgkZ+b(L+=luAEG?vXn8o$GH?K9rBZZ0IUZ=9jkFrA`Fuiy7wl%Th48{8}pxtpuK | |
| cZ}}#6{C{V$9he5y7b2I?re!#aua`O{iyZN;q2knK){4#8TEXfyHSAJi1uhF<h2Oh+k7B;v(#`Th~C=$vTy8cGz{I%?O|&3f*8*+$kn()6U=v=AJ0HbU@Typ | |
| {nqqI57yN8Gsp*1?ZSRw@$UCK{m|Q(GL3ql_saZF-8SjQZFv4M8@{o_@9d2nF-<?OX4-yh6DdFR3=Vp?9bO4HK=F3|- | |
| m&vJ!_`OIrgmJkVlK_P(_^0&MOppvn>!GqH7^ok_T8cL?W2jUx14<@^#@N!1H9UBj(SJC)zB2Vd*vbCiVN{$|CrZR3SA^TvaKE9A6dG2Ou!vERPTFuaK^OvQ | |
| h&&2C`M*N*{AM#&dCPj;d&UIIeeme5s5<mR1obZviBZtxSp>Eyc%~N*!5#pwwgA1ZQQvw)_w#BVH@oN*&6a`@3^_bXZefC)@Z!R=>waLD~%7Dq36NT==B}Y= | |
| h*yW6y8<~V5*zJBAjcRRoQn2S3_r7H)OYSmj|}FT5hW?rS!9^yF^=c>^^B&n;y{Kb&fRMru29Vw8Vw~`mg``KZ+*^pa | |
| """ | |
| def _load_embedded_data(): | |
| compact = re.sub(r"\s+", "", _EMBEDDED_DATA_B85) | |
| raw = zlib.decompress(base64.b85decode(compact.encode("ascii"))) | |
| return json.loads(raw.decode("utf-8")) | |
| _DATA = _load_embedded_data() | |
| _CANDIDATES = tuple(_DATA["candidates"]) | |
| _MATRIX = _DATA["matrix"] | |
| _OPTIONS = _DATA["options"] | |
| def _normalize_text(value): | |
| """Normalize GPT formatting differences without changing semantic words.""" | |
| if value is None: | |
| return "" | |
| s = unicodedata.normalize("NFKC", str(value)) | |
| s = ( | |
| s.replace("\u2018", "'") | |
| .replace("\u2019", "'") | |
| .replace("\u201c", '"') | |
| .replace("\u201d", '"') | |
| .replace("\u2013", "-") | |
| .replace("\u2014", "-") | |
| .replace("\u00a0", " ") | |
| ) | |
| s = s.casefold() | |
| s = re.sub(r"\s+", " ", s).strip() | |
| # Make harmless output punctuation differences irrelevant. | |
| s = s.strip(" \t\r\n\"'`*_-") | |
| s = re.sub(r"[.]+$", "", s).strip() | |
| return s | |
| def _extract_numbered_blocks(text): | |
| """ | |
| Parse outputs such as: | |
| 11. chosen answer | |
| 12) chosen answer | |
| Q13: chosen answer | |
| Question 14 - chosen answer | |
| Multi-line answers are supported. | |
| """ | |
| pattern = re.compile( | |
| r""" | |
| ^[ \t]* | |
| (?:(?:Q|Question)[ \t]*)? | |
| (?P<num>\d{1,3}) | |
| [ \t]* | |
| [\.\)\:\-] | |
| [ \t]* | |
| (?P<body>.*?) | |
| (?= | |
| ^[ \t]* | |
| (?:(?:Q|Question)[ \t]*)? | |
| \d{1,3} | |
| [ \t]* | |
| [\.\)\:\-] | |
| | | |
| \Z | |
| ) | |
| """, | |
| re.MULTILINE | re.DOTALL | re.VERBOSE, | |
| ) | |
| found = {} | |
| for match in pattern.finditer(text or ""): | |
| q = int(match.group("num")) | |
| if 1 <= q <= 110: | |
| found[q] = match.group("body").strip() | |
| return found | |
| def _explicit_letter(body): | |
| """Accept compact outputs such as '(A)', 'A)', 'A:', '[A]'.""" | |
| if not body: | |
| return None | |
| patterns = ( | |
| r"^\s*\(([ABCDabcd])\)(?:\s+|$)", | |
| r"^\s*\[([ABCDabcd])\](?:\s+|$)", | |
| r"^\s*([ABCDabcd])\s*[\)\.\:\-](?:\s+|$)", | |
| r"^\s*([ABCDabcd])\s*$", | |
| ) | |
| for pattern in patterns: | |
| m = re.search(pattern, body) | |
| if m: | |
| return m.group(1).upper() | |
| return None | |
| def _answer_letter(question_number, body): | |
| """ | |
| Resolve a question body to A/B/C/D. | |
| Resolution order: | |
| 1. Explicit answer letter. | |
| 2. Exact normalized option-text equality. | |
| 3. Exact option text occurring inside the body. | |
| 4. Conservative fuzzy matching for minor GPT punctuation/wording drift. | |
| """ | |
| qkey = str(question_number) | |
| option_map = _OPTIONS.get(qkey) | |
| if not option_map: | |
| return None, "unknown-question" | |
| letter = _explicit_letter(body) | |
| if letter in option_map: | |
| return letter, "explicit-letter" | |
| norm_body = _normalize_text(body) | |
| if not norm_body: | |
| return None, "empty" | |
| norm_options = { | |
| letter: _normalize_text(text) | |
| for letter, text in option_map.items() | |
| } | |
| # Exact normalized equality. | |
| for letter, norm_option in norm_options.items(): | |
| if norm_body == norm_option: | |
| return letter, "exact-text" | |
| # GPT occasionally returns "(B) full option text" even though the explicit | |
| # prefix formatting varies. Also allow the option to appear within a longer | |
| # sentence. | |
| for letter, norm_option in norm_options.items(): | |
| if norm_option and norm_option in norm_body: | |
| return letter, "contained-text" | |
| # Conservative fuzzy fallback. This is intentionally not permissive: | |
| # a wrong option is worse than reporting one question as unparsed. | |
| similarities = [] | |
| for letter, norm_option in norm_options.items(): | |
| ratio = difflib.SequenceMatcher( | |
| None, | |
| norm_body, | |
| norm_option, | |
| autojunk=False, | |
| ).ratio() | |
| similarities.append((ratio, letter)) | |
| similarities.sort(reverse=True) | |
| best_ratio, best_letter = similarities[0] | |
| second_ratio = similarities[1][0] | |
| if best_ratio >= 0.78 and (best_ratio - second_ratio) >= 0.035: | |
| return best_letter, "fuzzy-text" | |
| return None, "unresolved" | |
| def _parse_answers(text): | |
| blocks = _extract_numbered_blocks(text) | |
| answers = {} | |
| methods = {} | |
| unresolved = {} | |
| # Primary parse from numbered response blocks. | |
| for q in range(11, 111): | |
| body = blocks.get(q) | |
| if body is None: | |
| unresolved[q] = "missing" | |
| continue | |
| letter, method = _answer_letter(q, body) | |
| if letter is None: | |
| unresolved[q] = method | |
| else: | |
| answers[q] = letter | |
| methods[q] = method | |
| # Extra fallback for compact lines that did not fit the main block parser: | |
| # "11 A", "Q11 = B", "11 -> C". | |
| if len(answers) < 100: | |
| compact_pattern = re.compile( | |
| r"(?mi)^\s*(?:Q(?:uestion)?\s*)?(\d{1,3})\s*(?:=|->|:)?\s*[\(\[]?([ABCD])[\)\]]?\s*$" | |
| ) | |
| for match in compact_pattern.finditer(text or ""): | |
| q = int(match.group(1)) | |
| if 11 <= q <= 110 and q not in answers: | |
| answers[q] = match.group(2).upper() | |
| methods[q] = "compact-letter" | |
| unresolved.pop(q, None) | |
| return answers, methods, unresolved | |
| def _score_answers(answers): | |
| scores = {candidate: 0 for candidate in _CANDIDATES} | |
| for q, letter in answers.items(): | |
| row = _MATRIX.get("Q{}".format(q), {}).get(letter) | |
| if not row: | |
| continue | |
| for candidate in _CANDIDATES: | |
| scores[candidate] += int(row[candidate]) | |
| # Stable deterministic tie break: score descending, then candidate name. | |
| ranked = sorted( | |
| scores.items(), | |
| key=lambda item: (-item[1], item[0].casefold()), | |
| ) | |
| return ranked | |
| def _fit_percent(score, parsed_count): | |
| """ | |
| Normalized fit index for the amount of evidence actually parsed. | |
| Not a probability. | |
| With 100 questions: | |
| score -400 -> 0% | |
| score 0 -> 50% | |
| score +400 -> 100% | |
| """ | |
| if parsed_count <= 0: | |
| return 0.0 | |
| max_abs = 4 * parsed_count | |
| return ((score + max_abs) / (2.0 * max_abs)) * 100.0 | |
| class DereVisionScorer: | |
| def INPUT_TYPES(cls): | |
| return { | |
| "required": { | |
| "vision_result": ( | |
| "STRING", | |
| { | |
| "multiline": True, | |
| "default": "", | |
| }, | |
| ), | |
| } | |
| } | |
| RETURN_TYPES = ("STRING", "STRING", "STRING") | |
| RETURN_NAMES = ("dere", "top_3_deres", "scoring_summary") | |
| FUNCTION = "score_dere" | |
| CATEGORY = "Salia/Dere" | |
| def score_dere(self, vision_result): | |
| answers, methods, unresolved = _parse_answers(vision_result) | |
| parsed_count = len(answers) | |
| if parsed_count == 0: | |
| return ( | |
| "", | |
| "", | |
| ( | |
| "ERROR: Parsed 0/100 scored answers (Q11-Q110).\n" | |
| "Expected the numbered output produced by the Dere Vision " | |
| "questionnaire prompt." | |
| ), | |
| ) | |
| ranked = _score_answers(answers) | |
| top3 = ranked[:3] | |
| dere = top3[0][0] | |
| top_3_deres = ", ".join(name for name, _ in top3) | |
| top1_score = top3[0][1] | |
| top2_score = top3[1][1] if len(top3) > 1 else top1_score | |
| margin = top1_score - top2_score | |
| letter_counts = Counter(answers.values()) | |
| method_counts = Counter(methods.values()) | |
| lines = [ | |
| "Dere scoring summary", | |
| "====================", | |
| "Parsed scored answers: {}/100".format(parsed_count), | |
| "Answer letters: A={} | B={} | C={} | D={}".format( | |
| letter_counts.get("A", 0), | |
| letter_counts.get("B", 0), | |
| letter_counts.get("C", 0), | |
| letter_counts.get("D", 0), | |
| ), | |
| "Parse methods: {}".format( | |
| ", ".join( | |
| "{}={}".format(name, count) | |
| for name, count in sorted(method_counts.items()) | |
| ) | |
| or "none" | |
| ), | |
| "", | |
| "TOP 3", | |
| ] | |
| for index, (name, score) in enumerate(top3, start=1): | |
| lines.append( | |
| "{}. {} | score {:+d} | normalized fit {:.2f}%".format( | |
| index, | |
| name, | |
| score, | |
| _fit_percent(score, parsed_count), | |
| ) | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| "Top1-Top2 margin: {:+d}".format(margin), | |
| "", | |
| "TOP 10", | |
| ] | |
| ) | |
| for index, (name, score) in enumerate(ranked[:10], start=1): | |
| lines.append( | |
| "{:>2}. {} | {:+d} | {:.2f}%".format( | |
| index, | |
| name, | |
| score, | |
| _fit_percent(score, parsed_count), | |
| ) | |
| ) | |
| if unresolved: | |
| missing = sorted(unresolved) | |
| preview = ", ".join("Q{}".format(q) for q in missing[:30]) | |
| if len(missing) > 30: | |
| preview += ", ... (+{} more)".format(len(missing) - 30) | |
| lines.extend( | |
| [ | |
| "", | |
| "WARNING: {} scored question(s) were not parsed.".format( | |
| len(unresolved) | |
| ), | |
| "Unparsed: {}".format(preview), | |
| ] | |
| ) | |
| lines.extend( | |
| [ | |
| "", | |
| "Normalized fit is a ranking diagnostic, not a probability.", | |
| "Scoring evidence used: {} questions x +/-4 points each.".format( | |
| parsed_count | |
| ), | |
| ] | |
| ) | |
| scoring_summary = "\n".join(lines) | |
| return (dere, top_3_deres, scoring_summary) | |
| try: | |
| import folder_paths | |
| except ImportError: # Allows tests outside ComfyUI. | |
| folder_paths = None | |
| import hashlib | |
| import os | |
| import secrets | |
| import shutil | |
| import tempfile | |
| import threading | |
| import urllib.error | |
| import urllib.parse | |
| import urllib.request | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Any, Dict, List, Mapping, MutableMapping, Sequence, Tuple | |
| NODE_VERSION = "1.0.0" | |
| HF_REPO_ID = "saliacoel/chars" | |
| HF_REPO_TYPE = "model" | |
| HF_REVISION = "main" | |
| OPENAI_BATCH_ENDPOINT = "/v1/responses" | |
| TEXT_PURPOSE_PREFIX = "salia-character-responses-text-batch" | |
| TEXT_STATE_DIRECTORY_NAME = "Salia_Character_Text_Responses_Batch_To_HF" | |
| TEXT_STATE_DIRECTORIES = ( | |
| TEXT_STATE_DIRECTORY_NAME + "_1Image_OptionalText", | |
| TEXT_STATE_DIRECTORY_NAME + "_1Text", | |
| ) | |
| CUSTOM_SCORER_STATE_DIRECTORY_NAME = "Salia_Custom_Batch_Scorers" | |
| _SAFE_BATCH_ID = re.compile(r"^batch_[A-Za-z0-9_-]{1,200}$") | |
| _SAFE_FILE_ID = re.compile(r"^file-[A-Za-z0-9_-]{1,200}$") | |
| _STATE_READ_LOCK = threading.RLock() | |
| def _utc_now() -> str: | |
| return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") | |
| def _clean_secret(value: str, kind: str) -> str: | |
| cleaned = str(value or "").strip() | |
| if cleaned.lower() in {"", "none", "xxx", "sk_xxx", "sk-xxx", "hf_xxx"}: | |
| raise ValueError(f"A real {kind} key is required.") | |
| return cleaned | |
| def _output_root() -> Path: | |
| if folder_paths is not None: | |
| return Path(folder_paths.get_output_directory()) | |
| return Path.cwd() / "output" | |
| def _write_json(path: Path, value: Any) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| temporary = path.with_suffix(path.suffix + ".part") | |
| temporary.write_text( | |
| json.dumps(value, ensure_ascii=False, indent=2) + "\n", | |
| encoding="utf-8", | |
| ) | |
| os.replace(temporary, path) | |
| def _read_json(path: Path) -> Dict[str, Any]: | |
| value = json.loads(path.read_text(encoding="utf-8-sig")) | |
| if not isinstance(value, dict): | |
| raise ValueError(f"Expected a JSON object in {path}.") | |
| return value | |
| def _write_bytes_atomically(path: Path, data: bytes) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| temporary = path.with_name(path.name + f".{os.getpid()}.{secrets.token_hex(8)}.part") | |
| try: | |
| temporary.write_bytes(data) | |
| os.replace(temporary, path) | |
| finally: | |
| try: | |
| temporary.unlink() | |
| except FileNotFoundError: | |
| pass | |
| def _load_hf_tools(): | |
| try: | |
| from huggingface_hub import CommitOperationAdd, HfApi, hf_hub_download | |
| except ImportError as exception: | |
| raise RuntimeError( | |
| "huggingface_hub is missing. Install it with ComfyUI's Python and restart ComfyUI." | |
| ) from exception | |
| return CommitOperationAdd, HfApi, hf_hub_download | |
| def _repo_commit(api: Any, hf_key: str = "") -> str: | |
| info = api.repo_info( | |
| repo_id=HF_REPO_ID, | |
| repo_type=HF_REPO_TYPE, | |
| revision=HF_REVISION, | |
| token=hf_key or None, | |
| ) | |
| commit = str(getattr(info, "sha", "") or "").strip() | |
| if not commit: | |
| raise ValueError(f"Could not resolve the current commit of {HF_REPO_ID}.") | |
| return commit | |
| def _safe_relative_asset(value: str, default_extension: str, label: str) -> str: | |
| normalized = str(value or "").strip().replace("\\", "/").strip("/") | |
| if not normalized: | |
| raise ValueError(f"{label} cannot be empty.") | |
| parts = normalized.split("/") | |
| if any( | |
| part in {"", ".", ".."} | |
| or any(ord(character) < 32 or ord(character) == 127 for character in part) | |
| for part in parts | |
| ): | |
| raise ValueError(f"Unsafe relative {label} value: {value!r}") | |
| if len(normalized) > 500: | |
| raise ValueError(f"{label} is too long.") | |
| if not Path(normalized).suffix: | |
| normalized += default_extension | |
| return normalized | |
| def _normalize_result_file(value: str) -> str: | |
| target = _safe_relative_asset(value, ".txt", "Result_File") | |
| if "/" in target: | |
| raise ValueError("Result_File must be one filename, not a nested path.") | |
| if Path(target).suffix.lower() != ".txt": | |
| raise ValueError("Result_File must be a TXT filename or a name without extension.") | |
| return target | |
| def _remote_targets_identical( | |
| api: Any, | |
| results: Sequence[Mapping[str, Any]], | |
| revision: str, | |
| hf_key: str, | |
| hf_hub_download: Any, | |
| ) -> bool: | |
| for result in results: | |
| target_path = str(result["targetPath"]) | |
| if not api.file_exists( | |
| repo_id=HF_REPO_ID, | |
| filename=target_path, | |
| repo_type=HF_REPO_TYPE, | |
| revision=revision, | |
| token=hf_key, | |
| ): | |
| return False | |
| local = hf_hub_download( | |
| repo_id=HF_REPO_ID, | |
| filename=target_path, | |
| repo_type=HF_REPO_TYPE, | |
| revision=revision, | |
| token=hf_key, | |
| force_download=True, | |
| ) | |
| actual = hashlib.sha256(Path(local).read_bytes()).hexdigest() | |
| if actual != str(result["sha256"]).lower(): | |
| return False | |
| return True | |
| def _publish_text_results( | |
| results: Sequence[Mapping[str, Any]], | |
| target_name: str, | |
| hf_key: str, | |
| *, | |
| commit_context: str, | |
| ) -> Tuple[str, str]: | |
| CommitOperationAdd, HfApi, hf_hub_download = _load_hf_tools() | |
| api = HfApi(token=hf_key) | |
| target_commit = _repo_commit(api, hf_key) | |
| if _remote_targets_identical(api, results, target_commit, hf_key, hf_hub_download): | |
| return "already_published", target_commit | |
| operations = [ | |
| CommitOperationAdd( | |
| path_in_repo=str(result["targetPath"]), | |
| path_or_fileobj=str(result["localPath"]), | |
| ) | |
| for result in results | |
| ] | |
| commit = api.create_commit( | |
| repo_id=HF_REPO_ID, | |
| repo_type=HF_REPO_TYPE, | |
| revision=HF_REVISION, | |
| operations=operations, | |
| commit_message=(f"Write {target_name} for {len(results)} characters {commit_context}"), | |
| parent_commit=target_commit, | |
| token=hf_key, | |
| ) | |
| oid = str(getattr(commit, "oid", "") or "").strip() | |
| if not oid: | |
| raise RuntimeError("Hugging Face commit completed without an oid.") | |
| return "published", oid | |
| def _openai_error(exception: urllib.error.HTTPError) -> str: | |
| try: | |
| body = exception.read().decode("utf-8", errors="replace") | |
| parsed = json.loads(body) | |
| error = parsed.get("error") if isinstance(parsed, dict) else None | |
| if isinstance(error, dict): | |
| return str(error.get("message") or body)[:1200] | |
| return body[:1200] | |
| except Exception: | |
| return str(exception) | |
| def _openai_json( | |
| method: str, | |
| path: str, | |
| openai_key: str, | |
| payload: Mapping[str, Any] | None = None, | |
| timeout: int = 240, | |
| ) -> Dict[str, Any]: | |
| data = None | |
| headers = { | |
| "Authorization": "Bearer " + openai_key, | |
| "User-Agent": f"Salia-Custom-Batch-Scorer/{NODE_VERSION}", | |
| } | |
| if payload is not None: | |
| data = json.dumps(payload, separators=(",", ":")).encode("utf-8") | |
| headers["Content-Type"] = "application/json" | |
| request = urllib.request.Request( | |
| "https://api.openai.com" + path, | |
| data=data, | |
| headers=headers, | |
| method=method, | |
| ) | |
| try: | |
| with urllib.request.urlopen(request, timeout=timeout) as response: | |
| parsed = json.loads(response.read().decode("utf-8")) | |
| except urllib.error.HTTPError as exception: | |
| raise RuntimeError(f"OpenAI API {method} {path} failed: {_openai_error(exception)}") from exception | |
| if not isinstance(parsed, dict): | |
| raise RuntimeError(f"OpenAI API {path} returned a non-object response.") | |
| return parsed | |
| def _download_openai_file(file_id: str, destination: Path, openai_key: str) -> Path: | |
| if not _SAFE_FILE_ID.fullmatch(file_id): | |
| raise ValueError("OpenAI output file id is invalid.") | |
| destination.parent.mkdir(parents=True, exist_ok=True) | |
| temporary = destination.with_suffix(destination.suffix + ".part") | |
| request = urllib.request.Request( | |
| "https://api.openai.com/v1/files/" + urllib.parse.quote(file_id, safe="") + "/content", | |
| headers={ | |
| "Authorization": "Bearer " + openai_key, | |
| "User-Agent": f"Salia-Custom-Batch-Scorer/{NODE_VERSION}", | |
| }, | |
| method="GET", | |
| ) | |
| try: | |
| with urllib.request.urlopen(request, timeout=3600) as response, temporary.open("wb") as output: | |
| shutil.copyfileobj(response, output, length=1024 * 1024) | |
| os.replace(temporary, destination) | |
| except urllib.error.HTTPError as exception: | |
| if temporary.exists(): | |
| temporary.unlink() | |
| raise RuntimeError("OpenAI Batch output download failed: " + _openai_error(exception)) from exception | |
| except Exception: | |
| if temporary.exists(): | |
| temporary.unlink() | |
| raise | |
| if not destination.is_file() or destination.stat().st_size == 0: | |
| raise ValueError("OpenAI Batch output JSONL is empty.") | |
| return destination | |
| def _batch_status(batch_id: str, openai_key: str) -> Dict[str, Any]: | |
| if not _SAFE_BATCH_ID.fullmatch(batch_id): | |
| raise ValueError("Custom batch id is invalid.") | |
| return _openai_json("GET", "/v1/batches/" + urllib.parse.quote(batch_id, safe=""), openai_key) | |
| def _request_counts(batch: Mapping[str, Any]) -> Tuple[int, int, int]: | |
| counts = batch.get("request_counts") | |
| if not isinstance(counts, Mapping): | |
| return 0, 0, 0 | |
| try: | |
| return ( | |
| int(counts.get("total") or 0), | |
| int(counts.get("completed") or 0), | |
| int(counts.get("failed") or 0), | |
| ) | |
| except (TypeError, ValueError): | |
| return 0, 0, 0 | |
| def _short_error(value: Any) -> str: | |
| try: | |
| if isinstance(value, str): | |
| return value[:800] | |
| return json.dumps(value, ensure_ascii=False, separators=(",", ":"))[:800] | |
| except Exception: | |
| return str(value)[:800] | |
| def _response_text_result(body: Mapping[str, Any]) -> str: | |
| status = str(body.get("status") or "") | |
| if status and status != "completed": | |
| raise ValueError(f"Responses result status is {status!r}, not 'completed'.") | |
| if body.get("error"): | |
| raise ValueError(_short_error(body.get("error"))) | |
| top_level = body.get("output_text") | |
| if isinstance(top_level, str) and top_level.strip(): | |
| return top_level.strip() | |
| output = body.get("output") | |
| if not isinstance(output, list): | |
| raise ValueError("Responses result has no output array.") | |
| text_parts: List[str] = [] | |
| refusal_parts: List[str] = [] | |
| for item in output: | |
| if not isinstance(item, Mapping) or str(item.get("type") or "") != "message": | |
| continue | |
| content = item.get("content") | |
| if not isinstance(content, list): | |
| continue | |
| for part in content: | |
| if not isinstance(part, Mapping): | |
| continue | |
| part_type = str(part.get("type") or "") | |
| if part_type == "output_text": | |
| text_value = part.get("text") | |
| if isinstance(text_value, str) and text_value: | |
| text_parts.append(text_value) | |
| elif part_type == "refusal": | |
| refusal = str(part.get("refusal") or part.get("text") or "").strip() | |
| if refusal: | |
| refusal_parts.append(refusal) | |
| combined = "".join(text_parts).strip() | |
| if combined: | |
| return combined | |
| if refusal_parts: | |
| raise ValueError("Model returned a refusal instead of output text: " + " ".join(refusal_parts)[:1200]) | |
| raise ValueError("Responses result contains no non-empty output_text.") | |
| def _text_request_mapping_by_custom_id(state: Mapping[str, Any]) -> Dict[str, Mapping[str, Any]]: | |
| requests = state.get("requests") | |
| if not isinstance(requests, list): | |
| raise ValueError("Saved Batch state has no request manifest.") | |
| mapping: Dict[str, Mapping[str, Any]] = {} | |
| for request in requests: | |
| if not isinstance(request, Mapping): | |
| continue | |
| custom_id = str(request.get("batchCustomId") or "") | |
| if not custom_id: | |
| continue | |
| if custom_id in mapping: | |
| raise ValueError(f"Saved state contains duplicate custom_id {custom_id!r}.") | |
| mapping[custom_id] = request | |
| if not mapping: | |
| raise ValueError("Saved Batch state contains no request mappings.") | |
| return mapping | |
| def _write_text_result_file(results_directory: Path, character: str, text: str) -> Tuple[Path, bytes]: | |
| results_directory.mkdir(parents=True, exist_ok=True) | |
| normalized_text = str(text) | |
| if not normalized_text.endswith("\n"): | |
| normalized_text += "\n" | |
| data = normalized_text.encode("utf-8") | |
| local_name = hashlib.sha256(character.encode("utf-8")).hexdigest()[:16] | |
| local_path = results_directory / f"{local_name}.txt" | |
| _write_bytes_atomically(local_path, data) | |
| return local_path, data | |
| def _text_state_roots() -> List[Path]: | |
| return [_output_root() / name for name in TEXT_STATE_DIRECTORIES] | |
| def _find_saved_batch_state(batch_id: str) -> Tuple[Dict[str, Any], Path]: | |
| searched: List[str] = [] | |
| candidates: List[Tuple[int, Path, Dict[str, Any]]] = [] | |
| with _STATE_READ_LOCK: | |
| for root in _text_state_roots(): | |
| searched.append(str(root.resolve())) | |
| if not root.exists(): | |
| continue | |
| for path in root.rglob("*.json"): | |
| try: | |
| state = _read_json(path) | |
| except Exception: | |
| continue | |
| if str(state.get("batchId") or "").strip() != batch_id: | |
| continue | |
| purpose = str(state.get("purpose") or "") | |
| if not purpose.startswith(TEXT_PURPOSE_PREFIX): | |
| continue | |
| requests = state.get("requests") | |
| if not isinstance(requests, list) or not requests: | |
| continue | |
| score = 0 | |
| if path.name == "batch_state.json": | |
| score += 50 | |
| if "/aborted/" in path.as_posix().replace("\\", "/"): | |
| score += 40 | |
| if str(state.get("runRoot") or ""): | |
| score += 20 | |
| if str(state.get("statePath") or ""): | |
| score += 5 | |
| score += min(len(requests), 100) | |
| candidates.append((score, path, state)) | |
| if not candidates: | |
| raise FileNotFoundError( | |
| "No local saved text-batch state was found for custom batch id " | |
| f"{batch_id!r}. Searched: " + " | ".join(searched) | |
| ) | |
| candidates.sort(key=lambda item: (item[0], str(item[1])), reverse=True) | |
| _, path, state = candidates[0] | |
| return dict(state), path | |
| def _new_custom_run_root(node_slug: str, batch_id: str) -> Path: | |
| parent = _output_root() / CUSTOM_SCORER_STATE_DIRECTORY_NAME / node_slug / "runs" | |
| stamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| safe_batch = batch_id if _SAFE_BATCH_ID.fullmatch(batch_id) else "unknown_batch" | |
| candidate = parent / f"{stamp}_{safe_batch}" | |
| suffix = 1 | |
| while candidate.exists(): | |
| suffix += 1 | |
| candidate = parent / f"{stamp}_{safe_batch}_{suffix}" | |
| candidate.mkdir(parents=True, exist_ok=False) | |
| return candidate | |
| def _process_custom_batch( | |
| *, | |
| node_slug: str, | |
| batch_id: str, | |
| result_file: str, | |
| openai_key: str, | |
| hf_key: str, | |
| score_func, | |
| ) -> str: | |
| normalized_batch_id = str(batch_id or "").strip() | |
| if not _SAFE_BATCH_ID.fullmatch(normalized_batch_id): | |
| raise ValueError("custom_batch_id must look like 'batch_...'.") | |
| normalized_result_file = _normalize_result_file(result_file) | |
| openai_token = _clean_secret(openai_key, "OpenAI API") | |
| hf_token = _clean_secret(hf_key, "Hugging Face") | |
| state, state_path = _find_saved_batch_state(normalized_batch_id) | |
| request_by_id = _text_request_mapping_by_custom_id(state) | |
| batch = _batch_status(normalized_batch_id, openai_token) | |
| if str(batch.get("id") or "") != normalized_batch_id: | |
| raise ValueError("OpenAI returned a different Batch identity.") | |
| if str(batch.get("endpoint") or "") != OPENAI_BATCH_ENDPOINT: | |
| raise ValueError("Saved Batch does not target /v1/responses.") | |
| status = str(batch.get("status") or "unknown") | |
| total, completed, failed = _request_counts(batch) | |
| if status in {"validating", "in_progress", "finalizing", "cancelling"}: | |
| return ( | |
| f"Waiting. Custom Batch {normalized_batch_id} is {status!r} " | |
| f"(total={total}, completed={completed}, failed={failed})." | |
| ) | |
| if status in {"failed", "expired", "cancelled"}: | |
| raise ValueError( | |
| f"Custom Batch {normalized_batch_id} ended as {status!r}. Nothing was published." | |
| ) | |
| if status != "completed": | |
| raise ValueError(f"Unknown Batch status {status!r}. Nothing was published.") | |
| output_file_id = str(batch.get("output_file_id") or "") | |
| if completed and not _SAFE_FILE_ID.fullmatch(output_file_id): | |
| raise ValueError( | |
| f"Batch {normalized_batch_id} reports {completed} completed requests but has no valid output_file_id." | |
| ) | |
| run_root = _new_custom_run_root(node_slug, normalized_batch_id) | |
| diagnostics_path = run_root / "custom_batch_score_diagnostics.json" | |
| failure_report_path = run_root / "custom_batch_score_failures.json" | |
| output_jsonl = run_root / "openai_batch_output.jsonl" | |
| if completed: | |
| _download_openai_file(output_file_id, output_jsonl, openai_token) | |
| else: | |
| output_jsonl.write_text("", encoding="utf-8") | |
| results: List[Dict[str, Any]] = [] | |
| failures: List[Dict[str, Any]] = [] | |
| diagnostics: List[Dict[str, Any]] = [] | |
| seen = set() | |
| with output_jsonl.open("r", encoding="utf-8-sig", errors="strict") as handle: | |
| for line_number, line in enumerate(handle, start=1): | |
| if not line.strip(): | |
| continue | |
| try: | |
| record = json.loads(line) | |
| except Exception as exception: | |
| raise ValueError( | |
| f"Batch output line {line_number} is invalid JSON: {_short_error(exception)}" | |
| ) from exception | |
| if not isinstance(record, Mapping): | |
| raise ValueError(f"Batch output line {line_number} is not an object.") | |
| custom_id = str(record.get("custom_id") or "") | |
| mapping = request_by_id.get(custom_id) | |
| if mapping is None: | |
| raise ValueError(f"Batch output contains unexpected custom_id {custom_id!r}.") | |
| if custom_id in seen: | |
| raise ValueError(f"Batch output contains duplicate custom_id {custom_id!r}.") | |
| seen.add(custom_id) | |
| diagnostic: Dict[str, Any] = { | |
| "lineNumber": line_number, | |
| "batchCustomId": custom_id, | |
| "character": str(mapping.get("character") or ""), | |
| "targetPath": str(mapping.get("targetPath") or ""), | |
| "statusCode": 0, | |
| "responseStatus": "", | |
| "error": "", | |
| } | |
| try: | |
| response = record.get("response") | |
| if not isinstance(response, Mapping): | |
| raise ValueError(_short_error(record.get("error"))) | |
| status_code = int(response.get("status_code") or 0) | |
| diagnostic["statusCode"] = status_code | |
| if status_code < 200 or status_code >= 300: | |
| raise ValueError( | |
| f"HTTP {status_code}: " + _short_error(response.get("body") or record.get("error")) | |
| ) | |
| body = response.get("body") | |
| if not isinstance(body, Mapping): | |
| raise ValueError("Successful Batch line has no Responses body.") | |
| diagnostic["responseStatus"] = str(body.get("status") or "") | |
| raw_text = _response_text_result(body) | |
| scored_text = score_func(raw_text) | |
| character = str(mapping["character"]) | |
| local_path, data = _write_text_result_file(run_root / "scored_text", character, scored_text) | |
| diagnostic["textByteCount"] = len(data) | |
| diagnostic["textPreview"] = scored_text[:1000] | |
| results.append( | |
| { | |
| "batchCustomId": custom_id, | |
| "character": character, | |
| "targetPath": f"{character}/{normalized_result_file}", | |
| "localPath": str(local_path.resolve()), | |
| "sha256": hashlib.sha256(data).hexdigest(), | |
| "byteCount": len(data), | |
| } | |
| ) | |
| except Exception as exception: | |
| reason = _short_error(str(exception)) | |
| diagnostic["error"] = reason | |
| failures.append( | |
| { | |
| "batchCustomId": custom_id, | |
| "character": str(mapping.get("character") or ""), | |
| "targetPath": f"{str(mapping.get('character') or '')}/{normalized_result_file}", | |
| "reason": reason, | |
| "failureKind": "scoring_or_generation", | |
| } | |
| ) | |
| diagnostics.append(diagnostic) | |
| missing = sorted(set(request_by_id) - seen) | |
| for custom_id in missing: | |
| mapping = request_by_id[custom_id] | |
| failure = { | |
| "batchCustomId": custom_id, | |
| "character": str(mapping.get("character") or ""), | |
| "targetPath": f"{str(mapping.get('character') or '')}/{normalized_result_file}", | |
| "reason": "OpenAI returned no successful output line for this request.", | |
| "failureKind": "request_error", | |
| } | |
| failures.append(failure) | |
| diagnostics.append( | |
| { | |
| "lineNumber": None, | |
| "batchCustomId": custom_id, | |
| "character": failure["character"], | |
| "targetPath": failure["targetPath"], | |
| "statusCode": 0, | |
| "responseStatus": "", | |
| "error": failure["reason"], | |
| "diagnosticKind": "batch_request_error", | |
| } | |
| ) | |
| results.sort(key=lambda result: str(result["character"]).casefold()) | |
| failures.sort(key=lambda failure: str(failure["character"]).casefold()) | |
| _write_json( | |
| diagnostics_path, | |
| { | |
| "formatVersion": 1, | |
| "batchId": normalized_batch_id, | |
| "statePath": str(state_path.resolve()), | |
| "rawBatchOutputJsonlPath": str(output_jsonl.resolve()), | |
| "returnedResponseCount": len(seen), | |
| "successfulScoreCount": len(results), | |
| "failureCount": len(failures), | |
| "entries": diagnostics, | |
| }, | |
| ) | |
| if failures: | |
| _write_json( | |
| failure_report_path, | |
| { | |
| "formatVersion": 1, | |
| "batchId": normalized_batch_id, | |
| "statePath": str(state_path.resolve()), | |
| "failureCount": len(failures), | |
| "failures": failures, | |
| }, | |
| ) | |
| action = "no_results" | |
| commit_oid = "" | |
| if results: | |
| action, commit_oid = _publish_text_results( | |
| results, | |
| normalized_result_file, | |
| hf_token, | |
| commit_context=f"via custom Batch scorer {normalized_batch_id}", | |
| ) | |
| if failures: | |
| return ( | |
| f"Partial Done. custom Batch {normalized_batch_id} processed. " | |
| f"Publish action={action!r}. Published/scored {len(results)} character TXT files as {normalized_result_file!r}. " | |
| f"Failures={len(failures)}. Diagnostics: {diagnostics_path.resolve()} | Failure report: {failure_report_path.resolve()}" | |
| ) | |
| return ( | |
| f"All Done. custom Batch {normalized_batch_id} processed. " | |
| f"Publish action={action!r}. Published/scored {len(results)} character TXT files as {normalized_result_file!r}. " | |
| f"Diagnostics: {diagnostics_path.resolve()} | Commit: {commit_oid}" | |
| ) | |
| def _score_for_hf(vision_result: str) -> str: | |
| dere, top_3_deres, scoring_summary = DereVisionScorer().score_dere(vision_result) | |
| if not dere: | |
| raise ValueError(scoring_summary or "Dere scorer returned no result.") | |
| return ( | |
| "Dere: {}\n" | |
| "Top 3: {}\n\n" | |
| "{}\n" | |
| ).format(dere, top_3_deres, scoring_summary) | |
| # ============================================================================= | |
| # FIXING TEST MODE: optional NEW cohort multipliers, with NO extra/tie-break scores | |
| # ============================================================================= | |
| _DERE_RENAMES_FIXING = {'Sunao Cool': 'Sunaocool', 'Sunao Heat': 'Sunaoheat', 'Sunao Surreal': 'Sunaosurreal', 'S Dere / Sadodere': 'Sadodere', 'Yandere — Sick': 'Yansick', 'Yandere — Yankii': 'YankiiYandere', 'Megadere — Mega': 'Megadere', 'Mayadere — Japanese': 'Mayadere', 'Megadere — Goddess': 'Megamide', 'Mayadere — Western': 'Westmayadere', 'Kamidere — Bite': 'Kamikami', 'Kamidere — Deity': 'Kamidere', 'M Dere': 'Masodere', 'Tsun-Aho': 'Tsunaho', 'Tsun-Ama': 'Tsunama', 'Tsun-Pure': 'Tsunpure', 'Gou-dere': 'Goudere', 'Sunao Chill': 'Sunaochill', 'Tsun-utsu': 'Tsunutsu'} | |
| _DERE_TO_AVG_MULTIPLIER_FIXING = {'Himedere': 0.943226, 'Ambidere': 0.918176, 'Bakadere': 0.964209, 'Biridere': 1.197835, 'Biyadere': 0.93106, 'Bocchandere': 1.007229, 'Bokodere': 1.158822, 'Borodere': 1.191923, 'Bosudere': 0.894561, 'Bureidere': 0.952454, 'Butsudere': 0.841989, 'Chindere': 0.955586, 'Dandere': 1.41492, 'Danyan': 0.926542, 'Deredere': 0.908661, 'Dereutsu': 0.908943, 'Dorodere': 1.029681, 'Erodere': 0.935712, 'Erohaji': 1.299714, 'Fuandere': 1.328516, 'Fushidere': 1.013867, 'Gundere': 0.823542, 'Gurodere': 0.920399, 'Hamedere': 0.811333, 'Hikadere': 1.200137, 'Hinedere': 0.924739, 'Hokodere': 0.834814, 'Kahodere': 0.870576, 'Kakkodere': 0.935341, 'Kichidere': 1.016932, 'Kidere': 0.963218, 'Kiredere': 1.132622, 'Kundere': 0.832525, 'Kurodere': 0.979905, 'Kuudere': 0.950722, 'Kuutsun': 0.97301, 'Masodere': 0.970982, 'Megamide': 0.976595, 'Megaun': 0.974824, 'Narudere': 0.8981, 'Nemuidere': 1.298231, 'Nisedere': 1.006706, 'Nyandere': 0.9014, 'Onidere': 1.021052, 'Pyondere': 0.910858, 'Roshidere': 0.829068, 'Sadodere': 0.900583, 'Sashidere': 0.883635, 'Sattodere': 0.821962, 'Shijidere': 0.876167, 'Shittodere': 1.120021, 'Sunaochill': 1.295406, 'Taidere': 1.478803, 'Teasedere': 0.888733, 'Thugdere': 0.992674, 'Toubodere': 1.00978, 'Toukadere': 0.858785, 'Tsunaho': 1.097955, 'Tsunama': 1.117567, 'Tsunbaka': 1.096329, 'Tsundere': 1.153682, 'Tsundero': 0.987008, 'Tsundora': 0.962384, 'Tsunpure': 1.149089, 'Tsunshun': 1.213801, 'Tsunutsu': 1.140001, 'Tsuyodere': 1.00727, 'Usodere': 1.045222, 'Utadere': 0.88179, 'Utsudan': 1.326589, 'Utsudere': 1.299547, 'Uzadere': 0.899334, 'Westmayadere': 1.040059, 'Yancool': 0.85428, 'Yansick': 1.065965, 'Zondere': 0.990691, 'Amadere': 0.863264, 'Byoukidere': 0.809163, 'Dandoro': 0.971538, 'Darudere': 1.432758, 'Doromuga': 0.961023, 'Gandere': 1.154484, 'Gesudere': 0.937201, 'Goudere': 0.936738, 'Hajidere': 1.378644, 'Inudere': 0.833058, 'Jendere': 0.921319, 'Kamidere': 1.021282, 'Kamikami': 1.145439, 'Kamikan': 0.919426, 'Kanedere': 1.045112, 'Kekkondere': 0.848293, 'Kiridere': 0.987166, 'Kondere': 0.925612, 'Kumadere': 0.851288, 'Kuzudere': 1.036878, 'Mayadere': 1.134827, 'Megadere': 0.881445, 'Nipadere': 0.910701, 'Nisekami': 0.860631, 'Norodere': 1.400411, 'Ojoudere': 1.000232, 'Osadere': 0.908937, 'Oujidere': 1.021354, 'Oujodere': 0.961048, 'Rindere': 0.926787, 'Shindere': 0.969181, 'Smugdere': 0.92349, 'Sunaocool': 0.903576, 'Sunaoheat': 0.934234, 'Sunaosurreal': 1.028935, 'Tekidere': 1.051304, 'Teredere': 1.321377, 'Tomedere': 0.913198, 'Tsungire': 1.204133, 'Tsunneko': 1.153867, 'Tsuntere': 1.429546, 'Undere': 0.977391, 'Yanheat': 1.046777, 'YankiiYandere': 1.03601, 'Yoidere': 0.870548, 'Tsunpuri': 0.942959} | |
| def _canonical_dere_name_fixing(name): | |
| return _DERE_RENAMES_FIXING.get(str(name), str(name)) | |
| def _score_answers_with_mult_fixing(answers): | |
| """Original matrix points + user's NEW multipliers. Nothing else.""" | |
| raw_scores = {candidate: 0 for candidate in _CANDIDATES} | |
| for q, letter in answers.items(): | |
| row = _MATRIX.get("Q{}".format(q), {}).get(letter) | |
| if not row: | |
| continue | |
| for candidate in _CANDIDATES: | |
| raw_scores[candidate] += int(row[candidate]) | |
| parsed_count = len(answers) | |
| max_abs = 4 * parsed_count | |
| adjusted_scores = {} | |
| adjusted_fits = {} | |
| for candidate, raw_score in raw_scores.items(): | |
| canonical = _canonical_dere_name_fixing(candidate) | |
| if canonical not in _DERE_TO_AVG_MULTIPLIER_FIXING: | |
| raise RuntimeError("Missing NEW Dere multiplier for {}".format(canonical)) | |
| base_fit = _fit_percent(raw_score, parsed_count) | |
| adjusted_fit = base_fit * float(_DERE_TO_AVG_MULTIPLIER_FIXING[canonical]) | |
| adjusted_score = int(round((adjusted_fit / 100.0) * (2.0 * max_abs) - max_abs)) | |
| adjusted_scores[candidate] = adjusted_score | |
| adjusted_fits[candidate] = adjusted_fit | |
| # Same weighted ranking rule used to produce the supplied NEW multiplier result. | |
| return sorted( | |
| adjusted_scores.items(), | |
| key=lambda item: (-adjusted_fits[item[0]], _canonical_dere_name_fixing(item[0]).casefold()), | |
| ) | |
| def _score_for_hf_with_mult_fixing(vision_result: str) -> str: | |
| answers, methods, unresolved = _parse_answers(vision_result) | |
| parsed_count = len(answers) | |
| if parsed_count == 0: | |
| raise ValueError( | |
| "Parsed 0/100 scored answers (Q11-Q110). Expected the numbered Dere questionnaire output." | |
| ) | |
| ranked = _score_answers_with_mult_fixing(answers) | |
| top3 = ranked[:3] | |
| dere = _canonical_dere_name_fixing(top3[0][0]) | |
| top_3_deres = ", ".join(_canonical_dere_name_fixing(name) for name, _ in top3) | |
| letter_counts = Counter(answers.values()) | |
| method_counts = Counter(methods.values()) | |
| lines = [ | |
| "Dere scoring summary", | |
| "====================", | |
| "MODE: WITH NEW MULTIPLIERS; original matrix only; no extra score; no tie-breaker score", | |
| "Parsed scored answers: {}/100".format(parsed_count), | |
| "Answer letters: A={} | B={} | C={} | D={}".format( | |
| letter_counts.get("A", 0), letter_counts.get("B", 0), | |
| letter_counts.get("C", 0), letter_counts.get("D", 0), | |
| ), | |
| "Parse methods: {}".format( | |
| ", ".join("{}={}".format(name, count) for name, count in sorted(method_counts.items())) or "none" | |
| ), | |
| "", | |
| "TOP 3", | |
| ] | |
| for index, (name, score) in enumerate(top3, start=1): | |
| lines.append("{}. {} | score {:+d}".format(index, _canonical_dere_name_fixing(name), score)) | |
| lines.extend(["", "TOP 10"]) | |
| for index, (name, score) in enumerate(ranked[:10], start=1): | |
| lines.append("{:>2}. {} | {:+d}".format(index, _canonical_dere_name_fixing(name), score)) | |
| if unresolved: | |
| missing = sorted(unresolved) | |
| preview = ", ".join("Q{}".format(q) for q in missing[:30]) | |
| if len(missing) > 30: | |
| preview += ", ... (+{} more)".format(len(missing) - 30) | |
| lines.extend(["", "WARNING: {} scored question(s) were not parsed.".format(len(unresolved)), "Unparsed: {}".format(preview)]) | |
| return ( | |
| "Dere: {}\n" | |
| "Top 3: {}\n\n" | |
| "{}\n" | |
| ).format(dere, top_3_deres, "\n".join(lines)) | |
| class HF_DereVisionScorer_CustomBatch_fixing_without_mult: | |
| RETURN_TYPES = ("STRING",) | |
| RETURN_NAMES = ("status_out",) | |
| FUNCTION = "run" | |
| CATEGORY = "Salia/HuggingFace/Scorers" | |
| OUTPUT_NODE = True | |
| def INPUT_TYPES(cls): | |
| return { | |
| "required": { | |
| "custom_batch_id": ("STRING", {"default": "batch_6a98cb8c3f8c8190be53e868d3c269c9", "multiline": False}), | |
| "Result_File": ("STRING", {"default": "dere_score", "multiline": False}), | |
| "openAi_key": ("STRING", {"default": "sk-xxx", "multiline": False}), | |
| "hf_key": ("STRING", {"default": "hf_xxx", "multiline": False}), | |
| } | |
| } | |
| def IS_CHANGED(cls, **kwargs): | |
| return float("nan") | |
| def run(self, custom_batch_id, Result_File, openAi_key, hf_key): | |
| try: | |
| # IMPORTANT: exact original _score_for_hf from the original no-mult CustomBatch file. | |
| status = _process_custom_batch( | |
| node_slug="dere_custom_batch_fixing_without_mult", | |
| batch_id=custom_batch_id, | |
| result_file=Result_File, | |
| openai_key=openAi_key, | |
| hf_key=hf_key, | |
| score_func=_score_for_hf, | |
| ) | |
| return (status,) | |
| except Exception as exception: | |
| return ( | |
| "ERROR: Dere CustomBatch fixing WITHOUT_MULT failed; nothing was intentionally published. " | |
| + type(exception).__name__ + ": " + str(exception), | |
| ) | |
| class HF_DereVisionScorer_CustomBatch_fixing_with_mult: | |
| RETURN_TYPES = ("STRING",) | |
| RETURN_NAMES = ("status_out",) | |
| FUNCTION = "run" | |
| CATEGORY = "Salia/HuggingFace/Scorers" | |
| OUTPUT_NODE = True | |
| def INPUT_TYPES(cls): | |
| return { | |
| "required": { | |
| "custom_batch_id": ("STRING", {"default": "batch_6a98cb8c3f8c8190be53e868d3c269c9", "multiline": False}), | |
| "Result_File": ("STRING", {"default": "dere_score", "multiline": False}), | |
| "openAi_key": ("STRING", {"default": "sk-xxx", "multiline": False}), | |
| "hf_key": ("STRING", {"default": "hf_xxx", "multiline": False}), | |
| } | |
| } | |
| def IS_CHANGED(cls, **kwargs): | |
| return float("nan") | |
| def run(self, custom_batch_id, Result_File, openAi_key, hf_key): | |
| try: | |
| status = _process_custom_batch( | |
| node_slug="dere_custom_batch_fixing_with_mult", | |
| batch_id=custom_batch_id, | |
| result_file=Result_File, | |
| openai_key=openAi_key, | |
| hf_key=hf_key, | |
| score_func=_score_for_hf_with_mult_fixing, | |
| ) | |
| return (status,) | |
| except Exception as exception: | |
| return ( | |
| "ERROR: Dere CustomBatch fixing WITH_MULT failed; nothing was intentionally published. " | |
| + type(exception).__name__ + ": " + str(exception), | |
| ) | |
| NODE_CLASS_MAPPINGS = { | |
| "HF_DereVisionScorer_CustomBatch_fixing_without_mult": HF_DereVisionScorer_CustomBatch_fixing_without_mult, | |
| "HF_DereVisionScorer_CustomBatch_fixing_with_mult": HF_DereVisionScorer_CustomBatch_fixing_with_mult, | |
| } | |
| NODE_DISPLAY_NAME_MAPPINGS = { | |
| "HF_DereVisionScorer_CustomBatch_fixing_without_mult": "HF Dere CustomBatch Fixing _without_mult", | |
| "HF_DereVisionScorer_CustomBatch_fixing_with_mult": "HF Dere CustomBatch Fixing _with_mult", | |
| } | |