From e5c0df308f2d3372f3d102e89c0a9c90e7751132 Mon Sep 17 00:00:00 2001 From: Grigo Date: Thu, 28 May 2026 08:42:46 +0300 Subject: [PATCH] first commit --- .gitignore | 43 +++++ Luna.png | Bin 0 -> 78626 bytes README.md | 0 Rin.png | Bin 0 -> 72806 bytes database/__init__.py | 0 database/db.py | 70 +++++++ libretranslate/docker-compose.yml | 13 ++ main.py | 39 ++++ models/__init__.py | 0 models/schemas.py | 35 ++++ pull.sh | 5 + push.sh | 5 + requirements.txt | 182 ++++++++++++++++++ routers/__init__.py | 0 routers/characters.py | 90 +++++++++ routers/chat.py | 199 +++++++++++++++++++ routers/images.py | 34 ++++ routers/personas.py | 42 ++++ routers/sessions.py | 48 +++++ routers/translate.py | 18 ++ services/__init__.py | 0 services/character_card.py | 214 +++++++++++++++++++++ services/llm.py | 63 ++++++ services/lorebook.py | 52 +++++ services/memory.py | 142 ++++++++++++++ services/persona_seed.py | 26 +++ services/personas.py | 168 ++++++++++++++++ services/sd_prompt.py | 125 ++++++++++++ services/sdbackend.py | 121 ++++++++++++ services/translate.py | 17 ++ static/css/app.css | 306 ++++++++++++++++++++++++++++++ static/index.html | 116 +++++++++++ static/js/app.js | 30 +++ static/js/chat.js | 260 +++++++++++++++++++++++++ static/js/personas.js | 164 ++++++++++++++++ static/js/sessions.js | 86 +++++++++ static/js/state.js | 24 +++ static/js/utils.js | 16 ++ 38 files changed, 2753 insertions(+) create mode 100644 .gitignore create mode 100644 Luna.png create mode 100644 README.md create mode 100644 Rin.png create mode 100644 database/__init__.py create mode 100644 database/db.py create mode 100644 libretranslate/docker-compose.yml create mode 100644 main.py create mode 100644 models/__init__.py create mode 100644 models/schemas.py create mode 100644 pull.sh create mode 100644 push.sh create mode 100644 requirements.txt create mode 100644 routers/__init__.py create mode 100644 routers/characters.py create mode 100644 routers/chat.py create mode 100644 routers/images.py create mode 100644 routers/personas.py create mode 100644 routers/sessions.py create mode 100644 routers/translate.py create mode 100644 services/__init__.py create mode 100644 services/character_card.py create mode 100644 services/llm.py create mode 100644 services/lorebook.py create mode 100644 services/memory.py create mode 100644 services/persona_seed.py create mode 100644 services/personas.py create mode 100644 services/sd_prompt.py create mode 100644 services/sdbackend.py create mode 100644 services/translate.py create mode 100644 static/css/app.css create mode 100644 static/index.html create mode 100644 static/js/app.js create mode 100644 static/js/chat.js create mode 100644 static/js/personas.js create mode 100644 static/js/sessions.js create mode 100644 static/js/state.js create mode 100644 static/js/utils.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4d9fa5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Python cache +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +*.egg-info/ +.eggs/ +dist/ +build/ + +# Virtual environments +.venv/ +venv/ +env/ + +# Environment & secrets +.env +.env.* +!.env.example + +# Generated images (ComfyUI / SD output) +static/images/ +data/images/ + +# Local database +data/ +*.db +*.sqlite3 + +# IDE / OS +.idea/ +.vscode/ +*.swp +*~ +.DS_Store +Thumbs.db + +# Logs +*.log diff --git a/Luna.png b/Luna.png new file mode 100644 index 0000000000000000000000000000000000000000..b5466d09fd4c77342e4a7742729e9a7d2c920aa9 GIT binary patch literal 78626 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/Rin.png b/Rin.png new file mode 100644 index 0000000000000000000000000000000000000000..08e4b70358063988b7b2aff105d1e86613a8815d GIT binary patch literal 72806 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%0Aajg^S_A^Lu4gT`@1zicv zYf#j|HB&lF?K@b>cwCP6^&l6_0|JjGpk@UdUk}Si;h@`RPCz{O_4R6#$Yq>wgPFIa zu@lWktL<~bM#snOFTU}0J8*cv_-UzJB);3jM9GKIUSV*Say*(CHqv36rYdUt-4&$(J0)*u4T^Tfo(4H%o8 zva5qb+Sph_kVLHRTy5JnZxN3czQ}w)AtzQx)q_AM5AlL?j`GFsqMR6$B&6jrt*UEl ziiI_5j^g9RL3QEBFtG@ph#C|gj#Qy7F-h}2%VMYsrX6hUXe=}ZWOU#E!T0RDPd;Tk zw`{TI+IoiyR;MMGm1jp$QnDCkc4*de4?9FPxJUd9CRI%ERaKfOff=a}86H;jOc9Xj zX@M^3YQ+O}&03Z>b{wpMq+-VU`+M!>S6*7Ia^0yCqM#+f!6pep;@jW;wtelZU$;ANzug{w_+b%8P@{k9@?}BZE2=6TMv)V! z{-cWI)|BIr%H=JSpOtCF?yZ{dSj;)7bF)rE_9|Y8(GR17}Tso;+7OiXD<&s0C z56IOAxVm$Mn|onnAQY?DQ%M^c8&jy8T}Em*3Hr0vhnC|CACv2V%HOelICDUs_ecZ2 zn+N3jailC&Sz%q>TkPI@?y?6TdcZbrTrU{={KA<>+g@*BE*HgFZfN5WvH-l2v(dTs zsaZ-A=FJ~H5pUa<;-PNzyS0E9g`shngF~Lwd^#wThq=exzi82n=zkpJq{1KaalJp8 znU%#dr@ksK?2HveYJyI>uE4Kg(=!OkoZbK6L)K8=WIuiBMSJ!2z4lAL^h;LT&}c7y z^)HH4RVinQ6o~#%f-YvMi&CSl*$l#DDGb@uxJAdM|eeElME;vVf+X`265_twV z=3*CLzh3Nwpm?JQWz~47#yuv=PVzqdP85oS>7|R8M4f~7&|Su`UFpr}gikCiX7VYB zd_j1|_aJO6a?B~V`PkfANiJeLZ{jrbrpV$y_>FLBD* z7?gkT+BO#PF_sdLQhzQw7STDCa14Zyckm|!l_^HDIDbG_6tBYl-@l&?lOf^A%&A0R za&lT5+0^8W%w&)dtRFOj!sxl2f=p;E>OlVR5B^a3mtXp8f6c!4^wTo0!y1bH{@1?k zuiPh^B0HD zIS$6pf|Q@+P21=~D5VX$Q6FR}Au0Vk3@bWsJm&f|dSMRljYZxbg@Q-Vbv=P(16p9x zmPuSj!@3B_g8YTARCPZqPPKq9K^ssqX%$Hyoyy0=8x6@igxP1$T~w}v2pC$QXAxRi zBP@{Q(Lepfi4#f;}`tP9M!{0KP(qCGrxq!VqD zuCAP~UCz140yly!2j*YBB}yvF+)d8!V5PV&*Cn8sYSlkT{!d?f)%Ne*t4Ku`IfI5~ zM<(IH-46Z(L;8GWVqkv9+zQdT=mXJEg>(W4IYMcP%{F@8QZf-&kQS3kMdMR?mSA#YE(V@r zOlCGo)@-TIdGz=(p%^I57By}rWB1*6pCCT!<^H9wf2VMLaAEoW7dSFZB!p#rDT^-b zn)7wZVad87=Q(if78TV(EmEZQDPp$xxu7ih#q$^J+c30GO)6gi0fTY@-W&_xh31&Q zftXqkGa10lnJ^|JTw~O%8yXoAk1d&ZCQYVg))j22HJV@(bmMYRZwBm*;W}NkiM-3S zB31DAa*3s=zB5rCN2lv3LVoSaMpv&vtZl zM7a&t=e5BBX#)f#L)(_30t#vjf3v9iQTM#gf`FYeQ#?ARlM9UU<8@qg#@?|x0>qO1wIS{ zBDshKE$RBRBa{d%6#r}2uC_?VjX!Im0pX>BQ*=uyEF{3RZjZ%CpR4rFp*VdKKDNmBF^ zTB4Ox%cZ{~xttg>WOh+{PY0(BlSJrOqE|i|LPlDNoUpqGMiOsQITLDO0#VMF`fJB* zVr;~o{Khxz+LbHT+14(oHy9`3`a!Q68mCzBxJaq1W0Y?kN#6=G;}AWwXq^c``{F@6 zc;JA9zrN1(eP@H=(m*taO#)J8k+FquQY`_XJBKttlU?iY*Mqrj&rTIDQ5R3|a^?g7 zLui9Q$$7n^lroFy-HcM0gx%lx#y9MzFTUVfJj^&P5`rx1wJ8dzYE@a{6FT46`FmU!kxtEh zA|dmc<~zWaX#e5L4_s}E)>MKZ^9|le7$HXh)Ec;OOR80b(9_e7O=2E_>Bz}sGD&rM zHVum=4NDw2HMMm92*TIj*C#IB{M?+Ja26oc`h12;L{w!x&14n`$-?X9GfW_wje#%0 zH6h52jmF2HWchO^KeGiODyO`c3{(fPRNqP7QK^YeaM zzd?>ic%6+8K@9npFntpC|MX|yDiF?HL;)G0YKaaOj|-7GR4F8DAxeXw5IS>-!lXjJ z)c=MHb8L04W&uECr5!nXTs{Ml5GQz30$yr@;mMkw@z8%q!71(LBTtmR zqB%Bi+F&1i_#u1fp$DydT~{#&f?(3$QTOiiAvX&ZiEbh)gz^iwbSz~;mD3(kA=iD9 zE6Tb4;cYFgBGX&BW_5AX2=BeoQR;^Tk`5)8{X7R@txBZ-*; z-sRfTHFfF|RohT6AMtWupS|_gn|AutSx0_>!1!pS@;3qM?-xDyqAtLzS4lXaLB*<| zeFGNMQqMu$yr`rgg#or6Zmx7-Vj&QO1X(1(q@rsoOo~L4SbPDdFykR2O4wz|%sR;= zvZbb;Dd#sRe270m0nzld_4M}I7r*#LOIK1{L<~D^v(wY|-6y{-f^4enRAYW~c`;2? zs;Nnf|IoqxDh4CV$~BQ?4U3z}V<)BIJeeV_S;@+fTLu5m2jS`xo535n*% z$H1gUD4^HH2W8`q-dFg!^!}1OJeoRAOs+6dxYcG{{fy@1jOp~Ly7NQv_hMuj;9)9#z!{Fa?Qc2pFj z0Z0cU{LPF2NTWqDwclbF_f)Fj$tb%-E6|@0WoLVU;%Oj6`SR@Ip zZB5dOz}8_)myH|4JaY>Xn1&lJ;k96d{KgG7Tz8NP7cs;sMPS)k!{S&VQUVlc zMhReZrTStd}Mqo(L!byDQ^f~vw0hjQ@ zk#1;cax_i8jJBw)JeM%S#rx<1LkR&J8bX0N<*G{Yt9(xr|7_eXE?n?QH#u&SY0eTl z(FXPE;NkNl)Hdq|{f>-KVcP@VLxRyJ>_Yq^zUs50!QAZecB|srF7XVM`u7W4RFV zjSQz@Ts0_+^fB^c_>fp9c^8g;FTO=6pIvaC5J@V;)eD_^{N^=SN>=HLUSM6pzW*pefAc^o-Ti)@$t|eA5#SfsDW48VKdoacXjQ%AWk@Q}%EE&Hp7L z>6539+L_ZQ1S~{b660`_>zIOzM9G0eEasvmWk)erQ$AM^!mpU3EClWtCwlF=E@^G{ zz2(T8=S?eD2^r{>=6#a+T8S70;eG(`nXf6275^=(rAp_!q+bW~_{vMKNF&H}f*(tZ z+d1Pzp|aV$Fn*X&uv8)-_#u#dZ|GdDUMGP-gv>wn_<^pcDwXt*sFbwCmd#rP`UGF7 zs8*PNHEDtvByyjT9M=3OK`#9MSHD|;R%XlAtqOJ0u8bN4@)cnaRt;sfIH;~AbV`mL z1wihtQPKui3$D;7g-#JC5oBdxuoL-k!KwFfSy29KB5|H9X2*^kwjX@wX`30J5Y-KN z5fycY1p6Wqj)gaf5ORKiZtom^#|8!mtiG<^iCCW4{-B5+;7kgpNN5zKj=57a_R)_% zYX9Ijf5Xygiox8(27FprJmvBg$TptgKfD3AcU`&-`gp6xl$^DqP9_e%>48ev6YiiOVx=mi<>o2wHO zio-L^<~%h!EiDudj3d2hwdp_-m@hc*a&x0JNANC(AA3yyMx9yG(A!|c9$FNsg2Lo$ zYDxNvR+G!9UXNaRWud?(?hUPl**VpoyZ5=0EkjId8j8}t@`e?06h~X4%oO`9NbfS8 z=p3;QU@TdUW3CiJpw{YfDmQL=a#9R}d_TDV5$!$}Lb8YHkEd@4=fk;U925*<{{`b< z4ji;+F}Q8rKtUa-Za_p8upwo z;Vb$nB%A6d$+rC(w(BFLOS#UDX)-Rof5bRNI-rzs_~LnRqF+w2!p8${+|7TS`e)ET zMc4gHr%u|DV@Cz#yf-i?LxIquqrFqRI^F~W85kO{-Z%RK)~&)kqwuM=R+}@TJ^*DU zThZAiWCY$rc_{-=sfokSnmffKPaJ)3Xvk)!@7puaobY%~Oop1pwe>YBGl+MtsFS+8 zqeEk-h1!B>aqup&T+87w_%N_!p{*Y$;!-1)$%#W4fp$bPRpvV=!%OeI_nr+54l2xp z>ISC5WMusl9^6=N{_R`0y-62t`poo{Za7@F+wCqGIvspKw%m&!{pf$#sZ%HHV9&ly zJMdN-ozHg=cXM9Qk22~D6%Dd#(EHRKV5YqS*83z39~e4QnIq$4!iZIV5wAII9JZrz za8!GkogRmP4wA6&VLS}w@yDN#p>v(X!((>i_D!K1E?&B5H}BjI%2io4DhUgDet35n zWax@Fw=^oe#gpPSfE!6dkRFrRHiOn>Ky!SO8J(SuJe*jZY)w9E-Q9ad#!UD`g-90} z%D%lby*4=1FZAP{U7d1;^E0zHU&fPoc`Xj(Gb~lZ|A8xH)+;wUC5aV(rdjYTpuJUL zoLsM=Y<+j{3Kxa0$N0SwHQZdI(C5P+xGb`jkb&L?q!PePR@YWlhUWb-aDES@#Iq$S zBwBH;7!+RJ;FnjHq*}#}wf_Kmg%u*`sG?LIUTqGswqHI8{)ua+hLXvu575`wZ-)-` zC>J9cr<@5X9$qABg{SdI+a1*yGn#5wu3uAEIo*R$m>PN$8yh@>cwSXG#KvZX!{Xw+ z{*4jSdFP%`U{UE`g&oJK5f!kp!s^0j#6dc|A=k=6Xoi&U{*>MByDPmDUPF|?{jaUB z*T#sKz#!sxs6%0_2NB`i$c)(1{q~JdLwdoBa3J`)X~8GFXq9K7uK;<9uhZ`sX`rmh z4>1ZXQkr^DuY_tmEiiub zB~ZE~@i1~_0S=G1A;%z@eC_qu?LYs~AKPeOpDoYKRo4HG%OgBUjR-s23bF+Kef{o! znN?m|)OTDm(0L8lEHCTYIM2gJ4~sXKUGCQH+xFaZU$*C-JEe+vi3b4I%kz;8=YL8(l+6(3Ms{sEftqUz^7*s7JB3^dr(fXk>4t?Q{g18 z@}i9I_74m=%f|GSz542_cKigwj*PJSgdoQUADmH_mE9C$@d>Y-6l=vFwX`IX1Lt(| z5^xc{F{6>wVs%Y*HZnY>8yb~Lqj*dq(Eoec&Y>GuKfbIvZA2#j-<>;&aHwOiwk`zy zE?gTidM2OKP!+#Jwt#T0rJ-I%yf8a&-Fv$=fwm0cM5bhidk)zXM~=v7cnCC=NcMjB zYtP&9wR|!KtM0!oZ2)0{|%0nuH*((d$IyrH6Lo%jL3lb#>`yeSN+1YUrpm zwd}Ia67w^C_S&nj+TFYTG9)|*n_ql3D+t@1emNt+$k`W6@`S;#_$LrMjjJn3~JMDhD zO~lHCOQRzrHaa|1hhp|5m$FZNNNTuT zwp%xD+QQtNjDsErV7$1%#aL)%)$!Zfcgr}06{?b**r|IS+4y-UC)YqlD|F6R*KB#! z@pB4`;iIK|ie^gsy-KP154cx~C7lxoFb}Fv_wP^Zvk?0E!4Lk_e)z+G(6ADV+piab zxt!%n{(L#2A{2|hqiwf55)lj~Y&D$eibJ3f)$8Bn;w;WiiOh#oi2;!tzOV40&$=-eYZOdlA zAtu3WhB4S>E%KUt&Kjj{=(FeSuFZ9|&M(!9?6X^Z#X>y}m(@lJ`dmhetbh=2UcIIV zL^nH@gF$tskGMN=ZkMlI5e&MevC*D>`nX(wB!pgEm{a)=ulJ>s&)Tu4pRz_~z{^Fs zbOh~rNW}Ym1P@B@%MA~u%e01{H%m7^gl)Zu;P_cBBtfpj0v#S0w4s4Pd9C@t+5#~} zx^V|)=+VQ6gBn-xR1Y!8I8UPm9THxyp~ToS3ohu*mtkR9Lq2-KM(+*l9{EmPuLW9P z9|_ByZb`#%#N^Ul-w^5CjO2O4laj1SL$XeUpd6l8??>*igpR$p6dZ@&4a4fgk|vK2A)c$~9m&qz(S zvvZHaC~giQMm5|>w8}ZFE7VKBVtC}9?R(^)K!W*#$GnA9-^9;6*7i39w_aIX5Mw)@ zKrW(zw!Z#8jktd8^l|+j%Fn%fyEJ%p^yuTZ|KI^F@;I-u z-Ol27@bmE&Lei{i3X7A}IB1$vor2$sd&W7BjgNUgw6-QMcl_BCP9~lrCU}syNFPy6 z)+s#wvlEyH$cRFXrtdhPrlwXq@>rX7b#x`O_e2Mt^tv{SL`DO7g%%P7;I%$(FgkHx z8x{o{o<9rg?3pu?y?f&55i#IICPg+Us))?IGnq;5D<0#p@g?Z!O@$4b%HAG;Yb~2| z?y_tyaum?qhJ)Eh8rTegw(Mji)z5jm{*H`9O!FI-hq;`GL{j+5E3b%bjf?&2SHEh5 z{R6sKsw}ZwhHDh45r*^earHGc%354k)rEO%p;+?;Teo%Tm5+{%SXcMHqnrP4$Ye#h2U_~E zLyueeP{|YVkr5#eV#Go~04!)@dDZ4e$CHV@@v-|NNPuLoWml^mEV!f@UWm%szV1%( z0q}p4?dT;qM~NYYRzSQCp~OY@7D%yIkgB69<@)K)661!?+S4_sJx3y;0X^C_EGnrg zRlI*mLp3!_m4HKG3iPUv$6h3iw{G6D&dx5oee!DpB>?{iwjO zxF#nj^-zV&-zeGjTes|;ciuIckr)s$K47vaX2}sJ5-|{V*V;l(p~v0ZcdV_o+45|< z;bz4qUi#ohGl0**&O~V$5F%bI78!tOT)T4B>g#Ky=fPCp!Gk?I$M1aSJAy$+Hux|4 zh*}hc0avbEvZjVco1C1|PRDh(cQ)$&v3OWWj~qOZH2=g;6_V%pFsqO4I^W!@{(*iO z$Kt}0QHa57@tk=!=RW$-hKEPxJ!|uIBL9wEfBIr1z~UA*A6#QGVgx+jiOI=i7Kfrs zZ9|jFwa~TV`KUZ$7(^iS$eJ6Sj@3)V;Hq+oSf_hc$MZ!mf>%i|P-uWE2IAE)ty2+v z`?`pzj6|K5yo;Y_z4(wms&lLu>c{>GuocSeV7~ZHP`b&=C_Td8bn)UvCFhhoIg#nv zxq$62YKLH#f!w)RI-trAl`KcZuIj;1>%M&XvOs2&Fp!)-FNGv-u*;~G1xj36UGfSA zl?g(DxkbD5;dyIoZME6yDbe<9qK*kXPEqdrO}6s@%$M6t7*fZfSA7p$g` zQ&sEwOaIrNJ#oVR;xGSP4;74$FmxN6jl`=|Rb|3psGN%zE}V=Ze!kB$39^M|0B#puR|GuvQz4=fQq$-op#V8wGT#KLAfamy(-<06i4^#HMj^#3Yc5WhY^< zX-0Y%nFJ9`7noW;i#oQb(fo1B`og{65rar_zYerB?E?c+-_ z(uUd^n_%44wW2p+iQ>FC3xmbrDzRK#!4{Vn1V>0+k*%#$F)Q0DDp0dC_N{My!~Wy< zzpru22elj@)Lo9}5$7^09cHhp#J++9IVoorlyBU*dDEVH>S-Gq7|`z#{UnFu8VEm1 zWe@kNd83@WHdLfSd6*{RrAwFW^y$;83i{`{cNiEL&`wSA&vUzc`I=q+_^K_;&&m)Y zOZ|}}$E>Y=k6pfcMbt)|M-BO&=yPcJ5)M<@$|i&yY=jsA-Y$kr{6CIZYl2kbq`ue# z$4v69^o(ivrWkD2SG*J)$-|?u#5O>h_Gl;Wgm|Pea{e4ExvS(Ws-ZGrNbfk zyUc^bL(sH>)K_tOAcTuU?lcq`+HrRr0f>G9Bw}FuDF-rBj@w*YFWDP!ykURvhku~| zU%z%k5DL1YEYiAsUU)A+ZHEp%V*S0nqQYT8 z^Sl)d`LH87C+>As4kG1&DilOIl-*(=7zR5Oi}>uBvw@HxW%2M0jgPBRgfU?tF*f8z zlGs7G#^RrwpAlEoMiJ7kX1j6yx^?aC7FHj43n7p~B6<#m8oPPzx-~VqTp0*sDo!jk z8k8>KJf-&$xp5(HBV%J~gb{KTVMZ^ZX4?`v&VkXrSaie5nLYpf3-;abe$W2HmyTO2 z+9qr3PFK3c0CvVEX&yRsrAJ1_bPY_aMdQIxs8}(&dilady@tgF?1Nqb6*O{-5~bfD zqx>LHof}6aaW?B69JK%S;~(2!|J7fmevd(#UB7)v-z zVG!~b8eIjql!CgjJ^&25=6of@(ZbF;nVbj$LC&_cXd;iHqo}c%?zBhPPy`tY`Vn>Q zBT8D*%bq;D4tm(R#c)}fgDj9vWj zg1h4o=SF}Z?vfk2usEkL>)x-9bCFUKbf?x#)pG zyLsb=ojiFec@Pv7NP6#2OxUIKAG!9Anpin&YHqdeefySWb$Pi7!{Q z^$<*k7#lkQ+$v?W6rEo-kFqc=8{eqc)|em+hDb>JIcLTm2;&dO)*J*cK}~zX*~*2rG1t~Nvuh`9NHv(0shYRfNv2vD{ z9E+21Zp`z-D(&67&xLVvgoc!8hwcCW`W5@x&wge%?%viSC1i1JmKybc&Ye4FKmDg) z*c<=)mL?us8X9Fp)gj2{K7;L$g9S*C6mywRSy!T8#{gWtlXMW;LU54`LWMjceXOOh zcdVtzYMCs^hp5_aQUnm%m1Nt)rKpJfMH~@b=U znO~6P7-c;UAFXuC(V6L)MA8>`%?<`8OYWVDf^I(L-jYI~zS|?37^J$wra}XV@+^4? zaW0{WDj%vyz%&oRwzJPuu54@d9hKj4tSqnEuI4s1`Wot+?a%(}f3t%J4yZCVIM{FZ zh6nA*qfa_lY%q>AbUm3S7d7gJt8NtvN<4>0$L!phGYUD#vjmxmVZ?e-{UA03PYg>c z-GWS6uu?3-Iqo}ULLt}czN09=9#TH_rX~$PS^9~>C)9e|#{{N*q0?|=CBA}gR=%kz+ica1Y15_aKn?)LTCZ(n;|Msn!DLFI@;;)LELgA;KYPT``^((=u+VjP zgHoxQp0aD#;P;x*YtcvN>#eumwSW9SKeb=|>YwG^NT6AeHHBK=fXax1F6v&vVBSA; z&wl;NZ>)V!r#Q;Ub;O9SFm|&tRW%{B7GO|*jbE{Zo%BDl5p7*T^@`YS*(EYud)x~J zvBc`eRe`9)Qft~!qVP>kzHZZo5$|_gk8CpOBVB3In8v3}+8=gQPiW?o3k?+nl^w%? z3y9aK6%vn#mr)5&L=N6WQiH7Evm*|x2n#EaAaJM}vTIkO1IR9n{IpJ_wqWJub!%*d zIw&WSFmjJ5DwD2+02d%Ud-BK;b$B^PVqg+(&I@8|nsp@fk3RZ{P_i_>sKEU0SD)8) zc}>1z2=Sr?QpKvSJ3E!AxArxWmkE6kLX6n97fvRq zw1%*PT)@eiJx0X<*%{XZKA-o?6-a{Xf-t6+lOZgKhZX>O+%B1`SbTkYOZpO*)2 zX=_)(!6gwgDg$Ry+)W~l2-+oF_q*?ZpoSmUK^~M%jHAM=vXeG^+Bo74AA2GBu&}w_ zs9Q*Q%J-N%XoHZ^u2;lxLS;~xyo3)e$Udi-|I_E--zytdEMHd}NZh#6A=ek_F{(Gr zyn7D!2JC*iFb%eVdD%MjPKZOu!~$mlcXV`aDwV~>PhJ86oEkvE#jdYF3O8lloqKij z#C@A9*s=u%3V3ee&{|*990y3=hL%>pfsrp2d=L*am$R9vNyX>`gE^s85A_^U?g5Sl z=8UdA?V>Zn*q(p>D*;v#;LhqfZisv$xi(!uJ-V3%AVcNqXI7|P~^TZ03`syyB4y{r3=S3**nUAs{523e(< z8Ak$~TlUP^Gdg#5&Aa#M_fGizu$*J_kv5K`cag7*(AE{aOCCjp*p%XEHP;bCoXBOI z{#<#1)W^pjM(lO?Re25(OYD`lMHsBE_7ME&K@)(V;n+TcNS(7{da(A?r^AD#uOU=q z@CdPstwmj2Q`keez);M@#F*`4&_%UyWRmAxgAW)vE>Q39dg1KcRHfEhcR49^#xxv7 z;+HEoS`9qD1sZ>#Nk`R-(GG$XaMtliVrIlhEUVE@a8?4E6|!2S7|8Lbj@gl;M|B>h zjdlCV3ty4ZF)u>WoY8agTtItnsKE~m4BDlO016#C!{Xrc$rmQZ##O%6gDm6)vjEBs zJKvRySFEw2SycSYO8CRg>RjkRGou3N&KX9^Hov%Ny+Z@`t6#rtcY6B*{Cl^&WjfSN zF94#f_&*jav)sRV^)-19;4GT*2s-jf7hA7zl(g1X2$?vd=qLpMJQh6;dL9z41RW7L z1`2WVlL}tWnMGnB1J9YOi89q;!BUJMOlPIDv#hSEup@Hl03dY_5IkH$=EjCq!M@S7 z6t=B;in7XSJdE8YstQD?$wQB-P%t0yEzT?J;51@u!YeMa;{M#Cy&B`YKVcYyKnx~{ z`Mc=IOghwb#7q)5egdJ9eUjkdD5EHVXpj~!F!L$Brczx{28+n4Fi78FM>uPtdI1Iz@T zq2?tCeN}$kJH*oWU2+xEu1w20N=4#}I2TMOCLOwL?1K=M|p zk=OKY{zcZNqH$DjI-lgo>%}RkfS4B983ScXyivM{jf~aS)Y$%g`}IL{^K({S@%$kw zgA#r%3%%(q=)w}EN{OI1VbJU7>Jp2im-$HN0+>gye_e6Gf3rsuq1q0quN1|bO+mIa%*JKV49 zW=f8P{KlQT+Wi?rpM1!Y7a0O8Bz!hYSB5AxQUO*Mj~qNA>ucb zoSoaYRo6%3w1S@XK8?TUW_qUd&*Q(-Aymve;z6FOHIU-XjT^C^Venuo1gI%loh!LF z{34CxvmE)fMa3ZG0zc=6c+&(MspJ*0xbS&rbgDVTKJ7qUUREvb?%JgJR{a6` z9v}nsOn@B3Nj%^T@rEmAT#X_m$-wrem>L6&Jp+z-1IihoD=SLOTk0EZVtm}{YhjPi zI}4y5l+&CNN6*d82vYIQub;LWD1$V53Ch<2c*_ebNy4g7qH~XiwLbdrg2WwP{Jn1~ z-X7?^BXYW!XNk3td5EwC7Z>NGf2a6CR=rFls?ZmRU&sy$O$ z{w}%a512pK-=|OxVpw^IC(#-Xo0Y10?t;Di<{ferlf3t_-G{PBh^1AA-0-GRoXDeO zEY;Rr*6Q>19;yX2W_9A}?Cn1Nl|Z`*9T0xcdA#1)u_r;t&IRa3$L`&lFPWO2v~Rxn z4TT5_jq1GNBxPN7oBgwR{o%uhlrNn>e?ilS&pr1QF`0k{W~5k+&IiC1B;>M8r=Nbv znppmL&#B3Y1P``Zbkrd?{X>JcRxaBI=gwMhf1fqLB%jO5E4H__SuMFx7<3Acvii|( zdp=Q$Irf$`2a_)dVVgD#yoILYY6=P|lL4hzu}b28f4_98wR(YNb`TP+mXWLeeF6qe zCCRY~=n{6DCGB8hET@ceYn7?${|U%6ZTyvUNE^f_Nz!Ag5&x|qR!MT`O2!!l!5YLa zxU%_%Fp3!=@Ec`YTp&K5@un7Rc}>lFy}W@HrTi1StS(UR6%F7el`gv&zku=e&{GUnNZS*vY8Uk=vAoJzc2-QaT}#M`F^A7AZzx~%8$ zqw<2cZ{AWB4TBPSMVen*0Z4E4##w#c&XMMo>E$l zP;;hzvkGOTXC6-|pA!uZVFBS73w(5P(z?34d=M%ypF`1!_ZuC(m*`~(vQq0ie3bbG zN<+@`@uf?;PRhSL)GJpm*zm}ZSHxDSq{GOj`rv=H_#$QUge786s}_Z}iOP+v_hRQ2+n{07*qoM6N<$g4msuO#lD@ literal 0 HcmV?d00001 diff --git a/database/__init__.py b/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/database/db.py b/database/db.py new file mode 100644 index 0000000..c6a69ff --- /dev/null +++ b/database/db.py @@ -0,0 +1,70 @@ +import aiosqlite +import os + +DB_PATH = os.getenv("DB_PATH", "data/chat.db") + + +async def init_db(): + os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) + async with aiosqlite.connect(DB_PATH) as db: + await db.executescript(""" + CREATE TABLE IF NOT EXISTS sessions ( + session_id TEXT PRIMARY KEY, + persona_id TEXT DEFAULT 'default', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + title TEXT DEFAULT 'Новый чат' + ); + + CREATE TABLE IF NOT EXISTS messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT NOT NULL, + role TEXT NOT NULL, + content TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (session_id) REFERENCES sessions(session_id) + ); + + CREATE INDEX IF NOT EXISTS idx_messages_session + ON messages(session_id); + + CREATE TABLE IF NOT EXISTS personas ( + persona_id TEXT PRIMARY KEY, + name TEXT NOT NULL, + emoji TEXT DEFAULT '🤖', + description TEXT DEFAULT '', + prompt TEXT NOT NULL, + custom INTEGER DEFAULT 1, + sd_enabled INTEGER DEFAULT 0, + lora_name TEXT DEFAULT '', + lora_weight REAL DEFAULT 0.8, + appearance_tags TEXT DEFAULT '' + ); + + CREATE TABLE IF NOT EXISTS characters ( + card_id TEXT PRIMARY KEY, + name TEXT NOT NULL, + description TEXT DEFAULT '', + personality TEXT DEFAULT '', + scenario TEXT DEFAULT '', + first_mes TEXT DEFAULT '', + mes_example TEXT DEFAULT '', + raw_json TEXT NOT NULL, + lora_name TEXT DEFAULT '', + lora_weight REAL DEFAULT 0.8, + appearance_tags TEXT DEFAULT '', + lorebook_json TEXT DEFAULT '[]', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + """) + await _migrate_messages_columns(db) + await db.commit() + + +async def _migrate_messages_columns(db): + async with db.execute("PRAGMA table_info(messages)") as cur: + cols = {row[1] for row in await cur.fetchall()} + if "image_prompt" not in cols: + await db.execute("ALTER TABLE messages ADD COLUMN image_prompt TEXT") + if "image_path" not in cols: + await db.execute("ALTER TABLE messages ADD COLUMN image_path TEXT") diff --git a/libretranslate/docker-compose.yml b/libretranslate/docker-compose.yml new file mode 100644 index 0000000..57cd110 --- /dev/null +++ b/libretranslate/docker-compose.yml @@ -0,0 +1,13 @@ +services: + libretranslate: + image: libretranslate/libretranslate:latest + ports: + - "5100:5000" + environment: + - LT_LOAD_ONLY=en,ru,ja,zh,ko + volumes: + - lt-data:/home/libretranslate/.local + restart: unless-stopped + +volumes: + lt-data: diff --git a/main.py b/main.py new file mode 100644 index 0000000..802a95a --- /dev/null +++ b/main.py @@ -0,0 +1,39 @@ +import logging +from contextlib import asynccontextmanager +from fastapi import FastAPI +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse +from routers import chat, personas, sessions, characters, images, translate +from database.db import init_db +from services.persona_seed import seed_default_personas + +logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s") + + +@asynccontextmanager +async def lifespan(app: FastAPI): + await init_db() + await seed_default_personas() + yield + + +app = FastAPI(title="AI Chat Bot", lifespan=lifespan) + +app.include_router(chat.router) +app.include_router(personas.router) +app.include_router(sessions.router) +app.include_router(characters.router) +app.include_router(images.router) +app.include_router(translate.router) + +app.mount("/static", StaticFiles(directory="static"), name="static") + + +@app.get("/") +async def root(): + return FileResponse("static/index.html") + + +@app.get("/health") +async def health(): + return {"status": "ok"} diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/schemas.py b/models/schemas.py new file mode 100644 index 0000000..85066c5 --- /dev/null +++ b/models/schemas.py @@ -0,0 +1,35 @@ +from pydantic import BaseModel +from typing import Optional + +class ChatRequest(BaseModel): + message: str + session_id: str + persona_id: Optional[str] = "default" + +class ChatResponse(BaseModel): + reply: str + session_id: str + image_prompt: Optional[str] = None + +class PersonaCreate(BaseModel): + persona_id: str + name: str + emoji: str = "🤖" + description: str = "" + prompt: str + sd_enabled: bool = False + lora_name: str = "" + lora_weight: float = 0.8 + appearance_tags: str = "" + +class PersonaResponse(BaseModel): + persona_id: str + name: str + emoji: str + description: str + prompt: str + custom: bool = False + sd_enabled: bool = False + lora_name: str = "" + lora_weight: float = 0.8 + appearance_tags: str = "" diff --git a/pull.sh b/pull.sh new file mode 100644 index 0000000..8fa0078 --- /dev/null +++ b/pull.sh @@ -0,0 +1,5 @@ +#!/bin/bash +rsync -avz -e "ssh -p 22022" --exclude='__pycache__' --exclude='*.pyc' --exclude='data/' \ + grigo@grigowashere.ru:/home/grigo/to_services/aiChatBot/ \ + /mnt/t/sources/aiChatBot/ +echo "✅ Скачано!" \ No newline at end of file diff --git a/push.sh b/push.sh new file mode 100644 index 0000000..ae43c6f --- /dev/null +++ b/push.sh @@ -0,0 +1,5 @@ +#!/bin/bash +rsync -avz -e "ssh -p 22022" --exclude='__pycache__' --exclude='*.pyc' --exclude='data/' \ + /mnt/t/sources/aiChatBot/ \ + grigo@grigowashere.ru:/home/grigo/to_services/aiChatBot/ +echo "✅ Залито на сервер!" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..37f6db5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,182 @@ +acme==4.0.0 +aiofiles==25.1.0 +aiosqlite==0.22.1 +annotated-types==0.7.0 +anyio==4.11.0 +argcomplete==3.6.3 +attrs==25.4.0 +autocommand==2.2.2 +Automat==25.4.16 +babel==2.17.0 +bcc==0.35.0 +bcrypt==5.0.0 +beautifulsoup4==4.14.3 +blinker==1.9.0 +boto3==1.40.72 +botocore==1.40.72 +Brotli==1.2.0 +certbot==4.0.0 +certbot-nginx==4.0.0 +certifi==2026.1.4 +chardet==5.2.0 +click==8.1.8 +command-not-found==0.3 +ConfigArgParse==1.7 +configobj==5.0.9 +constantly==23.10.4 +contourpy==1.3.3 +cryptography==46.0.5 +cssselect==1.4.0 +cycler==0.12.1 +dbus-python==1.4.0 +defusedxml==0.7.1 +distro==1.9.0 +distro-info==1.15 +dnspython==2.8.0 +docker==7.1.0 +email_validator==2.2.0 +fastapi==0.118.0 +fonttools==4.61.1 +ghp-import==2.1.0 +Glances==4.5.4 +h11==0.16.0 +html5lib-modern==1.2 +httpcore==1.0.9 +httplib2==0.22.0 +httptools==0.8.0 +httpx==0.28.1 +hyperlink==21.0.0 +idna==3.11 +incremental==24.7.2 +inflect==7.5.0 +influxdb==5.3.2 +iniconfig==2.1.0 +itsdangerous==2.2.0 +jaraco.context==6.0.1 +jaraco.functools==4.1.0 +jaraco.text==4.0.0 +Jinja2==3.1.6 +jmespath==1.0.1 +joblib==1.5.2 +josepy==2.2.0 +jsonpatch==1.32 +jsonpointer==2.4 +jsonschema==4.19.2 +jsonschema-specifications==2023.12.1 +kiwisolver==1.4.10rc0 +launchpadlib==2.1.0 +lazr.restfulclient==0.14.6 +lazr.uri==1.0.6 +libpass==1.9.3 +libvirt-python==12.0.0 +linkify-it-py==2.0.3 +livereload==2.7.1 +lunr==0.8.0 +lxml==6.0.2 +lz4==4.4.5+dfsg +Markdown==3.10.2 +markdown-it-py==3.0.0 +MarkupSafe==3.0.3 +matplotlib==3.10.7+dfsg1 +mdurl==0.1.2 +mergedeep==1.3.4 +mkdocs==1.6.1 +mkdocs-get-deps==0.2.0 +more-itertools==10.8.0 +mpmath==1.3.0 +msgpack==1.1.2 +munkres==1.1.4 +mutagen==1.47.0 +netaddr==1.3.0 +netifaces==0.11.0 +nltk==3.9.2 +numpy==2.3.5 +oauthlib==3.3.1 +olefile==0.47 +orjson==3.11.5 +packaging==26.0 +parsedatetime==2.6 +pathspec==1.0.4 +pexpect==4.9.0 +pillow==12.1.1 +pipx==1.8.0 +platformdirs==4.9.4 +pluggy==1.6.0 +psutil==7.2.2 +psycopg2==2.9.11 +ptyprocess==0.7.0 +pyasn1==0.6.3 +pyasn1_modules==0.4.1 +pyasyncore==1.0.2 +pydantic==2.12.5 +pydantic_core==2.41.5 +pyelftools==0.32 +Pygments==2.19.2 +PyGObject==3.56.2 +PyHamcrest==2.1.0 +pyicu==2.16.1 +pyinotify==0.9.6 +pyinstrument==5.1.2 +PyJWT==2.10.1 +pyOpenSSL==25.3.0 +pyparsing==3.3.2 +pyRFC3339==2.0.1 +pyserial==3.5 +pysnmp==7.1.21 +pystache==0.6.8 +pytest==9.0.2 +python-apt==3.1.0+ubuntu1 +python-dateutil==2.9.0 +python-debian==1.0.1+ubuntu2 +python-dotenv==1.2.2 +python-magic==0.4.27 +python-multipart==0.0.20 +pytz==2025.2 +PyYAML==6.0.3 +pyyaml_env_tag==1.1 +referencing==0.36.2 +regex==2025.9.18 +requests==2.32.5 +rich==13.9.4 +rpds-py==0.27.1 +s3transfer==0.14.0 +screen-resolution-extra==0.0.0 +service-identity==24.2.0 +setuptools==78.1.1 +shtab==1.8.0 +six==1.17.0 +sniffio==1.3.1 +sos==4.10.2 +soupsieve==2.8.3 +speedtest-cli==2.1.3 +ssh-import-id==5.11 +starlette==0.48.0 +sympy==1.14.0 +systemd-python==235 +tornado==6.5.4 +tqdm==4.67.3 +Twisted==25.5.0 +typeguard==4.4.4 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +ubuntu-drivers-common==0.0.0 +ubuntu-pro-client==8001 +uc-micro-py==1.0.3 +ufoLib2==0.18.1 +unattended-upgrades==0.1 +unicodedata2==16.0.0 +urllib3==2.6.3 +userpath==1.9.2 +uvicorn==0.38.0 +uvloop==0.22.1 +wadllib==2.0.0 +watchdog==6.0.0 +watchfiles==1.2.0 +webencodings==0.5.1 +websockets==16.0 +wheel==0.46.3 +wsproto==1.3.2 +xkit==0.0.0 +zipp==3.23.0 +zope.interface==8.2 +zopfli==0.4.1 diff --git a/routers/__init__.py b/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/routers/characters.py b/routers/characters.py new file mode 100644 index 0000000..65a37eb --- /dev/null +++ b/routers/characters.py @@ -0,0 +1,90 @@ +from fastapi import APIRouter, File, Form, HTTPException, UploadFile +from pydantic import BaseModel +from typing import Optional + +from services.character_card import list_characters, get_character, import_card_file, update_character, update_appearance_tags + +router = APIRouter(prefix="/characters", tags=["characters"]) + + +class CardPatch(BaseModel): + name: Optional[str] = None + description: Optional[str] = None + personality: Optional[str] = None + scenario: Optional[str] = None + first_mes: Optional[str] = None + mes_example: Optional[str] = None + appearance_tags: Optional[str] = None + lora_name: Optional[str] = None + lora_weight: Optional[float] = None + + +@router.get("/") +async def list_all(): + return await list_characters() + + +@router.get("/{card_id}") +async def get_one(card_id: str): + card = await get_character(card_id) + if not card: + raise HTTPException(status_code=404, detail="Карточка не найдена") + return card + + +@router.patch("/{card_id}") +async def patch_card(card_id: str, body: CardPatch): + card = await get_character(card_id) + if not card: + raise HTTPException(status_code=404, detail="Карточка не найдена") + fields = {k: v for k, v in body.model_dump().items() if v is not None} + await update_character(card_id, fields) + # sync appearance_tags and lora to persona + from services.personas import update_persona_appearance + if "appearance_tags" in fields: + await update_persona_appearance(f"card_{card_id}", fields["appearance_tags"]) + if {"lora_name", "lora_weight"} & fields.keys(): + from services.personas import update_persona_lora + await update_persona_lora(f"card_{card_id}", fields.get("lora_name"), fields.get("lora_weight")) + # rebuild system prompt if character fields changed + char_fields = {"name", "description", "personality", "scenario", "first_mes", "mes_example"} + if char_fields & fields.keys(): + updated = await get_character(card_id) + from services.character_card import build_system_prompt + from services.personas import update_persona_prompt + await update_persona_prompt(f"card_{card_id}", build_system_prompt(updated)) + return await get_character(card_id) + + +@router.post("/import") +async def import_card( + file: UploadFile = File(...), + lora_name: str = Form(""), + lora_weight: float = Form(0.8), +): + content = await file.read() + try: + card = await import_card_file( + content, + file.filename or "card.json", + lora_name=lora_name, + lora_weight=lora_weight, + ) + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + return { + "status": "imported", + "card_id": card["card_id"], + "persona_id": f"card_{card['card_id']}", + "name": card["name"], + } + + +@router.delete("/{card_id}") +async def remove_card(card_id: str): + from services.personas import delete_persona + + if not await delete_persona(f"card_{card_id}"): + raise HTTPException(status_code=404, detail="Карточка не найдена") + return {"status": "deleted", "card_id": card_id} + diff --git a/routers/chat.py b/routers/chat.py new file mode 100644 index 0000000..3a28984 --- /dev/null +++ b/routers/chat.py @@ -0,0 +1,199 @@ +import json +import os + +import aiosqlite +from fastapi import APIRouter +from fastapi.responses import StreamingResponse + +from database.db import DB_PATH +from models.schemas import ChatRequest, ChatResponse +from services.llm import send_message, stream_message +from services.memory import ( + get_history, + add_message, + clear_history, + get_or_create_session, + update_session_title, + get_message_count, + get_last_assistant_message_id, + update_message_image, +) +from services.personas import get_persona +from services.sd_prompt import ( + generate_sd_prompt, + strip_image_prompt_tag, + extract_image_prompt_tag, +) +from services.lorebook import get_lorebook_context +from services.character_card import get_character +from services import sdbackend as sd_service + +router = APIRouter(prefix="/chat", tags=["chat"]) + +DEFAULT_PROMPT = "Ты — полезный AI ассистент. Отвечай чётко и по делу." +SD_AUTO_GENERATE = os.getenv("SD_AUTO_GENERATE", "false").lower() in ("1", "true", "yes") + + +async def get_system_prompt(persona_id: str, history: list, user_message: str = "") -> str: + persona = await get_persona(persona_id) + if not persona: + return DEFAULT_PROMPT + + prompt = persona["prompt"] + + if persona_id.startswith("card_"): + card_id = persona_id[5:] + card = await get_character(card_id) + if card: + # match lorebook against recent context + current message + recent = [m for m in history if m["role"] in ("user", "assistant")][-5:] + context = recent + [{"role": "user", "content": user_message}] + lore = get_lorebook_context(card.get("lorebook_json", "[]"), context) + if lore: + prompt = prompt + "\n\n" + lore + + return prompt + + +@router.get("/history/{session_id}") +async def get_chat_history(session_id: str): + return await get_history(session_id) + + +@router.post("/init") +async def init_chat(request: ChatRequest): + """Called when opening a new chat. Seeds system prompt and first_mes if card persona.""" + persona_id = request.persona_id or "default" + await get_or_create_session(request.session_id, persona_id) + history = await get_history(request.session_id) + if history: + return {"first_mes": None} # already initialized + + system_prompt = await get_system_prompt(persona_id, [], "") + await add_message(request.session_id, "system", system_prompt) + + first_mes = None + if persona_id.startswith("card_"): + card = await get_character(persona_id[5:]) + if card and card.get("first_mes"): + first_mes = card["first_mes"] + await add_message(request.session_id, "assistant", first_mes) + + return {"first_mes": first_mes} + + +@router.post("/stream") +async def chat_stream(request: ChatRequest): + persona_id = request.persona_id or "default" + + await get_or_create_session(request.session_id, persona_id) + + history = await get_history(request.session_id) + system_prompt = await get_system_prompt(persona_id, history, request.message) + + if not history: + await add_message(request.session_id, "system", system_prompt) + elif history[0]["role"] == "system" and history[0]["content"] != system_prompt: + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """UPDATE messages SET content = ? + WHERE session_id = ? AND role = 'system' + AND id = (SELECT MIN(id) FROM messages WHERE session_id = ?)""", + (system_prompt, request.session_id, request.session_id), + ) + await db.commit() + + await add_message(request.session_id, "user", request.message) + messages = await get_history(request.session_id) + + full_reply = [] + + async def generate(): + async for chunk in stream_message( + [{"role": m["role"], "content": m["content"]} for m in messages] + ): + full_reply.append(chunk) + yield f"data: {json.dumps({'chunk': chunk})}\n\n" + + complete = "".join(full_reply) + display_text = strip_image_prompt_tag(complete) + + hist_with_reply = await get_history(request.session_id) + [ + {"role": "assistant", "content": display_text} + ] + sd_result = await generate_sd_prompt(hist_with_reply, persona_id) + prompt_str = sd_result[0] if sd_result else None + if not prompt_str: + prompt_str = extract_image_prompt_tag(complete) + + await add_message( + request.session_id, + "assistant", + display_text or complete, + image_prompt=prompt_str, + ) + + count = await get_message_count(request.session_id) + if count == 2: + title = request.message[:40] + ("…" if len(request.message) > 40 else "") + await update_session_title(request.session_id, title) + + image_path = None + image_error = None + if prompt_str and SD_AUTO_GENERATE: + rel, err = await sd_service.generate_from_full_prompt(prompt_str) + if rel: + image_path = rel + msg_id = await get_last_assistant_message_id(request.session_id) + if msg_id: + await update_message_image(msg_id, rel) + else: + image_error = err + + yield f"data: {json.dumps({ + 'done': True, + 'image_prompt': prompt_str, + 'image_path': f'/static/{image_path}' if image_path else None, + 'image_error': image_error, + })}\n\n" + + return StreamingResponse( + generate(), + media_type="text/event-stream", + headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"}, + ) + + +@router.post("/", response_model=ChatResponse) +async def chat(request: ChatRequest): + persona_id = request.persona_id or "default" + await get_or_create_session(request.session_id, persona_id) + + history = await get_history(request.session_id) + system_prompt = await get_system_prompt(persona_id, history, request.message) + + if not history: + await add_message(request.session_id, "system", system_prompt) + + await add_message(request.session_id, "user", request.message) + messages = await get_history(request.session_id) + reply = await send_message( + [{"role": m["role"], "content": m["content"]} for m in messages] + ) + display = strip_image_prompt_tag(reply) + prompt_tuple = await generate_sd_prompt(messages, persona_id) + prompt_str = prompt_tuple[0] if prompt_tuple else extract_image_prompt_tag(reply) + + await add_message(request.session_id, "assistant", display, image_prompt=prompt_str) + + return ChatResponse( + reply=display, + session_id=request.session_id, + image_prompt=prompt_str, + ) + + +@router.delete("/{session_id}") +async def clear_chat(session_id: str): + await clear_history(session_id) + return {"status": "cleared", "session_id": session_id} diff --git a/routers/images.py b/routers/images.py new file mode 100644 index 0000000..dde08f2 --- /dev/null +++ b/routers/images.py @@ -0,0 +1,34 @@ +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel + +from services import sdbackend as sd_service +from services.memory import get_last_assistant_message_id, update_message_image + +router = APIRouter(prefix="/images", tags=["images"]) + + +class GenerateRequest(BaseModel): + session_id: str + prompt: str + + +@router.get("/health") +async def sd_health(): + ok = await sd_service.check_sd() + return {"sd_available": ok, "url": sd_service.SD_BASE_URL} + + +@router.post("/generate") +async def generate_image(req: GenerateRequest): + if not req.prompt.strip(): + raise HTTPException(status_code=400, detail="Пустой промпт") + + rel, err = await sd_service.generate_from_full_prompt(req.prompt) + if not rel: + raise HTTPException(status_code=502, detail=err or "SD backend недоступен") + + msg_id = await get_last_assistant_message_id(req.session_id) + if msg_id: + await update_message_image(msg_id, rel) + + return {"image_path": f"/static/{rel}", "status": "ok"} diff --git a/routers/personas.py b/routers/personas.py new file mode 100644 index 0000000..4fccb94 --- /dev/null +++ b/routers/personas.py @@ -0,0 +1,42 @@ +from fastapi import APIRouter, HTTPException +from models.schemas import PersonaCreate +from services.personas import get_all_personas, get_persona, create_persona, delete_persona + +router = APIRouter(prefix="/personas", tags=["personas"]) + + +@router.get("/") +async def list_personas(): + personas = await get_all_personas() + return [{"persona_id": pid, **data} for pid, data in personas.items()] + + +@router.get("/{persona_id}") +async def get_one_persona(persona_id: str): + persona = await get_persona(persona_id) + if not persona: + raise HTTPException(status_code=404, detail="Персонаж не найден") + return {"persona_id": persona_id, **persona} + + +@router.post("/") +async def create_new_persona(data: PersonaCreate): + persona = await create_persona( + persona_id=data.persona_id, + name=data.name, + emoji=data.emoji, + description=data.description, + prompt=data.prompt, + sd_enabled=data.sd_enabled, + lora_name=data.lora_name, + lora_weight=data.lora_weight, + appearance_tags=data.appearance_tags, + ) + return {"persona_id": data.persona_id, **persona} + + +@router.delete("/{persona_id}") +async def remove_persona(persona_id: str): + if not await delete_persona(persona_id): + raise HTTPException(status_code=400, detail="Нельзя удалить встроенного персонажа") + return {"status": "deleted", "persona_id": persona_id} diff --git a/routers/sessions.py b/routers/sessions.py new file mode 100644 index 0000000..15b47b5 --- /dev/null +++ b/routers/sessions.py @@ -0,0 +1,48 @@ +from fastapi import APIRouter, HTTPException +from services.memory import ( + get_all_sessions, + get_or_create_session, + delete_session, + update_session_title, + update_session_persona, + get_history, + get_message_count +) + +router = APIRouter(prefix="/sessions", tags=["sessions"]) + + +@router.get("/") +async def list_sessions(): + sessions = await get_all_sessions() + result = [] + for s in sessions: + count = await get_message_count(s["session_id"]) + result.append({**s, "message_count": count}) + return result + + +@router.get("/{session_id}") +async def get_session(session_id: str): + sessions = await get_all_sessions() + s = next((x for x in sessions if x["session_id"] == session_id), None) + if not s: + raise HTTPException(status_code=404, detail="Сессия не найдена") + return s + + +@router.patch("/{session_id}") +async def patch_session(session_id: str, data: dict): + # ensure session exists before patching + await get_or_create_session(session_id, data.get("persona_id", "default")) + if "title" in data: + await update_session_title(session_id, data["title"]) + if "persona_id" in data: + await update_session_persona(session_id, data["persona_id"]) + return {"status": "updated"} + + +@router.delete("/{session_id}") +async def remove_session(session_id: str): + await delete_session(session_id) + return {"status": "deleted", "session_id": session_id} diff --git a/routers/translate.py b/routers/translate.py new file mode 100644 index 0000000..84bbfd4 --- /dev/null +++ b/routers/translate.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel +from services.translate import translate_to_russian + +router = APIRouter(prefix="/translate", tags=["translate"]) + + +class TranslateRequest(BaseModel): + text: str + + +@router.post("/") +async def translate(req: TranslateRequest): + try: + result = await translate_to_russian(req.text) + return {"translated": result} + except Exception as e: + raise HTTPException(status_code=502, detail=str(e)) diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/character_card.py b/services/character_card.py new file mode 100644 index 0000000..5144aa0 --- /dev/null +++ b/services/character_card.py @@ -0,0 +1,214 @@ +import json +import base64 +import uuid + +import aiosqlite +from database.db import DB_PATH + + +def parse_card_v2(data: dict) -> dict: + inner = data.get("data", data) + if isinstance(inner, str): + inner = json.loads(inner) + + book = inner.get("character_book") or {} + entries = book.get("entries", []) + if isinstance(entries, dict): + entries = list(entries.values()) + + return { + "card_id": ( + inner.get("name", "imported").lower().replace(" ", "_")[:48] + + "_" + + uuid.uuid4().hex[:8] + ), + "name": inner.get("name", "Character"), + "description": inner.get("description", ""), + "personality": inner.get("personality", ""), + "scenario": inner.get("scenario", ""), + "first_mes": inner.get("first_mes", ""), + "mes_example": inner.get("mes_example", ""), + "appearance_tags": _extract_appearance(inner), + "lorebook_json": json.dumps(entries, ensure_ascii=False), + "raw_json": json.dumps(data if "data" in data else {"data": inner}, ensure_ascii=False), + } + + +def _extract_appearance(inner: dict) -> str: + """Extract booru-style appearance tags from character fields.""" + import re + # fall back: scan description for visual keywords, skip world-building sentences + desc = inner.get("description", "") + appearance_keywords = re.findall( + r'\b(?:' + r'\w*hair|hair\w*|\w*eyes|eye\w*|\w*skin|skin\w*' + r'|tall|short|slim|curvy|muscular|petite' + r'|ears?|tail|horns?|wings?|cloak|dress|outfit|uniform|armor' + r'|wolf\w*|cat\w*|fox\w*|elf\w*|demon\w*|angel\w*' + r'|silver|blonde|black|white|red|blue|green|purple|pink|brown|golden' + r')\b', + desc, re.IGNORECASE + ) + seen = [] + for kw in appearance_keywords: + kw_lower = kw.lower() + if kw_lower not in seen: + seen.append(kw_lower) + return ", ".join(seen[:20]) + + +def parse_png_card(file_bytes: bytes) -> dict | None: + if not file_bytes.startswith(b"\x89PNG"): + return None + idx = 8 # skip PNG file signature + while idx < len(file_bytes) - 12: + length = int.from_bytes(file_bytes[idx : idx + 4], "big") + chunk_type = file_bytes[idx + 4 : idx + 8] + chunk_data = file_bytes[idx + 8 : idx + 8 + length] + if chunk_type == b"tEXt": + try: + key, _, val = chunk_data.partition(b"\x00") + if key in (b"chara", b"ccv3"): + decoded = base64.b64decode(val).decode("utf-8") + return parse_card_v2(json.loads(decoded)) + except Exception: + pass + elif chunk_type == b"iTXt": + try: + # iTXt: keyword \x00 compression_flag \x00 compression_method \x00 language \x00 translated_keyword \x00 text + key, _, rest = chunk_data.partition(b"\x00") + if key in (b"chara", b"ccv3"): + # skip compression_flag, compression_method, language tag, translated keyword + text = rest[2:].split(b"\x00", 2)[-1].decode("utf-8") + # text may be base64 or raw JSON + try: + return parse_card_v2(json.loads(base64.b64decode(text).decode("utf-8"))) + except Exception: + return parse_card_v2(json.loads(text)) + except Exception: + pass + idx += 12 + length + return None + + +def build_system_prompt(card: dict) -> str: + parts = [ + f"You are {card['name']}. Stay in character.", + f"Description: {card['description']}", + f"Personality: {card['personality']}", + f"Scenario: {card['scenario']}", + ] + if card.get("mes_example"): + parts.append(f"Example dialogue:\n{card['mes_example']}") + parts.append("Reply only as the character. Do not add image tags.") + return "\n\n".join(p for p in parts if p.split(": ", 1)[-1].strip()) + + +async def save_character(card: dict, lora_name: str = "", lora_weight: float = 0.8) -> dict: + card_id = card["card_id"] + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """INSERT OR REPLACE INTO characters + (card_id, name, description, personality, scenario, first_mes, + mes_example, raw_json, lora_name, lora_weight, appearance_tags, lorebook_json) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", + ( + card_id, + card["name"], + card["description"], + card["personality"], + card["scenario"], + card["first_mes"], + card["mes_example"], + card["raw_json"], + lora_name, + lora_weight, + card.get("appearance_tags", ""), + card["lorebook_json"], + ), + ) + await db.commit() + return {**card, "lora_name": lora_name, "lora_weight": lora_weight} + + +async def get_character(card_id: str) -> dict | None: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT * FROM characters WHERE card_id = ?", (card_id,) + ) as cur: + row = await cur.fetchone() + return dict(row) if row else None + + +async def list_characters() -> list: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT card_id, name, description, lora_name FROM characters ORDER BY name" + ) as cur: + rows = await cur.fetchall() + return [dict(r) for r in rows] + + +async def delete_character(card_id: str) -> bool: + async with aiosqlite.connect(DB_PATH) as db: + cur = await db.execute( + "DELETE FROM characters WHERE card_id = ?", (card_id,) + ) + await db.commit() + return cur.rowcount > 0 + + +async def update_appearance_tags(card_id: str, appearance_tags: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE characters SET appearance_tags = ? WHERE card_id = ?", + (appearance_tags, card_id), + ) + await db.commit() + + +async def update_character(card_id: str, fields: dict) -> bool: + allowed = {"name", "description", "personality", "scenario", "first_mes", + "mes_example", "appearance_tags", "lora_name", "lora_weight"} + updates = {k: v for k, v in fields.items() if k in allowed} + if not updates: + return False + cols = ", ".join(f"{k} = ?" for k in updates) + async with aiosqlite.connect(DB_PATH) as db: + cur = await db.execute( + f"UPDATE characters SET {cols} WHERE card_id = ?", + (*updates.values(), card_id), + ) + await db.commit() + return cur.rowcount > 0 + + +async def import_card_file(content: bytes, filename: str, lora_name: str = "", lora_weight: float = 0.8) -> dict: + if filename.lower().endswith(".png"): + card = parse_png_card(content) + if not card: + raise ValueError("PNG does not contain character card metadata") + else: + card = parse_card_v2(json.loads(content.decode("utf-8"))) + + saved = await save_character(card, lora_name=lora_name, lora_weight=lora_weight) + + persona_id = f"card_{saved['card_id']}" + from services.personas import create_persona, get_persona + + existing = await get_persona(persona_id) + if not existing: + await create_persona( + persona_id=persona_id, + name=saved["name"], + emoji="🎭", + description=saved["description"][:80] or "Character card", + prompt=build_system_prompt(saved), + sd_enabled=True, + lora_name=lora_name, + lora_weight=lora_weight, + appearance_tags=saved.get("appearance_tags", ""), + ) + return saved diff --git a/services/llm.py b/services/llm.py new file mode 100644 index 0000000..17dfa8b --- /dev/null +++ b/services/llm.py @@ -0,0 +1,63 @@ +import httpx +import os +from dotenv import load_dotenv + +load_dotenv() + +OPENROUTER_KEY = os.getenv("ROUTER_KEY") +OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions" +MODEL = "google/gemini-2.5-flash" + +HEADERS = { + "Authorization": f"Bearer {OPENROUTER_KEY}", + "Content-Type": "application/json", + "HTTP-Referer": "http://localhost:8000", +} + +async def send_message(messages: list) -> str: + """Обычный запрос — используем для внутренних нужд""" + payload = { + "model": MODEL, + "messages": messages, + } + async with httpx.AsyncClient(timeout=60) as client: + response = await client.post( + OPENROUTER_URL, + headers=HEADERS, + json=payload + ) + response.raise_for_status() + data = response.json() + return data["choices"][0]["message"]["content"] + + +async def stream_message(messages: list): + """Стриминг — отдаём чанки по мере получения""" + payload = { + "model": MODEL, + "messages": messages, + "stream": True, + } + async with httpx.AsyncClient(timeout=60) as client: + async with client.stream( + "POST", + OPENROUTER_URL, + headers=HEADERS, + json=payload + ) as response: + response.raise_for_status() + async for line in response.aiter_lines(): + if not line.startswith("data: "): + continue + data = line[6:] # убираем "data: " + if data == "[DONE]": + break + try: + import json + chunk = json.loads(data) + delta = chunk["choices"][0]["delta"] + content = delta.get("content", "") + if content: + yield content + except Exception: + continue diff --git a/services/lorebook.py b/services/lorebook.py new file mode 100644 index 0000000..4d461b6 --- /dev/null +++ b/services/lorebook.py @@ -0,0 +1,52 @@ +import json + + +def _match_entry(entry: dict, text: str) -> bool: + keys = entry.get("keys", []) + if isinstance(keys, str): + keys = [k.strip() for k in keys.split(",") if k.strip()] + text_lower = text.lower() + for key in keys: + if key and key.lower() in text_lower: + return True + secondary = entry.get("secondary_keys", []) or entry.get("keysecondary", []) + if isinstance(secondary, str): + secondary = [k.strip() for k in secondary.split(",") if k.strip()] + for key in secondary: + if key and key.lower() in text_lower: + return True + return False + + +def get_lorebook_context(lorebook_json: str, context: str | list, max_entries: int = 5) -> str: + """Match lorebook entries against context. + context can be a string or a list of message dicts (role/content). + """ + try: + entries = json.loads(lorebook_json or "[]") + except json.JSONDecodeError: + return "" + + if isinstance(entries, dict): + entries = list(entries.values()) + + if isinstance(context, list): + text = " ".join(m.get("content", "") for m in context if m.get("role") in ("user", "assistant")) + else: + text = context + + matched = [] + for entry in entries: + if not entry.get("enabled", True): + continue + if _match_entry(entry, text): + content = entry.get("content", "").strip() + if content: + name = entry.get("name", entry.get("comment", "Lore")) + matched.append(f"[{name}]\n{content}") + + if not matched: + return "" + + block = "\n\n".join(matched[:max_entries]) + return f"--- Lorebook (relevant world info) ---\n{block}\n---" diff --git a/services/memory.py b/services/memory.py new file mode 100644 index 0000000..4668dfe --- /dev/null +++ b/services/memory.py @@ -0,0 +1,142 @@ +import aiosqlite +from database.db import DB_PATH + + +async def get_or_create_session(session_id: str, persona_id: str = "default") -> dict: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT * FROM sessions WHERE session_id = ?", (session_id,) + ) as cursor: + row = await cursor.fetchone() + + if row: + return dict(row) + + await db.execute( + "INSERT INTO sessions (session_id, persona_id) VALUES (?, ?)", + (session_id, persona_id), + ) + await db.commit() + + async with db.execute( + "SELECT * FROM sessions WHERE session_id = ?", (session_id,) + ) as cursor: + row = await cursor.fetchone() + return dict(row) + + +async def get_all_sessions() -> list: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT * FROM sessions ORDER BY updated_at DESC" + ) as cursor: + rows = await cursor.fetchall() + return [dict(r) for r in rows] + + +async def update_session_title(session_id: str, title: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE sessions SET title = ?, updated_at = CURRENT_TIMESTAMP WHERE session_id = ?", + (title, session_id), + ) + await db.commit() + + +async def update_session_persona(session_id: str, persona_id: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE sessions SET persona_id = ?, updated_at = CURRENT_TIMESTAMP WHERE session_id = ?", + (persona_id, session_id), + ) + await db.commit() + + +async def delete_session(session_id: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute("DELETE FROM messages WHERE session_id = ?", (session_id,)) + await db.execute("DELETE FROM sessions WHERE session_id = ?", (session_id,)) + await db.commit() + + +async def get_history(session_id: str) -> list: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + """SELECT role, content, image_prompt, image_path + FROM messages WHERE session_id = ? ORDER BY id""", + (session_id,), + ) as cursor: + rows = await cursor.fetchall() + return [ + { + "role": r["role"], + "content": r["content"], + "image_prompt": r["image_prompt"], + "image_path": r["image_path"], + } + for r in rows + ] + + +async def add_message( + session_id: str, + role: str, + content: str, + image_prompt: str | None = None, + image_path: str | None = None, +): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """INSERT INTO messages (session_id, role, content, image_prompt, image_path) + VALUES (?, ?, ?, ?, ?)""", + (session_id, role, content, image_prompt, image_path), + ) + await db.execute( + "UPDATE sessions SET updated_at = CURRENT_TIMESTAMP WHERE session_id = ?", + (session_id,), + ) + await db.commit() + + +async def update_message_image(message_id: int, image_path: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE messages SET image_path = ? WHERE id = ?", + (image_path, message_id), + ) + await db.commit() + + +async def get_last_assistant_message_id(session_id: str) -> int | None: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + """SELECT id FROM messages + WHERE session_id = ? AND role = 'assistant' + ORDER BY id DESC LIMIT 1""", + (session_id,), + ) as cursor: + row = await cursor.fetchone() + return row["id"] if row else None + + +async def clear_history(session_id: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "DELETE FROM messages WHERE session_id = ?", (session_id,) + ) + await db.commit() + + +async def get_message_count(session_id: str) -> int: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT COUNT(*) as cnt FROM messages WHERE session_id = ? AND role != 'system'", + (session_id,), + ) as cursor: + row = await cursor.fetchone() + return row["cnt"] diff --git a/services/persona_seed.py b/services/persona_seed.py new file mode 100644 index 0000000..975d897 --- /dev/null +++ b/services/persona_seed.py @@ -0,0 +1,26 @@ +import aiosqlite +from database.db import DB_PATH +from services.personas import DEFAULT_PERSONAS + + +async def seed_default_personas(): + async with aiosqlite.connect(DB_PATH) as db: + for pid, data in DEFAULT_PERSONAS.items(): + await db.execute( + """INSERT OR IGNORE INTO personas + (persona_id, name, emoji, description, prompt, custom, sd_enabled, + lora_name, lora_weight, appearance_tags) + VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?, ?)""", + ( + pid, + data["name"], + data["emoji"], + data["description"], + data["prompt"], + 1 if data.get("sd_enabled") else 0, + data.get("lora_name", ""), + data.get("lora_weight", 0.8), + data.get("appearance_tags", ""), + ), + ) + await db.commit() diff --git a/services/personas.py b/services/personas.py new file mode 100644 index 0000000..af92817 --- /dev/null +++ b/services/personas.py @@ -0,0 +1,168 @@ +from typing import Optional +import aiosqlite +from database.db import DB_PATH + +DEFAULT_PERSONAS = { + "default": { + "name": "AI Ассистент", + "emoji": "🤖", + "description": "Универсальный помощник", + "prompt": "Ты — полезный AI ассистент. Отвечай чётко и по делу.", + "sd_enabled": False, + }, + "rpg_master": { + "name": "Мастер RPG", + "emoji": "🧙", + "description": "Ведёт ролевые игры, создаёт атмосферу", + "prompt": """Ты — опытный Мастер ролевых игр. +Создавай живые описания, веди нарратив, реагируй на действия игрока. +Мир детальный, персонажи запоминающиеся. +Отвечай только текстом сюжета — без тегов изображений.""", + "sd_enabled": True, + }, + "villain": { + "name": "Злодей", + "emoji": "😈", + "description": "Харизматичный антагонист", + "prompt": """Ты — харизматичный злодей с грандиозными планами. +Говоришь театрально, с сарказмом и превосходством. +Никогда не выходишь из роли. Называешь собеседника 'герой' с иронией.""", + "sd_enabled": False, + }, + "scientist": { + "name": "Учёный", + "emoji": "🔬", + "description": "Объясняет сложное простыми словами", + "prompt": """Ты — увлечённый учёный. Объясняешь любые темы +через факты, аналогии и примеры. Любишь уточнять детали. +Иногда уходишь в интересные отступления.""", + "sd_enabled": False, + }, + "samurai": { + "name": "Самурай", + "emoji": "⚔️", + "description": "Мудрый воин феодальной Японии", + "prompt": """Ты — самурай феодальной Японии. +Говоришь кратко, мудро, с достоинством. +Используешь метафоры природы и войны. +Чтишь кодекс бусидо.""", + "sd_enabled": True, + "appearance_tags": "samurai armor, katana, feudal japan", + }, +} + + +def _row_to_persona(row: dict) -> dict: + return { + "name": row["name"], + "emoji": row["emoji"], + "description": row["description"], + "prompt": row["prompt"], + "custom": bool(row["custom"]), + "sd_enabled": bool(row["sd_enabled"]), + "lora_name": row["lora_name"] or "", + "lora_weight": row["lora_weight"] if row["lora_weight"] is not None else 0.8, + "appearance_tags": row["appearance_tags"] or "", + } + + +async def get_all_personas() -> dict: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute("SELECT * FROM personas ORDER BY custom ASC, persona_id ASC") as cur: + rows = await cur.fetchall() + return {r["persona_id"]: _row_to_persona(dict(r)) for r in rows} + + +async def get_persona(persona_id: str) -> Optional[dict]: + async with aiosqlite.connect(DB_PATH) as db: + db.row_factory = aiosqlite.Row + async with db.execute( + "SELECT * FROM personas WHERE persona_id = ?", (persona_id,) + ) as cur: + row = await cur.fetchone() + if not row: + return None + return _row_to_persona(dict(row)) + + +async def create_persona( + persona_id: str, + name: str, + emoji: str, + description: str, + prompt: str, + sd_enabled: bool = False, + lora_name: str = "", + lora_weight: float = 0.8, + appearance_tags: str = "", +) -> dict: + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + """INSERT INTO personas + (persona_id, name, emoji, description, prompt, custom, + sd_enabled, lora_name, lora_weight, appearance_tags) + VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?, ?)""", + ( + persona_id, name, emoji, description, prompt, + 1 if sd_enabled else 0, lora_name, lora_weight, appearance_tags, + ), + ) + await db.commit() + return { + "name": name, + "emoji": emoji, + "description": description, + "prompt": prompt, + "custom": True, + "sd_enabled": sd_enabled, + "lora_name": lora_name, + "lora_weight": lora_weight, + "appearance_tags": appearance_tags, + } + + +async def delete_persona(persona_id: str) -> bool: + async with aiosqlite.connect(DB_PATH) as db: + async with db.execute( + "SELECT custom FROM personas WHERE persona_id = ?", (persona_id,) + ) as cur: + row = await cur.fetchone() + if not row or not row[0]: + return False + await db.execute("DELETE FROM personas WHERE persona_id = ?", (persona_id,)) + await db.commit() + + if persona_id.startswith("card_"): + from services.character_card import delete_character + await delete_character(persona_id[5:]) + + return True + + +async def update_persona_appearance(persona_id: str, appearance_tags: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute( + "UPDATE personas SET appearance_tags = ? WHERE persona_id = ?", + (appearance_tags, persona_id), + ) + await db.commit() + + +async def update_persona_lora(persona_id: str, lora_name: str | None, lora_weight: float | None): + fields, vals = [], [] + if lora_name is not None: + fields.append("lora_name = ?"); vals.append(lora_name) + if lora_weight is not None: + fields.append("lora_weight = ?"); vals.append(lora_weight) + if not fields: + return + async with aiosqlite.connect(DB_PATH) as db: + await db.execute(f"UPDATE personas SET {', '.join(fields)} WHERE persona_id = ?", (*vals, persona_id)) + await db.commit() + + +async def update_persona_prompt(persona_id: str, prompt: str): + async with aiosqlite.connect(DB_PATH) as db: + await db.execute("UPDATE personas SET prompt = ? WHERE persona_id = ?", (prompt, persona_id)) + await db.commit() diff --git a/services/sd_prompt.py b/services/sd_prompt.py new file mode 100644 index 0000000..398596e --- /dev/null +++ b/services/sd_prompt.py @@ -0,0 +1,125 @@ +import json +import os +import re +from services.llm import send_message +from services.personas import get_persona + +PROMPT_BUILDER_SYSTEM = """You are a Stable Diffusion prompt engineer for anime illustration models. +Given a roleplay chat excerpt and character appearance hints, output ONLY valid JSON (no markdown): +{ + "should_generate": true, + "shot_type": "first_person_pov" | "landscape" | "third_person", + "appearance_tags": "booru-style tags for character appearance extracted from hints, e.g. 'white hair, wolf ears, wolf tail, yellow eyes'", + "action_tags": "booru-style tags for pose/action, e.g. 'sitting, smiling, looking at viewer'", + "environment_tags": "booru-style tags for location/lighting, e.g. 'indoors, kitchen, sunlight'" +} +Rules: +- ONLY use real danbooru/e621 tags. Multi-word concepts MUST be written as single tags: 'white hair' not 'white, hair'. 'wolf ears' not 'wolf, ears'. +- Do NOT include quality tags, model names, style words, 'pov', or category/metadata words. +- Do NOT invent tags. If unsure — omit. +- Keep each field to 3-6 tags.""" + + +def extract_image_prompt_tag(text: str) -> str | None: + if "[IMAGE_PROMPT:" not in text: + return None + try: + start = text.index("[IMAGE_PROMPT:") + len("[IMAGE_PROMPT:") + end = text.index("]", start) + return text[start:end].strip() + except ValueError: + return None + + +def strip_image_prompt_tag(text: str) -> str: + return re.sub(r"\[IMAGE_PROMPT:.*?\]", "", text, flags=re.DOTALL).strip() + + +PONY_CHECKPOINTS = {"ponyDiffusionV6XL_v6StartWithThisOne.safetensors"} +SD_CHECKPOINT = os.getenv("SD_CHECKPOINT", "") +PONY_NEGATIVE = "score_1, score_2, score_3, score_4, worst quality, low quality, blurry, bad anatomy, watermark, text, censored" + +def build_positive_prompt(scene: dict, persona: dict | None) -> str: + is_pony = SD_CHECKPOINT in PONY_CHECKPOINTS + quality = "score_9, score_8_up, score_7_up, source_anime, highres" if is_pony else "masterpiece, best quality, highres" + parts = [quality] + + # prefer LLM-extracted appearance over raw persona tags + appearance = scene.get("appearance_tags") or (persona or {}).get("appearance_tags", "") + if appearance: + parts.append(appearance) + + if scene.get("shot_type") == "landscape": + parts.append(scene.get("environment_tags", "")) + else: + if scene.get("shot_type") == "first_person_pov": + parts.append("pov, first-person view, looking at viewer") + parts.append(scene.get("action_tags", "")) + parts.append(scene.get("environment_tags", "")) + + lora = (persona or {}).get("lora_name", "") + weight = (persona or {}).get("lora_weight", 0.8) + if lora: + parts.append(f"") + + positive = ", ".join(p.strip() for p in parts if p and p.strip()) + seen, deduped = set(), [] + for tag in positive.split(", "): + t = tag.strip() + if t and t not in seen: + seen.add(t) + deduped.append(t) + return ", ".join(deduped) + + +async def generate_sd_prompt( + messages: list, + persona_id: str, +) -> tuple[str | None, str | None]: + persona = await get_persona(persona_id) + if not persona or not persona.get("sd_enabled"): + return None, None + + recent = [m for m in messages if m["role"] in ("user", "assistant")][-6:] + if not recent: + return None, None + + excerpt = "\n".join(f"{m['role']}: {strip_image_prompt_tag(m['content'])}" for m in recent) + + appearance = persona.get("appearance_tags", "") + # For card personas, also include description for better visual context + if persona_id.startswith("card_"): + from services.character_card import get_character + card = await get_character(persona_id[5:]) + if card and card.get("description"): + appearance = f"{appearance}\nCharacter description: {card['description'][:400]}" + + builder_messages = [ + {"role": "system", "content": PROMPT_BUILDER_SYSTEM}, + { + "role": "user", + "content": f"Persona appearance hints: {appearance}\n\nChat:\n{excerpt}", + }, + ] + + try: + raw = await send_message(builder_messages) + raw = raw.strip() + if raw.startswith("```"): + raw = re.sub(r"^```\w*\n?", "", raw) + raw = re.sub(r"\n?```$", "", raw) + scene = json.loads(raw) + except (json.JSONDecodeError, Exception): + return None, None + + + positive = build_positive_prompt(scene, persona) + is_pony = SD_CHECKPOINT in PONY_CHECKPOINTS + negative = PONY_NEGATIVE if is_pony else "low quality, blurry, bad anatomy, watermark, text" + if scene.get("shot_type") == "first_person_pov": + negative += ", third person, over the shoulder" + + full = positive + if negative: + full += f"\n\nNegative prompt: {negative}" + return full, negative diff --git a/services/sdbackend.py b/services/sdbackend.py new file mode 100644 index 0000000..fbd0691 --- /dev/null +++ b/services/sdbackend.py @@ -0,0 +1,121 @@ +import asyncio +import logging +import os +import uuid +from pathlib import Path + +import httpx +from dotenv import load_dotenv + +load_dotenv() + +logger = logging.getLogger(__name__) + +SD_BASE_URL = os.getenv("SD_BASE_URL", "http://127.0.0.1:8188").rstrip("/") +SD_STEPS = int(os.getenv("SD_STEPS", "28")) +SD_CFG = float(os.getenv("SD_CFG", "7")) +SD_SAMPLER = os.getenv("SD_SAMPLER", "euler") +SD_SCHEDULER = os.getenv("SD_SCHEDULER", "normal") +SD_CHECKPOINT = os.getenv("SD_CHECKPOINT", "NetaYumev35_pretrained_all_in_one.safetensors") +SD_DEFAULT_NEGATIVE = os.getenv( + "SD_DEFAULT_NEGATIVE", + "low quality, worst quality, blurry, bad anatomy, watermark, text", +) +IMAGES_DIR = Path(os.getenv("IMAGES_DIR", "static/images")) + + +def split_prompt_and_negative(full_prompt: str) -> tuple[str, str]: + if "\n\nNegative prompt:" in full_prompt: + pos, _, neg = full_prompt.partition("\n\nNegative prompt:") + return pos.strip(), neg.strip() + return full_prompt.strip(), SD_DEFAULT_NEGATIVE + + +def _build_workflow(positive: str, negative: str) -> dict: + """Minimal KSampler workflow for ComfyUI API.""" + return { + "4": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": SD_CHECKPOINT}}, + "5": {"class_type": "EmptyLatentImage", "inputs": {"width": 832, "height": 1216, "batch_size": 1}}, + "6": {"class_type": "CLIPTextEncode", "inputs": {"text": positive, "clip": ["4", 1]}}, + "7": {"class_type": "CLIPTextEncode", "inputs": {"text": negative, "clip": ["4", 1]}}, + "8": {"class_type": "VAEDecode", "inputs": {"samples": ["10", 0], "vae": ["4", 2]}}, + "9": {"class_type": "SaveImage", "inputs": {"filename_prefix": "chatbot", "images": ["8", 0]}}, + "10": { + "class_type": "KSampler", + "inputs": { + "model": ["4", 0], + "positive": ["6", 0], + "negative": ["7", 0], + "latent_image": ["5", 0], + "seed": int(uuid.uuid4().int % 2**32), + "steps": SD_STEPS, + "cfg": SD_CFG, + "sampler_name": SD_SAMPLER, + "scheduler": SD_SCHEDULER, + "denoise": 1.0, + }, + }, + } + + +async def check_sd() -> bool: + try: + async with httpx.AsyncClient(timeout=5) as client: + r = await client.get(f"{SD_BASE_URL}/system_stats") + return r.status_code == 200 + except Exception: + return False + + +async def txt2img(prompt: str, negative_prompt: str | None = None) -> tuple[bytes, str]: + neg = negative_prompt or SD_DEFAULT_NEGATIVE + workflow = _build_workflow(prompt, neg) + client_id = uuid.uuid4().hex + + logger.info("ComfyUI request → %s prompt: %.120s", SD_BASE_URL, prompt) + async with httpx.AsyncClient(timeout=300) as client: + # queue the prompt + resp = await client.post( + f"{SD_BASE_URL}/prompt", + json={"prompt": workflow, "client_id": client_id}, + ) + resp.raise_for_status() + prompt_id = resp.json()["prompt_id"] + logger.info("ComfyUI queued prompt_id=%s", prompt_id) + + # poll until done + for _ in range(300): + await asyncio.sleep(1) + hist = await client.get(f"{SD_BASE_URL}/history/{prompt_id}") + data = hist.json() + if prompt_id in data: + outputs = data[prompt_id]["outputs"] + # find first image output + for node_output in outputs.values(): + if "images" in node_output: + img_info = node_output["images"][0] + img_resp = await client.get( + f"{SD_BASE_URL}/view", + params={"filename": img_info["filename"], "subfolder": img_info.get("subfolder", ""), "type": img_info.get("type", "output")}, + ) + img_resp.raise_for_status() + image_bytes = img_resp.content + + IMAGES_DIR.mkdir(parents=True, exist_ok=True) + filename = f"{uuid.uuid4().hex}.png" + (IMAGES_DIR / filename).write_bytes(image_bytes) + logger.info("ComfyUI done → saved %s", filename) + return image_bytes, f"images/{filename}" + break + + raise RuntimeError("ComfyUI generation timed out or produced no output") + + +async def generate_from_full_prompt(full_prompt: str) -> tuple[str | None, str | None]: + positive, negative = split_prompt_and_negative(full_prompt) + try: + _, rel_path = await txt2img(positive, negative) + return rel_path, None + except Exception as e: + logger.error("ComfyUI error: %s", e) + return None, str(e) diff --git a/services/translate.py b/services/translate.py new file mode 100644 index 0000000..123d5fa --- /dev/null +++ b/services/translate.py @@ -0,0 +1,17 @@ +import os +import httpx +from dotenv import load_dotenv + +load_dotenv() + +LIBRETRANSLATE_URL = os.getenv("LIBRETRANSLATE_URL", "http://192.168.1.109:5100") + + +async def translate_to_russian(text: str) -> str: + async with httpx.AsyncClient(timeout=30) as client: + r = await client.post( + f"{LIBRETRANSLATE_URL}/translate", + json={"q": text, "source": "auto", "target": "ru", "format": "text"}, + ) + r.raise_for_status() + return r.json()["translatedText"] diff --git a/static/css/app.css b/static/css/app.css new file mode 100644 index 0000000..2fc4c95 --- /dev/null +++ b/static/css/app.css @@ -0,0 +1,306 @@ +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + background: #1a1a2e; + color: #e0e0e0; + font-family: 'Segoe UI', sans-serif; + height: 100vh; + display: flex; + flex-direction: column; + overflow: hidden; +} + +header { + width: 100%; + padding: 12px 20px; + background: #16213e; + border-bottom: 1px solid #0f3460; + display: flex; + align-items: center; + gap: 12px; + flex-shrink: 0; + z-index: 10; +} + +header h1 { font-size: 1.1rem; color: #e94560; } + +#sidebarToggle { + background: none; + border: 1px solid #0f3460; + border-radius: 8px; + color: #888; + padding: 4px 10px; + cursor: pointer; + font-size: 1rem; + transition: all 0.2s; +} +#sidebarToggle:hover { border-color: #e94560; color: #e94560; } + +.header-title { + flex: 1; + font-size: 0.9rem; + color: #ccc; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.app-body { display: flex; flex: 1; overflow: hidden; } + +.sidebar { + width: 260px; + background: #16213e; + border-right: 1px solid #0f3460; + display: flex; + flex-direction: column; + flex-shrink: 0; + transition: width 0.25s ease, opacity 0.25s ease; + overflow: hidden; +} +.sidebar.collapsed { width: 0; opacity: 0; pointer-events: none; } + +.sidebar-header { + padding: 12px 14px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #0f3460; +} +.sidebar-header span { + font-size: 0.8rem; + color: #888; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +#newChatBtn { + background: #e94560; + border: none; + border-radius: 8px; + color: white; + padding: 5px 12px; + font-size: 0.8rem; + cursor: pointer; +} +#newChatBtn:hover { background: #c73652; } + +.session-list { flex: 1; overflow-y: auto; padding: 8px 0; } +.session-item { + display: flex; + align-items: center; + gap: 8px; + padding: 9px 14px; + cursor: pointer; + border-left: 3px solid transparent; +} +.session-item:hover { background: #1a1a2e; } +.session-item.active { background: #1a1a2e; border-left-color: #e94560; } +.session-item .s-title { flex: 1; font-size: 0.82rem; color: #ccc; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.session-item .s-meta { font-size: 0.7rem; color: #555; } +.session-item .s-del { background: none; border: none; color: #555; cursor: pointer; opacity: 0; } +.session-item:hover .s-del { opacity: 1; } +.session-item .s-del:hover { color: #e94560; } + +.main { flex: 1; display: flex; flex-direction: column; overflow: hidden; } + +.persona-bar { + display: flex; + gap: 8px; + padding: 8px 16px; + overflow-x: auto; + border-bottom: 1px solid #0f3460; +} + +.persona-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 6px 12px; + background: #16213e; + border: 1px solid #0f3460; + border-radius: 10px; + cursor: pointer; + position: relative; + flex-shrink: 0; +} +.persona-card:hover { border-color: #e94560; } +.persona-card.active { border-color: #e94560; background: #1f1535; } +.persona-card .emoji { font-size: 1.2rem; } +.persona-card .pname { font-size: 0.7rem; color: #ccc; } +.persona-card .del-btn { + position: absolute; top: -5px; right: -5px; + width: 14px; height: 14px; + background: #e94560; border: none; border-radius: 50%; + color: white; font-size: 0.55rem; cursor: pointer; + display: none; +} +.persona-card .edit-btn { + position: absolute; top: -5px; left: -5px; + width: 16px; height: 16px; + background: #0f3460; border: 1px solid #e94560; border-radius: 50%; + color: white; font-size: 0.55rem; cursor: pointer; + display: none; align-items: center; justify-content: center; +} +.persona-card:hover .del-btn { display: flex; align-items: center; justify-content: center; } +.persona-card:hover .edit-btn { display: flex; } + +.persona-add, .card-import-btn { + display: flex; flex-direction: column; align-items: center; + padding: 6px 12px; + background: transparent; + border: 1px dashed #0f3460; border-radius: 10px; + cursor: pointer; color: #555; font-size: 0.7rem; + flex-shrink: 0; +} +.persona-add:hover, .card-import-btn:hover { border-color: #e94560; color: #e94560; } + +.messages { + flex: 1; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; +} + +.message { display: flex; flex-direction: column; max-width: 75%; animation: fadeIn 0.2s ease; } +@keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } +.message.user { align-self: flex-end; } +.message.assistant { align-self: flex-start; } + +.bubble { + padding: 10px 14px; + border-radius: 16px; + line-height: 1.5; + font-size: 0.95rem; + white-space: pre-wrap; + word-break: break-word; +} +.bubble.typing-active::after { content: '▋'; animation: blink 0.7s infinite; color: #e94560; } +@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } + +.message.user .bubble { background: #0f3460; border-bottom-right-radius: 4px; } +.message.assistant .bubble { background: #16213e; border: 1px solid #0f3460; border-bottom-left-radius: 4px; } + +.label { font-size: 0.7rem; color: #888; margin-bottom: 4px; padding: 0 4px; } +.message.user .label { text-align: right; } + +.image-prompt-block { + margin-top: 8px; padding: 8px 12px; + background: #1a1a2e; + border: 1px dashed #e94560; + border-radius: 8px; + font-size: 0.8rem; color: #e94560; +} +.image-prompt-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; } +.image-prompt-block .prompt-text { display: block; color: #aaa; margin-top: 4px; font-style: italic; white-space: pre-wrap; } + +.copy-prompt-btn, .gen-image-btn { + background: #0f3460; + border: 1px solid #e94560; + border-radius: 6px; + color: #e94560; + font-size: 0.7rem; + padding: 2px 8px; + cursor: pointer; +} +.copy-prompt-btn:hover, .gen-image-btn:hover { background: #e94560; color: white; } + +.translate-btn { + align-self: flex-end; + background: #0f3460; + border: 1px solid #4a90d9; + border-radius: 6px; + color: #4a90d9; + font-size: 0.7rem; + padding: 2px 8px; + cursor: pointer; + margin-top: 4px; +} +.translate-btn:hover { background: #4a90d9; color: white; } +.translate-btn:disabled { opacity: 0.5; cursor: default; } + +.chat-image { margin-top: 8px; max-width: 100%; border-radius: 8px; border: 1px solid #0f3460; } +.image-error { margin-top: 6px; font-size: 0.75rem; color: #888; } + +.typing { + align-self: flex-start; + display: flex; gap: 4px; + padding: 12px 16px; + background: #16213e; + border: 1px solid #0f3460; + border-radius: 16px; +} +.typing span { width: 6px; height: 6px; background: #888; border-radius: 50%; animation: bounce 1.2s infinite; } +.typing span:nth-child(2) { animation-delay: 0.2s; } +.typing span:nth-child(3) { animation-delay: 0.4s; } +@keyframes bounce { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-6px); } } + +.input-area { + display: flex; gap: 10px; + padding: 12px 16px; + border-top: 1px solid #0f3460; +} + +textarea { + flex: 1; + background: #16213e; + border: 1px solid #0f3460; + border-radius: 12px; + color: #e0e0e0; + font-size: 0.95rem; + padding: 10px 14px; + resize: none; outline: none; + font-family: inherit; + max-height: 120px; +} +textarea:focus { border-color: #e94560; } + +#sendBtn { + background: #e94560; border: none; + border-radius: 12px; color: white; + padding: 0 20px; cursor: pointer; +} +#sendBtn:disabled { background: #555; cursor: not-allowed; } + +#clearBtn { + background: transparent; + border: 1px solid #0f3460; + border-radius: 12px; color: #888; + padding: 0 14px; cursor: pointer; +} +#clearBtn:hover { border-color: #e94560; color: #e94560; } + +.modal-overlay { + display: none; position: fixed; inset: 0; + background: rgba(0,0,0,0.7); + z-index: 100; align-items: center; justify-content: center; +} +.modal-overlay.open { display: flex; } + +.modal { + background: #16213e; border: 1px solid #0f3460; + border-radius: 16px; padding: 24px; + width: 100%; max-width: 440px; + display: flex; flex-direction: column; gap: 12px; +} +.modal h2 { font-size: 1.1rem; color: #e94560; } +.modal label { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem; color: #888; } +.modal input, .modal textarea { + background: #1a1a2e; border: 1px solid #0f3460; + border-radius: 8px; color: #e0e0e0; + padding: 8px 10px; outline: none; font-family: inherit; +} +.modal-buttons { display: flex; gap: 8px; justify-content: flex-end; } +.modal-buttons button { padding: 8px 18px; border-radius: 8px; border: none; cursor: pointer; } +#modalCancel, #cardModalCancel { background: #0f3460; color: #aaa; } +#modalSave, #cardModalImport { background: #e94560; color: white; } + +.empty-state { + flex: 1; display: flex; + align-items: center; justify-content: center; + color: #444; flex-direction: column; gap: 8px; +} +.empty-state .big { font-size: 2.5rem; } +.hidden { display: none !important; } diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..cc25218 --- /dev/null +++ b/static/index.html @@ -0,0 +1,116 @@ + + + + + + AI Chat + + + + +
+ +

🤖 AI Chat

+ Новый чат +
+ +
+ + +
+
+
+
+ 💬 + Начни новый чат +
+
+
+ + + +
+
+
+ + + + + + + + + + diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..41f143d --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,30 @@ +import { toggleSidebar, dom } from './state.js'; +import { initSessions, createNewChat } from './sessions.js'; +import { loadPersonas, initPersonaModals } from './personas.js'; +import { sendMessage, clearHistory } from './chat.js'; + +document.getElementById('sidebarToggle').addEventListener('click', () => { + const open = toggleSidebar(); + document.getElementById('sidebar').classList.toggle('collapsed', !open); +}); + +document.getElementById('newChatBtn').addEventListener('click', createNewChat); + +dom.inputEl.addEventListener('input', () => { + dom.inputEl.style.height = 'auto'; + dom.inputEl.style.height = dom.inputEl.scrollHeight + 'px'; +}); + +dom.inputEl.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + sendMessage(); + } +}); + +dom.sendBtn.addEventListener('click', sendMessage); +dom.clearBtn.addEventListener('click', clearHistory); + +initPersonaModals(); +await initSessions(); +loadPersonas(); diff --git a/static/js/chat.js b/static/js/chat.js new file mode 100644 index 0000000..bcfd75f --- /dev/null +++ b/static/js/chat.js @@ -0,0 +1,260 @@ +import { sessionId, currentPersona, dom } from './state.js'; +import { parseImagePromptFromContent, copyToClipboard } from './utils.js'; + +export async function initChat() { + if (!sessionId || !currentPersona) return; + const res = await fetch('/chat/init', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message: '', session_id: sessionId, persona_id: currentPersona }), + }); + if (!res.ok) return; + const data = await res.json(); + if (data.first_mes) addMessage('assistant', data.first_mes); +} + +export function updateEmptyState() { + const hasMessages = dom.messagesEl.querySelector('.message'); + dom.emptyState?.classList.toggle('hidden', !!hasMessages); +} + +export function createImagePromptBlock(promptText) { + const block = document.createElement('div'); + block.className = 'image-prompt-block'; + + const header = document.createElement('div'); + header.className = 'image-prompt-header'; + header.innerHTML = '🎨 SD prompt'; + + const copyBtn = document.createElement('button'); + copyBtn.type = 'button'; + copyBtn.className = 'copy-prompt-btn'; + copyBtn.textContent = 'Копировать'; + copyBtn.addEventListener('click', async () => { + const ok = await copyToClipboard(promptText); + copyBtn.textContent = ok ? 'Скопировано' : 'Ошибка'; + setTimeout(() => { copyBtn.textContent = 'Копировать'; }, 1500); + }); + header.appendChild(copyBtn); + + const genBtn = document.createElement('button'); + genBtn.type = 'button'; + genBtn.className = 'gen-image-btn'; + genBtn.textContent = '🖼 Генерировать'; + genBtn.addEventListener('click', () => generateImageViaA1111(promptText, block)); + header.appendChild(genBtn); + + const textEl = document.createElement('span'); + textEl.className = 'prompt-text'; + textEl.textContent = promptText; + + block.appendChild(header); + block.appendChild(textEl); + return block; +} + +async function generateImageViaA1111(promptText, block) { + block.parentElement.querySelector('.chat-image')?.remove(); + block.parentElement.querySelector('.image-error')?.remove(); + + try { + const res = await fetch('/images/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ session_id: sessionId, prompt: promptText }), + }); + const data = await res.json(); + if (!res.ok) throw new Error(data.detail || res.statusText); + + const img = document.createElement('img'); + img.className = 'chat-image'; + img.src = data.image_path; + block.parentElement.appendChild(img); + } catch (e) { + const err = document.createElement('div'); + err.className = 'image-error'; + err.textContent = '🖼 ' + e.message; + block.parentElement.appendChild(err); + } +} + +export function appendChatImage(wrapper, imagePath) { + if (!imagePath) return; + const img = document.createElement('img'); + img.className = 'chat-image'; + img.src = imagePath; + wrapper.appendChild(img); +} + +export function addMessage(role, content = '', imagePrompt = null, imagePath = null) { + updateEmptyState(); + + const wrapper = document.createElement('div'); + wrapper.className = `message ${role}`; + + const label = document.createElement('div'); + label.className = 'label'; + label.textContent = role === 'user' ? 'Вы' : 'AI'; + wrapper.appendChild(label); + + let displayContent = content; + let prompt = imagePrompt; + if (role === 'assistant' && !prompt) { + const parsed = parseImagePromptFromContent(content); + displayContent = parsed.text; + prompt = parsed.prompt; + } + + const bubble = document.createElement('div'); + bubble.className = 'bubble'; + bubble.textContent = displayContent; + wrapper.appendChild(bubble); + + if (role === 'assistant') { + const translateBtn = document.createElement('button'); + translateBtn.type = 'button'; + translateBtn.className = 'translate-btn'; + translateBtn.textContent = '🌐 RU'; + let originalText = null; + translateBtn.addEventListener('click', async () => { + if (originalText !== null) { + bubble.textContent = originalText; + originalText = null; + translateBtn.textContent = '🌐 RU'; + return; + } + originalText = bubble.textContent; + translateBtn.disabled = true; + translateBtn.textContent = '…'; + try { + const res = await fetch('/translate/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ text: originalText }), + }); + if (!res.ok) throw new Error(res.statusText); + const data = await res.json(); + bubble.textContent = data.translated; + translateBtn.textContent = '↩ Оригинал'; + } catch { + originalText = null; + translateBtn.textContent = '⚠️'; + setTimeout(() => { translateBtn.textContent = '🌐 RU'; }, 2000); + } + translateBtn.disabled = false; + }); + wrapper.appendChild(translateBtn); + } + + if (prompt) wrapper.appendChild(createImagePromptBlock(prompt)); + if (imagePath) appendChatImage(wrapper, imagePath); + + dom.messagesEl.appendChild(wrapper); + dom.messagesEl.scrollTop = dom.messagesEl.scrollHeight; + return bubble; +} + +export function showTyping() { + const typing = document.createElement('div'); + typing.className = 'typing'; + typing.id = 'typing'; + typing.innerHTML = ''; + dom.messagesEl.appendChild(typing); + dom.messagesEl.scrollTop = dom.messagesEl.scrollHeight; +} + +export function removeTyping() { + document.getElementById('typing')?.remove(); +} + +export function clearMessages() { + dom.messagesEl.innerHTML = ''; + if (dom.emptyState) { + dom.messagesEl.appendChild(dom.emptyState); + dom.emptyState.classList.remove('hidden'); + } +} + +export async function sendMessage() { + const text = dom.inputEl.value.trim(); + if (!text || !sessionId) return; + + dom.inputEl.value = ''; + dom.inputEl.style.height = 'auto'; + dom.sendBtn.disabled = true; + + addMessage('user', text); + showTyping(); + + try { + const res = await fetch('/chat/stream', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ message: text, session_id: sessionId, persona_id: currentPersona }), + }); + if (!res.ok) throw new Error('Ошибка сервера: ' + res.status); + + const reader = res.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + let bubble = null; + + removeTyping(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop(); + + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + try { + const data = JSON.parse(line.slice(6)); + + if (data.chunk !== undefined) { + if (!bubble) { + bubble = addMessage('assistant', ''); + bubble.classList.add('typing-active'); + } + bubble.textContent += data.chunk; + bubble.textContent = bubble.textContent.replace(/\[IMAGE_PROMPT:.*?\]/gs, '').trim(); + dom.messagesEl.scrollTop = dom.messagesEl.scrollHeight; + } + + if (data.done) { + bubble?.classList.remove('typing-active'); + if (data.image_prompt && bubble) { + bubble.parentElement.appendChild(createImagePromptBlock(data.image_prompt)); + } + if (data.image_path && bubble) { + appendChatImage(bubble.parentElement, data.image_path); + } + if (data.image_error && bubble) { + const err = document.createElement('div'); + err.className = 'image-error'; + err.textContent = '🖼 ' + data.image_error; + bubble.parentElement.appendChild(err); + } + const { loadSessions } = await import('./sessions.js'); + loadSessions(); + } + } catch { /* skip */ } + } + } + } catch (err) { + removeTyping(); + addMessage('assistant', '⚠️ Ошибка: ' + err.message); + } finally { + dom.sendBtn.disabled = false; + dom.inputEl.focus(); + } +} + +export async function clearHistory() { + if (!sessionId) return; + await fetch(`/chat/${sessionId}`, { method: 'DELETE' }); + clearMessages(); +} diff --git a/static/js/personas.js b/static/js/personas.js new file mode 100644 index 0000000..e36ca0f --- /dev/null +++ b/static/js/personas.js @@ -0,0 +1,164 @@ +import { currentPersona, setCurrentPersona, sessionId } from './state.js'; +import { initChat } from './chat.js'; + +export function highlightPersona(personaId) { + document.querySelectorAll('.persona-card').forEach(c => { + c.classList.toggle('active', c.dataset.id === personaId); + }); +} + +export async function loadPersonas() { + const res = await fetch('/personas/'); + const personas = await res.json(); + const bar = document.getElementById('personaBar'); + bar.innerHTML = ''; + + personas.forEach(p => { + const card = document.createElement('div'); + card.className = 'persona-card' + (p.persona_id === currentPersona ? ' active' : ''); + card.dataset.id = p.persona_id; + const isCard = p.persona_id.startsWith('card_'); + card.innerHTML = ` + ${p.emoji} + ${p.name} + ${p.custom ? `` : ''} + ${isCard ? `` : ''} + `; + card.addEventListener('click', () => selectPersona(p.persona_id)); + card.querySelector('.del-btn')?.addEventListener('click', async (e) => { + e.stopPropagation(); + await fetch(`/personas/${p.persona_id}`, { method: 'DELETE' }); + if (currentPersona === p.persona_id) await selectPersona('default'); + loadPersonas(); + }); + card.querySelector('.edit-btn')?.addEventListener('click', async (e) => { + e.stopPropagation(); + const cardId = p.persona_id.slice(5); + const r = await fetch(`/characters/${cardId}`); + const data = await r.json(); + document.getElementById('editCardId').value = cardId; + document.getElementById('editName').value = data.name || ''; + document.getElementById('editDescription').value = data.description || ''; + document.getElementById('editPersonality').value = data.personality || ''; + document.getElementById('editScenario').value = data.scenario || ''; + document.getElementById('editFirstMes').value = data.first_mes || ''; + document.getElementById('editMesExample').value = data.mes_example || ''; + document.getElementById('editAppearance').value = data.appearance_tags || ''; + document.getElementById('editLora').value = data.lora_name || ''; + document.getElementById('editLoraWeight').value = data.lora_weight ?? 0.8; + document.getElementById('cardEditOverlay').classList.add('open'); + }); + bar.appendChild(card); + }); + + const addBtn = document.createElement('button'); + addBtn.type = 'button'; + addBtn.className = 'persona-add'; + addBtn.innerHTML = '➕Создать'; + addBtn.addEventListener('click', () => document.getElementById('modalOverlay').classList.add('open')); + bar.appendChild(addBtn); + + const importBtn = document.createElement('button'); + importBtn.type = 'button'; + importBtn.className = 'card-import-btn'; + importBtn.innerHTML = '📥Chub'; + importBtn.addEventListener('click', () => document.getElementById('cardModalOverlay').classList.add('open')); + bar.appendChild(importBtn); +} + +export async function selectPersona(personaId) { + setCurrentPersona(personaId); + highlightPersona(personaId); + if (sessionId) { + await fetch(`/sessions/${sessionId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ persona_id: personaId }), + }); + await initChat(); + } +} + +export function initPersonaModals() { + document.getElementById('modalCancel').addEventListener('click', () => { + document.getElementById('modalOverlay').classList.remove('open'); + }); + document.getElementById('cardModalCancel').addEventListener('click', () => { + document.getElementById('cardModalOverlay').classList.remove('open'); + }); + document.getElementById('cardEditCancel').addEventListener('click', () => { + document.getElementById('cardEditOverlay').classList.remove('open'); + }); + + document.getElementById('modalSave').addEventListener('click', async () => { + const data = { + persona_id: document.getElementById('pId').value.trim(), + name: document.getElementById('pName').value.trim(), + emoji: document.getElementById('pEmoji').value.trim() || '🤖', + description: document.getElementById('pDesc').value.trim(), + prompt: document.getElementById('pPrompt').value.trim(), + sd_enabled: document.getElementById('pSdEnabled').checked, + lora_name: document.getElementById('pLora').value.trim(), + appearance_tags: document.getElementById('pAppearance').value.trim(), + }; + if (!data.persona_id || !data.name || !data.prompt) { + alert('Заполни ID, имя и промт'); + return; + } + await fetch('/personas/', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + }); + document.getElementById('modalOverlay').classList.remove('open'); + await loadPersonas(); + await selectPersona(data.persona_id); + }); + + document.getElementById('cardEditSave').addEventListener('click', async () => { + const cardId = document.getElementById('editCardId').value; + const body = { + name: document.getElementById('editName').value.trim() || undefined, + description: document.getElementById('editDescription').value.trim() || undefined, + personality: document.getElementById('editPersonality').value.trim() || undefined, + scenario: document.getElementById('editScenario').value.trim() || undefined, + first_mes: document.getElementById('editFirstMes').value.trim() || undefined, + mes_example: document.getElementById('editMesExample').value.trim() || undefined, + appearance_tags: document.getElementById('editAppearance').value.trim() || undefined, + lora_name: document.getElementById('editLora').value.trim() || undefined, + lora_weight: parseFloat(document.getElementById('editLoraWeight').value) || undefined, + }; + const res = await fetch(`/characters/${cardId}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + if (!res.ok) { alert('Ошибка сохранения'); return; } + document.getElementById('cardEditOverlay').classList.remove('open'); + await loadPersonas(); + }); + + document.getElementById('cardModalImport').addEventListener('click', async () => { + const fileInput = document.getElementById('cardFile'); + if (!fileInput.files?.length) { + alert('Выберите файл карточки (JSON или PNG)'); + return; + } + const form = new FormData(); + form.append('file', fileInput.files[0]); + form.append('lora_name', document.getElementById('cardLora').value.trim()); + form.append('lora_weight', document.getElementById('cardLoraWeight').value || '0.8'); + + const res = await fetch('/characters/import', { method: 'POST', body: form }); + const data = await res.json(); + if (!res.ok) { + alert(data.detail || 'Ошибка импорта'); + return; + } + document.getElementById('cardModalOverlay').classList.remove('open'); + fileInput.value = ''; + await loadPersonas(); + await selectPersona(data.persona_id); + }); +} + diff --git a/static/js/sessions.js b/static/js/sessions.js new file mode 100644 index 0000000..330ccc2 --- /dev/null +++ b/static/js/sessions.js @@ -0,0 +1,86 @@ +import { sessionId, setSessionId, setCurrentPersona, currentPersona, dom } from './state.js'; +import { clearMessages, addMessage, initChat } from './chat.js'; +import { highlightPersona } from './personas.js'; + +function escapeTitle(t) { + const d = document.createElement('div'); + d.textContent = t; + return d.innerHTML; +} + +export async function loadSessions() { + const res = await fetch('/sessions/'); + const sessions = await res.json(); + dom.sessionList.innerHTML = ''; + + sessions.forEach(s => { + const item = document.createElement('div'); + item.className = 'session-item' + (s.session_id === sessionId ? ' active' : ''); + item.innerHTML = ` +
${escapeTitle(s.title || 'Новый чат')}
+
${s.message_count} сообщ.
+ + `; + item.addEventListener('click', () => switchSession(s.session_id)); + item.querySelector('.s-del').addEventListener('click', async (e) => { + e.stopPropagation(); + await fetch(`/sessions/${s.session_id}`, { method: 'DELETE' }); + if (s.session_id === sessionId) createNewChat(); + else loadSessions(); + }); + dom.sessionList.appendChild(item); + }); +} + +export async function switchSession(id) { + setSessionId(id); + clearMessages(); + await loadSessions(); + await loadChatHistory(id); +} + +export async function loadChatHistory(id) { + const sessionRes = await fetch(`/sessions/${id}`); + if (sessionRes.ok) { + const s = await sessionRes.json(); + dom.headerTitle.textContent = s.title || 'Новый чат'; + if (s.persona_id) { + setCurrentPersona(s.persona_id); + highlightPersona(s.persona_id); + } + } + + const histRes = await fetch(`/chat/history/${id}`); + if (!histRes.ok) return; + + const messages = await histRes.json(); + clearMessages(); + messages.filter(m => m.role !== 'system').forEach(m => { + addMessage( + m.role === 'user' ? 'user' : 'assistant', + m.content, + m.image_prompt, + m.image_path ? `/static/${m.image_path}` : null, + ); + }); +} + +export async function createNewChat() { + setSessionId('sess_' + Math.random().toString(36).slice(2, 10)); + clearMessages(); + dom.headerTitle.textContent = 'Новый чат'; + highlightPersona(currentPersona); + await initChat(); + loadSessions(); +} + +export async function initSessions() { + await loadSessions(); + if (sessionId) { + const check = await fetch(`/sessions/${sessionId}`); + if (check.ok) await switchSession(sessionId); + else createNewChat(); + } else { + createNewChat(); + } +} diff --git a/static/js/state.js b/static/js/state.js new file mode 100644 index 0000000..b518c71 --- /dev/null +++ b/static/js/state.js @@ -0,0 +1,24 @@ +export let sessionId = localStorage.getItem('chat_session_id') || null; +export let currentPersona = localStorage.getItem('persona_id') || 'default'; +export let sidebarOpen = true; +export function toggleSidebar() { sidebarOpen = !sidebarOpen; return sidebarOpen; } + +export function setSessionId(id) { + sessionId = id; + if (id) localStorage.setItem('chat_session_id', id); +} + +export function setCurrentPersona(id) { + currentPersona = id; + localStorage.setItem('persona_id', id); +} + +export const dom = { + messagesEl: document.getElementById('messages'), + inputEl: document.getElementById('input'), + sendBtn: document.getElementById('sendBtn'), + clearBtn: document.getElementById('clearBtn'), + sessionList: document.getElementById('sessionList'), + headerTitle: document.getElementById('headerTitle'), + emptyState: document.getElementById('emptyState'), +}; diff --git a/static/js/utils.js b/static/js/utils.js new file mode 100644 index 0000000..7e43ecc --- /dev/null +++ b/static/js/utils.js @@ -0,0 +1,16 @@ +export function parseImagePromptFromContent(content) { + if (!content || !content.includes('[IMAGE_PROMPT:')) return { text: content, prompt: null }; + const match = content.match(/\[IMAGE_PROMPT:(.*?)\]/s); + const prompt = match ? match[1].trim() : null; + const text = content.replace(/\[IMAGE_PROMPT:.*?\]/gs, '').trim(); + return { text, prompt }; +} + +export async function copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + return false; + } +}