From 736e5a8c12f3f11f9adc2eea4ef96478116b09eb Mon Sep 17 00:00:00 2001 From: xds Date: Thu, 5 Feb 2026 15:29:31 +0300 Subject: [PATCH] feat: Add logging to API endpoints, update generation response model, and refine project configurations. --- .DS_Store | Bin 0 -> 6148 bytes .env | 3 +- .idea/.gitignore | 8 ++ .idea/ai-char-bot.iml | 10 ++ .idea/inspectionProfiles/Project_Default.xml | 16 +++ .../inspectionProfiles/profiles_settings.xml | 6 ++ .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ .vscode/launch.json | 5 + __pycache__/keyboards.cpython-313.pyc | Bin 0 -> 2323 bytes __pycache__/main.cpython-313.pyc | Bin 0 -> 7119 bytes adapters/Exception.py | 5 +- .../__pycache__/Exception.cpython-313.pyc | Bin 0 -> 716 bytes .../google_adapter.cpython-313.pyc | Bin 3923 -> 7479 bytes .../__pycache__/kling_adapter.cpython-313.pyc | Bin 0 -> 5330 bytes adapters/google_adapter.py | 39 ++++++-- api/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 155 bytes api/__pycache__/dependency.cpython-313.pyc | Bin 0 -> 1421 bytes api/dependency.py | 2 +- .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 165 bytes .../__pycache__/assets_router.cpython-313.pyc | Bin 0 -> 4769 bytes .../character_router.cpython-313.pyc | Bin 0 -> 3821 bytes .../generation_router.cpython-313.pyc | Bin 0 -> 5167 bytes api/endpoints/assets_router.py | 11 ++- api/endpoints/character_router.py | 10 +- api/endpoints/generation_router.py | 28 +++++- api/models/GenerationRequest.py | 8 +- .../__pycache__/AssetDTO.cpython-313.pyc | Bin 0 -> 1024 bytes .../GenerationRequest.cpython-313.pyc | Bin 0 -> 2543 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 162 bytes .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 163 bytes .../generation_service.cpython-313.pyc | Bin 0 -> 16484 bytes api/service/generation_service.py | 92 +++++++++++++++--- main.py | 3 + middlewares/__pycache__/album.cpython-313.pyc | Bin 0 -> 2709 bytes middlewares/__pycache__/dao.cpython-313.pyc | Bin 0 -> 1025 bytes models/Generation.py | 7 +- models/__pycache__/Asset.cpython-313.pyc | Bin 0 -> 2071 bytes models/__pycache__/Character.cpython-313.pyc | Bin 0 -> 866 bytes models/__pycache__/Generation.cpython-313.pyc | Bin 0 -> 2172 bytes models/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 158 bytes models/__pycache__/enums.cpython-313.pyc | Bin 0 -> 2112 bytes models/enums.py | 2 +- repos/__pycache__/assets_repo.cpython-313.pyc | Bin 0 -> 5730 bytes repos/__pycache__/char_repo.cpython-313.pyc | Bin 0 -> 3027 bytes repos/__pycache__/dao.cpython-313.pyc | Bin 0 -> 1020 bytes .../generation_repo.cpython-313.pyc | Bin 0 -> 3478 bytes repos/__pycache__/user_repo.cpython-313.pyc | Bin 3390 -> 3428 bytes routers/__pycache__/__init__.cpython-313.pyc | Bin 0 -> 159 bytes .../__pycache__/assets_router.cpython-313.pyc | Bin 0 -> 4200 bytes .../__pycache__/auth_router.cpython-313.pyc | Bin 0 -> 4716 bytes .../__pycache__/char_router.cpython-313.pyc | Bin 0 -> 10838 bytes .../__pycache__/gen_router.cpython-313.pyc | Bin 0 -> 27905 bytes routers/gen_router.py | 3 - 55 files changed, 244 insertions(+), 35 deletions(-) create mode 100644 .DS_Store create mode 100644 .idea/.gitignore create mode 100644 .idea/ai-char-bot.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .vscode/launch.json create mode 100644 __pycache__/keyboards.cpython-313.pyc create mode 100644 __pycache__/main.cpython-313.pyc create mode 100644 adapters/__pycache__/Exception.cpython-313.pyc create mode 100644 adapters/__pycache__/kling_adapter.cpython-313.pyc create mode 100644 api/__pycache__/__init__.cpython-313.pyc create mode 100644 api/__pycache__/dependency.cpython-313.pyc create mode 100644 api/endpoints/__pycache__/__init__.cpython-313.pyc create mode 100644 api/endpoints/__pycache__/assets_router.cpython-313.pyc create mode 100644 api/endpoints/__pycache__/character_router.cpython-313.pyc create mode 100644 api/endpoints/__pycache__/generation_router.cpython-313.pyc create mode 100644 api/models/__pycache__/AssetDTO.cpython-313.pyc create mode 100644 api/models/__pycache__/GenerationRequest.cpython-313.pyc create mode 100644 api/models/__pycache__/__init__.cpython-313.pyc create mode 100644 api/service/__pycache__/__init__.cpython-313.pyc create mode 100644 api/service/__pycache__/generation_service.cpython-313.pyc create mode 100644 middlewares/__pycache__/album.cpython-313.pyc create mode 100644 middlewares/__pycache__/dao.cpython-313.pyc create mode 100644 models/__pycache__/Asset.cpython-313.pyc create mode 100644 models/__pycache__/Character.cpython-313.pyc create mode 100644 models/__pycache__/Generation.cpython-313.pyc create mode 100644 models/__pycache__/__init__.cpython-313.pyc create mode 100644 models/__pycache__/enums.cpython-313.pyc create mode 100644 repos/__pycache__/assets_repo.cpython-313.pyc create mode 100644 repos/__pycache__/char_repo.cpython-313.pyc create mode 100644 repos/__pycache__/dao.cpython-313.pyc create mode 100644 repos/__pycache__/generation_repo.cpython-313.pyc create mode 100644 routers/__pycache__/__init__.cpython-313.pyc create mode 100644 routers/__pycache__/assets_router.cpython-313.pyc create mode 100644 routers/__pycache__/auth_router.cpython-313.pyc create mode 100644 routers/__pycache__/char_router.cpython-313.pyc create mode 100644 routers/__pycache__/gen_router.cpython-313.pyc diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7408157d3a16832079bbb6eb8c50bfc8525f91c3 GIT binary patch literal 6148 zcmeHKOHRWu5Pc2>surP&70ZY{mAFAvl?7`KP|_v{MB0>o1l{Ef9DozBfg^Ddcw<{4 zNy8QanvurOaprl(ud>|$FoSh+2J`^5>4L2en+B8j;#(H@kuK5LIVOlP!3|Q3wxV@F z6;K6!n*y?TyWFiK%(3C#{o22yi!7gvvwX}5IUWY|=MQV_$&nBvq0h16eqUhO5Z^Dw zA2B*-hC3U5%3h8s**TUR=`;HjS#=I`Qp?%Fur$XtZjm9aa->dkSju}~Gj1>@GiIF$ z^Yvx5RbH@Qu5!KmtS4rMSyMFLYSCRr-*WCmD#C-!l-4x~f*Na&tel*bth#=$jD0Ds zW0$q3POn9!7mPd>4(-vwMlLjY-x3sKU;Zozh8`o2g+sof8Al~Ls_~x~ z#?d(*`nbqr;n2}xY-Y~mGaLVfVr+Jfhc+B0a;U8;pbAtK*l~|Nx&P0<|NgI%^hp&^ z1^$%+rZv1A4!9(Lw{9&??%I%kMHiE}!l9+G@z=5K$W?qyx5js&bcm71!XZa!`bWUZ Lpp7c + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..476b22b --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6d15bbe --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b3ccf67 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..97b8ad3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,5 @@ +{ + "configurations": [ + {"name":"Python Debugger: FastAPI","type":"debugpy","request":"launch","module":"uvicorn","args":["main:app","--reload", "--port", "8090"],"jinja":true} + ] +} \ No newline at end of file diff --git a/__pycache__/keyboards.cpython-313.pyc b/__pycache__/keyboards.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65f1ac89aca19ea2032fd6890099fa0315290efd GIT binary patch literal 2323 zcmah~-ES0C6uBJ`jfo_R0hkvYDJQU`&mgI!*1>>3AW(lDoTL+h#5-xXLaoZzSy@ z-OiYnD<3H6*^E8q%I_GtgHw3}tP8!n_9f9%ZOA|fFc&*uIZKAQnqU~Y-q1LW*Mx39 z%0)R%?B=$RuryfM0Wv!+3cvE98aWlTffA)O&RZ=iSg=1DPnMmEaDHiUD3E@P%C;oWkplQq>(lA z(fpK}r{+<(trgWXk>nAbMn+9LS~FmE=BM1oF~d%P`+{NFiSbdo7OeSUT5YgQlY7CJ z?)hJ2ZT;+h!||t?Z>%ivuQfa-ydswgFUfQbx|a*!J&{Qzmtq54A51T9eL(zw+~TB> zZ1bX57f(@>mibYLtJdH}RW9rYW9%He#cs1Z>^du9ByTuhs1V_+>?SLLq^LSGYJaZ^ z3b#Raip|4*nH8NA>{>gKs`j*#$5&4?taWKC1xbhAOxu01flQ1Py^^ zVLdG6^XI-_2KE}*_JDPjs(#AIO(p0m(DZ-`a}=Fe7$B9l!0N8W(Cev*OfHc!k{K&w z<`98akTs%e4=mHPS zLWHCmLXu_cw&4mX-AwXexHf`7oP@b}0sgFwr~p))4AsL})qK6W_d|gn0az!wv%;XH zkQ^W9hUzU=^@gM>QMbi;jYGD^cXQ9!5r(nQyfKU!=nYMX3qxKTs&Oj2moktfSaNmR z4Z})J4}8h!3+E8VbH(s1_|U>r=MgY(zBMAB42V%B`Y; z)`ThVBZF69p6(LTTzJ)>D`Oo)sX?bwkkpg5S9%4E5<3r>JMGNuP~Da>BbS&kQ$_-` za%0{`E9r}k1Nzbob#1j#(o6yCR8oE0w1^V22iV?0<+{QOU}%6+N-8;!1ZC_U{`b{jDY2a zkg*gDieablVi8x&=@SOUymF;UJzFp+HYJMb6&C3WNN+@;=vfmzYZx6BMx{ub(Dc$z zKzT+%jP-B*1RP)6^ar=DtTzBzRudY*$l7_bG4?OXa(B_Nc4#UZY?KQLuw3lKfaA%FDk7 z`~J05xbumYo<{C#t|uVQwgGjW>ruoTyH^4Ev%Jy!5Yoaq*d8#SC z1<+2b5=;xL4k`t6HEVSmxx$3yLAH#Q>!(KEv^t@AQA~61m2!nl&i1;chY7{X^IoG0 zaK1OeU?FSlq}aKzp;>RhQWiLlyH8&Eo%H@mx*XDVpKSQsCvb8(AanACZBGbn<<(6< Px0VTP#kPNN^DO@dS@DME literal 0 HcmV?d00001 diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12cde9d4399baab18d3a503d181613b6b02e6ee5 GIT binary patch literal 7119 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 MP1YHH>!#rSU*eK9r~m)} literal 0 HcmV?d00001 diff --git a/adapters/Exception.py b/adapters/Exception.py index 173335b..b330748 100644 --- a/adapters/Exception.py +++ b/adapters/Exception.py @@ -1,3 +1,4 @@ class GoogleGenerationException(Exception): - message: str - pass \ No newline at end of file + def __init__(self, message: str): + self.message = message + super().__init__(message) \ No newline at end of file diff --git a/adapters/__pycache__/Exception.cpython-313.pyc b/adapters/__pycache__/Exception.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6528c4be590bb47a06856dbce3f13f80108fad07 GIT binary patch literal 716 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 literal 3923 zcma(UZEO_Bb@uZH}Ab&t*CG!XpQn2rN)EMU&y4`*aEOR1HdIDB9R(KI6qRDvgS0VVWfwd zAr`Yk9Oj02%n#YHO(1y8uzkpZ9Tv0sCVz840i^dq~lAlZ&WIQZ?BFN=!GIK6NTUncTriDJ~Nr zE~}D40DUSYYq30?eD5kuR!0E1gvJn24b-AABhq8+Zdzm-&~X-X&8QiPE|D8!MShGI zZDTgkK4u?d#~iz94$khjiw=T$oWf4giCu9v;5I7;lgW5O?i-g$|#Y6*B;6YT8R5u8X!+OeQ~DiyGqd?Fc-%h+TUbs}jxUzHMRxgX;s#zZ8O-=9!qRX6!q zesoSI$K^y+l_uo?ZL*r2n6T(;#At$G_J<;xjJ43IaV<11pOh2HR46qiq;T@M9MiRs zqy%HfBpf`N)I(C<`dTP%T}HGp??_FV_GlE;(4$ea?khLdQCx>tfwXTxcLuH4D=)at zyJiNL?2V7A>Q<^7ji#Q5)xCzhccsohn|LQN|6HbS*EIX4E5{@M&T00os#Q3NMob>$ znoQ}YOCcT^Jvyb!npp{6DPu{Oqt>d)V_8pQHCB9hVu;CBq7oD}mDUAIxo*MaWRir^--OFFeoW*$XQ_CbSd`f8u5lfR zE*RG>u@~}&{2=pvV&=?+%y}@$O#ztwwuJc`ShCo6jF{|^{@0E`A#%8Hus=RwwZFa? z&pS@;MaXO$v8<(%swPJv;YkTaS0FREba^sGQbQWc0mkI4Txi;3NmYj&sX>xVsuN1w zL?kup@+nB0mO2_HA;Dx`85x9Phle~pDe0Kx15BLtbaf$XlSFQrrCUgom}~qbGYu+S zCk`UOX15Ti#Wq5UO{Qekq?M#;+eebt!Iw>5O2OK=>F7TdlT$j}GA1t*w~%flImBD? zYNn^;t_8_)4Z$Hf#jGtjoxDG<6bwWoE+t{TpoU9P6N)NF$0c1d?UGhVldLA4lu1P( zpD-Cs$AJn=as(zJANxs1YD!Gvm&sXXF)7(3^+eIU_M**6%GJ^d-DC+{lTjw)rky0` zsD!ab${FF=hDp{j?M1EDmJG%9dce|t0T28!6b-osRO_F4e%k&I9=W|anyWZTt@xU* zI^T8vyz`gcKkJ@9@vyD?p>OXDm-YB&t1ebu#kdNH)Y&S zbMYnj)*QRV(F5DQ#@W{|zP{`YWW0f8Z+FJqovmw_Oo=`<8<)MEpLjd7?VXRd zcm0%E)ShsRuR3Q#zP&kQ^Hom2l&eN9o96drYD3clP_B4<7v8w=Mz(SD)sY{K%pAyi z{j*cEQ(0g0+{r7I^P7Jact4Qw?YdG4s6HUR4SpH#w$GbeuTEZ`ypo(_R@$}@b>8cl zvuAz&tM<$GYrcgobM}ny`K(X4T5-AJnzZC=ClpU*eC^A=P{tQpu>aB5^~8&swu7B& z8m15b^Xa!JRMWJIXksT%-K_hf+i2-slomy!sc*@%FY9YDHtB{p{i!GYWsW0gUq1ad zg*^3e#?iCluD)>S{2{|1oR{WBqkiX-yE9wkH(GYyXj_ney>-E7G<6#r_kQZ$OHMm_ z{-q`0y3V==TdBAA_U}L+JNy8Cyk+0Efj0DePhSfR_d*n`-P_?D5SV-2{sBL8zm@{{ zejNwv_x&7!1%|+F&cPPuesJ4h1M{GU0{B5K!FkZY5x9lm1ZeA(SZ^Ufj|{+90#u=- z(1j}086|NboglC~8vRaMN)&dgqR|NjYdU!nYBCzdT_87xN&>t*Ur(|}lgR`oNf(n6 z5AP#;wb7`iOS%$^O1h4fqiJ}=(dhdK6EC$K8j~*|t0T`?I|AJgP>!~7JFhWM5ezx_ z1lyWzYk%U{PjNk;*LZUr0l*U)T5>iEw4a!gQ~&?~ diff --git a/adapters/__pycache__/kling_adapter.cpython-313.pyc b/adapters/__pycache__/kling_adapter.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..236ecec551769ff558a56f57112fe608bc00fc1f GIT binary patch literal 5330 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 literal 0 HcmV?d00001 diff --git a/adapters/google_adapter.py b/adapters/google_adapter.py index bbabfd8..7298066 100644 --- a/adapters/google_adapter.py +++ b/adapters/google_adapter.py @@ -1,7 +1,7 @@ import io import logging from datetime import datetime -from typing import List, Union +from typing import List, Union, Tuple, Dict, Any from PIL import Image from google import genai @@ -27,6 +27,7 @@ class GoogleAdapter: """Вспомогательный метод для подготовки контента (текст + картинки)""" contents = [prompt] if images_list: + logger.info(f"Preparing content with {len(images_list)} images") for img_bytes in images_list: try: # Gemini API требует PIL Image на входе @@ -34,6 +35,8 @@ class GoogleAdapter: contents.append(image) except Exception as e: logger.error(f"Error processing input image: {e}") + else: + logger.info("Preparing content with no images") return contents def generate_text(self, prompt: str, images_list: List[bytes] = None) -> str: @@ -59,20 +62,25 @@ class GoogleAdapter: for part in response.parts: if part.text: result_text += part.text - logger.error(f"Generated text: {result_text}") + logger.info(f"Generated text length: {len(result_text)}") return result_text except Exception as e: logger.error(f"Gemini Text API Error: {e}") - return f"Ошибка генерации текста: {e}" + raise GoogleGenerationException(f"Gemini Text API Error: {e}") - def generate_image(self, prompt: str, aspect_ratio: AspectRatios, quality: Quality, images_list: List[bytes] = None, ) -> List[io.BytesIO]: + def generate_image(self, prompt: str, aspect_ratio: AspectRatios, quality: Quality, images_list: List[bytes] = None, ) -> Tuple[List[io.BytesIO], Dict[str, Any]]: """ Генерация изображений (Text-to-Image или Image-to-Image). Возвращает список байтовых потоков (готовых к отправке). """ - contents = self._prepare_contents(prompt, images_list) + contents = self._prepare_contents(prompt, images_list) + logger.info(f"Generating image. Prompt length: {len(prompt)}, Ratio: {aspect_ratio}, Quality: {quality}") + + start_time = datetime.now() + token_usage = 0 + try: response = self.client.models.generate_content( model=self.IMAGE_MODEL, @@ -86,6 +94,13 @@ class GoogleAdapter: ), ) ) + + end_time = datetime.now() + api_duration = (end_time - start_time).total_seconds() + + if response.usage_metadata: + token_usage = response.usage_metadata.total_token_count + if response.parts is None and response.candidates[0].finish_reason is not None: raise GoogleGenerationException(f"Generation blocked in cause of {response.candidates[0].finish_reason.value}") @@ -111,9 +126,17 @@ class GoogleAdapter: except Exception as e: logger.error(f"Error processing output image: {e}") - return generated_images + if generated_images: + logger.info(f"Successfully generated {len(generated_images)} images in {api_duration:.2f}s. Tokens: {token_usage}") + else: + logger.warning("No images text generated from parts") + + metrics = { + "api_execution_time_seconds": api_duration, + "token_usage": token_usage + } + return generated_images, metrics except Exception as e: logger.error(f"Gemini Image API Error: {e}") - # В случае ошибки возвращаем пустой список (или можно рейзить исключение) - return [] \ No newline at end of file + raise GoogleGenerationException(f"Gemini Image API Error: {e}") \ No newline at end of file diff --git a/api/__pycache__/__init__.cpython-313.pyc b/api/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8ed9432154643ffeb7cf18caa48c76e12171846 GIT binary patch literal 155 zcmey&%ge<81Q$*>W`gL)AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl=hunHXtf`Krbyv;I$N_N~EXiN=*e-%1S+5(?FB5TGwkC$VfR| z&(?B~Ls@HZTW_c{s-2l+%PJU&MMFzgTFgf3G;0{Md~ov&w{r~@&*U(|ponD-c^Q)* z#z@T^YBMR@Y~<{GL&H;<8OWT1L>y=j8ja4ge*o_T&6e(NGCvT$A?MuQ&U5#Q%P!>eshZSla{dviS1R8_vq{O_%24j;tH9jcQfpl zOWjKgz3OkfOyMWHlRx)~zqbD)wmZT`C!XK$6@GeGJky<;?G-QXrd3{kn>G6i^7rP6 QKIwgpX%qkJC`q(m0BhPX$N&HU literal 0 HcmV?d00001 diff --git a/api/dependency.py b/api/dependency.py index 70f4479..4455940 100644 --- a/api/dependency.py +++ b/api/dependency.py @@ -25,6 +25,6 @@ def get_dao(mongo_client: AsyncIOMotorClient = Depends(get_mongo_client)) -> DAO # Провайдер сервиса (собирается из DAO и Gemini) def get_generation_service( dao: DAO = Depends(get_dao), - gemini: GoogleAdapter = Depends(get_gemini_client) + gemini: GoogleAdapter = Depends(get_gemini_client), ) -> GenerationService: return GenerationService(dao, gemini) \ No newline at end of file diff --git a/api/endpoints/__pycache__/__init__.cpython-313.pyc b/api/endpoints/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..287cc9fcd54aed25153c81834f575fa3f7c6dfc2 GIT binary patch literal 165 zcmey&%ge<81k3I>W`gL)AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekkl;~{m|mnqGJ7u zlw$pq)Uwo^`~v-gN`-=={H)aEl4AYDOx@&+#3J3K{1W}df=vC?yp)3c%sik>e0*kJ nW=VX!UP0w84x8Nkl+v73yCPPgp&(0(L5z>gjEsy$%s>_Zp0g?0 literal 0 HcmV?d00001 diff --git a/api/endpoints/__pycache__/assets_router.cpython-313.pyc b/api/endpoints/__pycache__/assets_router.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff270361429657e210eaea2f86bd3f8a92c0687d GIT binary patch literal 4769 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 ZZD?eJ2e~#D+oC;1c%R#(n)os};lGp8jQaop literal 0 HcmV?d00001 diff --git a/api/endpoints/__pycache__/generation_router.cpython-313.pyc b/api/endpoints/__pycache__/generation_router.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..117aef62792faad9298f765f5e9730ca2d569d6e GIT binary patch literal 5167 zcmbUkOKcm*b!K<@7bz|!OR{9jv?YH8SfnkR;A?G%lV=^?F)9$FxW+NbK!0tVWH9rWPivNTF&TcbA8BFK$`1QfaT&5%n{j1(9h zU}xU^-e=x-H|=&Sf|k4cv&3h1gubQ|y|EpbZ2p@==yN0?35QS+6CC6S=b>{h#0Lc; z1P#OxG!kRbL`-a*51E4&VqvflvIcF$#$ZFJC1@vh1{*_;U@K{5uqosWibM>$h%4Ae z+Jfz*ovoWgdx9OLgTaXaJyB%J%S}?q+JptHpvy~klG?`BW9_+8{H6Y;FHy> zWdAN0_f#>OfWB477%{@Eqc+=4jh8wjT*PAl%u-hbM+^+AQ{LM|`2bM9s7p{ErEa(@ zvb!LWWR-d()FhW6?W<98NBX2*_<#;bx4(%-|1Q#bBI1tkbs*wucrOt!9&EyRXcw;g zcEQ*m@l;=595G6Vny?+-1>50W-WcoLjR?|_CR|6BtOG~Cr4Kp4X~J|u$!g{+nQS7R ziX{gG&EkJ!YBs%^m5F8zXOihy{8Az*YeKb8la&FinJ&l~ITcqZI4i%uDl1tEDl)m5 zSdaniSdz2RB{?ON7{wgbcLdEfSzDczl}tLN$eP%=#;|FQH%NLVldT=G>vNr0!yTzc z)>=n2lXLoFnS`&-ghE^up^X_LFD7njLN>Of?%RsR*l?bih@|uM4ReiJV;}sRh1nt;h`UC-80dgU|LM55$?d+V*VY$?B?v=csYB z7Dls19j=eM?i~=H-~^@6)Q(o?u{{WF!?wd&9orQw@eVXB=yMMWbF;Q4#4z2Nr=9sY z32~LSDTt-ZAnQ&1IX`1)K`DL?VsWK~c}W<>{qRsT_S5_#9x%-0j>nYuqScqLX~(k= zOD5&G=YvFc*;Co{B<0jnc6rQ`6TP14MCv`*hVyb(8S@ZI`ytjq>C~VGOqwy7URnb4 z780q&w8q0-vJcK^_9VL*t=!bwcOp(&;pBioI$$83wCkc*omDH4<5hMQ0hT?`wAX!M zFn+~%E3WwB@=ZCJ&iFEGl<^z#LRRs`5`zoNF*0~Pon`h4Zk0(VQn1rtS%f*HH?u|# zP@1#@l_KT$(0z+;qb)Ns*gg&238}8YUxy!mT%3&LCnJUP^J?IldM#Z>=q)bJ{g)0z zAD(3tSYO5G>B1_0liR2aTL`_6XF1rr#jVjHgL8BcKjdJ`@Kdhj_7&Y1^6m?duN5y| z&0oB#Ms5@%xAKu&1@~I9b4?Z6|7qK+9ypP=4XMJ=29LUO_&bF<-(CMiz=^*^IN%Vz z#14RSF5UeWK|h0umDrN(t?0wZCd$rk)b;g$b$?*ju3<2H2p z7(yu_EX-Q;8}(67kl!X_d-zN_09=cpO77B)(J z)osSXby}J02s62(Rkxw98f6bz^&78eioUVpSxB#@vfwgpUe6oMHK>*;h-6Nns(YsOfHrU0!+H+|hv>lBrWx0skZNk2uyyrre0*y#1O ztgL7THq%TC=~PxuWywJ}r%>Hc_kl+GC~5I~@{mDTr@SdYhmLtcOU(<${xbaN=AXwO z4XTq@pUp+pcNYtDOX}5S)tPuE+$go}xjp?4vGbmx)aCxI_1D(lv=m+a_e>>od(qsJ zH}~A1{JXh-!+=~}Wi#qMUUY}@?$Fcef;(31j5XW01+^X8Fru#OVB}QDyMA-Pil10< zz-WA88>H|V954${MjZfuX~xiN=v!KKYAw|FV+)XNqlvg?NG4ViSWfD4O2gaHmoPN}@{_*3VJY9Xdq=u;(sL>_Wx%^B>l*GQGIGh)U*QeFD zBC0s7it|O=yeiD+ZS&tLEr9Fp&^Z1h9u@v%7{@t-FG^NZnzbI4cMeSJaPd|3Gux

w0{(JguMp0j=^gxnr5M(pa*2j)J%YHQiRSD%CJkuJc$mXiQf#?|`vE zmu=Nd+GyM?f>Z)~#{xuW{Hoy3+x%6h z8$5;ofJcON!zr9I`@jfOiPX}zC!}VMx4pd2zfTo-NwWs>0d4?QB)9b~pYd?h$@v zaN~ianlZbU0l5g3g;1N&Op7u2`^Y2+4a|hr`-EmeLL-VLQ4_ipdBqT zh?Cyvr8lboaNY*nt}YRg9umKYlKpULCR%TUteOR$dAd@2o1@WGY(%CMj$V6?j(&|CFD%ZYrTcG|?)zs7mj2tu zmll`WeQbSteIjoO+%|%KIrFDIcY0L2XTA4!kBWcva&o5BH@eB=%XoZ)H{jt1AC(ad zk3V2TY4}2!uD&?;`j!F4O&)Q6T;>2QJ8Q@eta~Xk|4UE*?|%G+^{c)k5BM)ezdC+e zJu_1{er3bdf`?1auKR7ju$Os&UpU)8yP9`;$_AJKQ*LjW9)VKD*uG@}h>{5a8M@d@ z$#mc8z8AQkRr^on_n*Gqt>QDKGp{`zQs=LI6_`?k?-l~F4W7r>@B{OD+e3R9QTVY` z8P&J1R)CWC+!i}U@sE}{dZ^q|731F9>Y>wAj599-Q>ES!c$4$^)CMo$Nvs||T}Bjm ze55i0rZU@kF*dzrpgUER6f8TcEUPWOs?Sf^4ZQSUR;Qu`|GSjU0DizzjsmzeI8kN` zCHVu&T7cm>5291J!W)%6SiqexgKw&{%Y|TKgXi$X1Kh($ literal 0 HcmV?d00001 diff --git a/api/endpoints/assets_router.py b/api/endpoints/assets_router.py index 815fee8..89985db 100644 --- a/api/endpoints/assets_router.py +++ b/api/endpoints/assets_router.py @@ -13,12 +13,16 @@ from models.Asset import Asset, AssetType from repos.dao import DAO from api.dependency import get_dao +import logging + +logger = logging.getLogger(__name__) + router = APIRouter(prefix="/api/assets", tags=["Assets"]) @router.get("/{asset_id}") async def get_asset(asset_id: str, request: Request,dao: DAO = Depends(get_dao),) -> Response: - + logger.debug(f"get_asset called for ID: {asset_id}") asset = await dao.assets.get_asset(asset_id) # 2. Проверка на существование if not asset: @@ -32,8 +36,9 @@ async def get_asset(asset_id: str, request: Request,dao: DAO = Depends(get_dao), @router.get("") async def get_assets(request: Request, dao: DAO = Depends(get_dao), limit: int = 10, offset: int = 0) -> AssetsResponse: + logger.info(f"get_assets called. Limit: {limit}, Offset: {offset}") assets = await dao.assets.get_assets(limit, offset) - assets = await dao.assets.get_assets() + # assets = await dao.assets.get_assets() # This line seemed redundant/conflicting in original code total_count = await dao.assets.get_asset_count() return AssetsResponse(assets=assets, total_count=total_count) @@ -46,6 +51,7 @@ async def upload_asset( linked_char_id: Optional[str] = Form(None), dao: DAO = Depends(get_dao), ): + logger.info(f"upload_asset called. Filename: {file.filename}, ContentType: {file.content_type}, LinkedCharId: {linked_char_id}") if not file.content_type: raise HTTPException(status_code=400, detail="Unknown file type") @@ -65,6 +71,7 @@ async def upload_asset( asset_id = await dao.assets.create_asset(asset) asset.id = str(asset_id) + logger.info(f"Asset created successfully. ID: {asset_id}") return AssetResponse( id=asset.id, diff --git a/api/endpoints/character_router.py b/api/endpoints/character_router.py index 8f20bba..ae72b9b 100644 --- a/api/endpoints/character_router.py +++ b/api/endpoints/character_router.py @@ -12,11 +12,16 @@ from models.Character import Character from repos.dao import DAO from api.dependency import get_dao +import logging + +logger = logging.getLogger(__name__) + router = APIRouter(prefix="/api/characters", tags=["Characters"]) @router.get("/", response_model=List[Character]) async def get_characters(request: Request, dao: DAO = Depends(get_dao), ) -> List[Character]: + logger.info("get_characters called") characters = await dao.chars.get_all_characters() return characters @@ -24,6 +29,7 @@ async def get_characters(request: Request, dao: DAO = Depends(get_dao), ) -> Lis @router.get("/{character_id}/assets", response_model=AssetsResponse) async def get_character_assets(character_id: str, dao: DAO = Depends(get_dao), limit: int = 10, offset: int = 0, ) -> AssetsResponse: + logger.info(f"get_character_assets called. CharacterID: {character_id}, Limit: {limit}, Offset: {offset}") character = await dao.chars.get_character(character_id) if character is None: raise HTTPException(status_code=404, detail="Character not found") @@ -34,11 +40,13 @@ async def get_character_assets(character_id: str, dao: DAO = Depends(get_dao), l @router.get("/{character_id}", response_model=Character) async def get_character_by_id(character_id: str, request: Request, dao: DAO = Depends(get_dao)) -> Character: + logger.debug(f"get_character_by_id called. ID: {character_id}") character = await dao.chars.get_character(character_id) return character -@router.post("/{character_id}/_run", response_model=Asset) +@router.post("/{character_id}/_run", response_model=GenerationResponse) async def post_character_generation(character_id: str, generation: GenerationRequest, request: Request) -> GenerationResponse: + logger.info(f"post_character_generation called. CharacterID: {character_id}") generation_service = request.app.state.generation_service diff --git a/api/endpoints/generation_router.py b/api/endpoints/generation_router.py index 72d103a..0b4a092 100644 --- a/api/endpoints/generation_router.py +++ b/api/endpoints/generation_router.py @@ -1,6 +1,6 @@ from typing import List, Optional -from fastapi import APIRouter +from fastapi import APIRouter, UploadFile, File, Form from fastapi.params import Depends from starlette.requests import Request @@ -11,6 +11,10 @@ from api.models.GenerationRequest import GenerationResponse, GenerationRequest, from api.service.generation_service import GenerationService from models.Generation import Generation +import logging + +logger = logging.getLogger(__name__) + router = APIRouter(prefix='/api/generations', tags=["Generation"]) @@ -18,13 +22,31 @@ router = APIRouter(prefix='/api/generations', tags=["Generation"]) async def ask_prompt_assistant(prompt_request: PromptRequest, request: Request, generation_service: GenerationService = Depends( get_generation_service)) -> PromptResponse: + logger.info(f"ask_prompt_assistant called with prompt length: {len(prompt_request.prompt)}. Linked assets: {len(prompt_request.linked_assets) if prompt_request.linked_assets else 0}") generated_prompt = await generation_service.ask_prompt_assistant(prompt_request.prompt, prompt_request.linked_assets) return PromptResponse(prompt=generated_prompt) +@router.post("/prompt-from-image", response_model=PromptResponse) +async def prompt_from_image( + prompt: Optional[str] = Form(None), + images: List[UploadFile] = File(...), + generation_service: GenerationService = Depends(get_generation_service) +) -> PromptResponse: + logger.info(f"prompt_from_image called. Images count: {len(images)}. Prompt provided: {bool(prompt)}") + images_bytes = [] + for image in images: + content = await image.read() + images_bytes.append(content) + + generated_prompt = await generation_service.generate_prompt_from_images(images_bytes, prompt) + return PromptResponse(prompt=generated_prompt) + + @router.get("", response_model=List[GenerationResponse]) -async def get_generations(character_id: Optional[str], limit: int = 10, offset: int = 0, +async def get_generations(character_id: Optional[str] = None, limit: int = 10, offset: int = 0, generation_service: GenerationService = Depends(get_generation_service)): + logger.info(f"get_generations called. CharacterId: {character_id}, Limit: {limit}, Offset: {offset}") return await generation_service.get_generations(character_id, limit=limit, offset=offset) @@ -32,12 +54,14 @@ async def get_generations(character_id: Optional[str], limit: int = 10, offset: async def post_generation(generation: GenerationRequest, request: Request, generation_service: GenerationService = Depends( get_generation_service)) -> GenerationResponse: + logger.info(f"post_generation (run) called. LinkedCharId: {generation.linked_character_id}, PromptLength: {len(generation.prompt)}") return await generation_service.create_generation_task(generation) @router.get("/{generation_id}", response_model=GenerationResponse) async def get_generation(generation_id: str, generation_service: GenerationService = Depends(get_generation_service)) -> GenerationResponse: + logger.debug(f"get_generation called for ID: {generation_id}") return await generation_service.get_generation(generation_id) diff --git a/api/models/GenerationRequest.py b/api/models/GenerationRequest.py index 5a4c512..0b71ce8 100644 --- a/api/models/GenerationRequest.py +++ b/api/models/GenerationRequest.py @@ -5,7 +5,7 @@ from pydantic import BaseModel from models.Asset import Asset from models.Generation import GenerationStatus -from models.enums import AspectRatios, Quality +from models.enums import AspectRatios, Quality, GenType class GenerationRequest(BaseModel): @@ -20,12 +20,18 @@ class GenerationResponse(BaseModel): id: str status: GenerationStatus failed_reason: Optional[str] = None + linked_character_id: Optional[str] = None aspect_ratio: AspectRatios quality: Quality prompt: str + tech_prompt: Optional[str] = None assets_list: List[str] result: Optional[str] = None + execution_time_seconds: Optional[float] = None + api_execution_time_seconds: Optional[float] = None + token_usage: Optional[int] = None + progress: int = 0 created_at: datetime = datetime.now(UTC) updated_at: datetime = datetime.now(UTC) diff --git a/api/models/__pycache__/AssetDTO.cpython-313.pyc b/api/models/__pycache__/AssetDTO.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a355fb3870f09b44b55287e922d6d95cdb16d43 GIT binary patch literal 1024 zcmZuw&ubGw6rRog$R=(2LmG`jrCN*4p{8omm7vtJ!8Uul?X+BQjqS9(RvF4mtii6c(7nP!uj}5n z3qJ78X&F!bzfLQAm1gynUTIp|k0T{ivM-t??L11P(*Aa?k}Qq-=&js~q}a-0K^u2N zN}+lMcY%|O*!ULik64$16*4G=7T7DS>vUbJtm}oPo3xhEvdIb^@b}!Nr)LbM+z~R* zQYlU==+s_B_eedZG8OfCFB_!lGwU=f+UGot_66s<%K3g45Bg+JbN+e|^(UTL&YveB zRX<5ZnsE*eazdGi(G{gBl2UyVBkd$ry3V-hmtT?WXI7ZBdF z(e&J~{-J&BTpG3AdXV(GI+F|E}FD%N;lI{^vI;}Z(7>F zQ;gbOSjJHNcA0D<{Io60`pOo+v1>orRs5ZKC96Jsdd6@$tGd?Z;pQ)fi^=adr@--j literal 0 HcmV?d00001 diff --git a/api/models/__pycache__/GenerationRequest.cpython-313.pyc b/api/models/__pycache__/GenerationRequest.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..152561ec99d8c446ffbf622258d98ddccaaddd87 GIT binary patch literal 2543 zcma)8-ER{|5a08y&*!h$PGa&!Lq0^QX%b76k4BgTuqvd!h!4rx%%E?lSr%Gv_E)DR3DKbiCh!qA{{nqoi?=CiR+i^@9Z}F_>a=LHnzw{fA!DC(oMyvYl`$~AvI?3Y zgn)+ekgau?-y>DRHH+|wrE&cr3MPaJWv#-ZJ$X@1zfEtI*obE!2GqAS9IEqA_T#jqWDVI7TT+P030fUM-iuhd`o(T`A2kQbM(RN z{kMEN-_{J@UG zy`famUWHN>fMi9&%Z~<7vM7%MY^VZ2G=K)k;(f9JBneon-~kYdTBh=X{^ANUl&AVu zB$PJ&+-owl8R)Y~g|ZCDvf=LOP?qbH4TQ2mkPU@)870f|;pz~aAcwPRerXPdUEd6; zjvRwb;KxU+d9WV*pVni_dPwaw%*U5$Auk8}uU51Ha_rdA>KcN$Ho z4$d6Was$XJ6e{Q``=rNeH|(G?_%M1fI1`%zlk7 z>9uY$*X{^snehmEGr{#TcoZ^GgJXDb8po4~P8(Ys@(SgJ3BC^#GlB%$1s_8)j${H! z9Ld{Au;hYBu)B>oO^1t2ndz1afqhf(k@3iO8Sal9FhT~6w5>njd#2;uWc z7LZIL!Kw=|J%!xN4#9MZ-+(~5n8~~6da3l6Tpv(@2HRRLr-u|hzkJit3t?w@RbgH*-QY6^C+bw!R=Yq&E zsJ*N8C}?&!{uk9+g#1@y&#Os20Z^mnaMGC}lX5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iOerR!OQL%nS zO0j-QYFTPdet~{Lr9webepYI7NwI!nrfzaZVv%lAeu;i!L8g9geoAUiv3`7fW?p7V ke7s&kPO4oIE6_-gmBk>&M`lJw#v*1Q3jolNC`kYS literal 0 HcmV?d00001 diff --git a/api/service/__pycache__/generation_service.cpython-313.pyc b/api/service/__pycache__/generation_service.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62c6ea22b3d8d2f1da2e4861c4ab8a804176e9fe GIT binary patch literal 16484 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 Irj!c)FT12aBme*a literal 0 HcmV?d00001 diff --git a/api/service/generation_service.py b/api/service/generation_service.py index 0091d1f..3bcce54 100644 --- a/api/service/generation_service.py +++ b/api/service/generation_service.py @@ -2,7 +2,7 @@ import asyncio import logging import random from datetime import datetime, UTC -from typing import List, Optional +from typing import List, Optional, Tuple, Any, Dict from io import BytesIO from adapters.Exception import GoogleGenerationException @@ -11,7 +11,7 @@ from api.models.GenerationRequest import GenerationRequest, GenerationResponse # Импортируйте ваши модели DAO, Asset, Generation корректно from models.Asset import Asset, AssetType from models.Generation import Generation, GenerationStatus -from models.enums import AspectRatios, Quality +from models.enums import AspectRatios, Quality, GenType from repos.dao import DAO logger = logging.getLogger(__name__) @@ -24,20 +24,24 @@ async def generate_image_task( aspect_ratio: AspectRatios, quality: Quality, gemini: GoogleAdapter -) -> List[bytes]: +) -> Tuple[List[bytes], Dict[str, Any]]: """ Обертка для вызова синхронного метода Gemini в отдельном потоке. Возвращает список байтов сгенерированных изображений. """ try : + logger.info(f"Starting generate_image_task with prompt length: {len(prompt)}") # Запускаем блокирующую операцию в отдельном потоке, чтобы не тормозить Event Loop - generated_images_io: List[BytesIO] = await asyncio.to_thread( + result = await asyncio.to_thread( gemini.generate_image, prompt=prompt, images_list=media_group_bytes, aspect_ratio=aspect_ratio, quality=quality, ) + generated_images_io, metrics = result + + logger.info(f"generate_image_task completed, received {len(generated_images_io) if generated_images_io else 0} images") except GoogleGenerationException as e: raise e images_bytes = [] @@ -51,13 +55,13 @@ async def generate_image_task( # Закрываем поток img_io.close() - return images_bytes - + return images_bytes, metrics class GenerationService: def __init__(self, dao: DAO, gemini: GoogleAdapter): self.dao = dao self.gemini = gemini + async def ask_prompt_assistant(self, prompt: str, assets: List[str] = None) -> str: future_prompt = """You are an prompt-assistant. You improving user-entered prompts for image generation. User may upload reference image too. @@ -68,11 +72,20 @@ class GenerationService: if assets is not None: assets_db = await self.dao.assets.get_assets_by_ids(assets) assets_data.extend(asset.data for asset in assets_db) - generated_prompt = self.gemini.generate_text(future_prompt, assets_data) + generated_prompt = await asyncio.to_thread(self.gemini.generate_text, future_prompt, assets_data) logger.info(future_prompt) logger.info(generated_prompt) return generated_prompt + async def generate_prompt_from_images(self, images: List[bytes], user_prompt: Optional[str] = None) -> str: + technical_prompt = "You are a prompt engineer. Describe this image in detail to create a stable diffusion using this image as reference. " + if user_prompt: + technical_prompt += f"User also provided this context: {user_prompt}. " + + technical_prompt += "Provide ONLY the detailed prompt." + + return await asyncio.to_thread(self.gemini.generate_text, prompt=technical_prompt, images_list=images) + async def get_generations(self, character_id: Optional[str] = None, limit: int = 10, offset: int = 0) -> List[ Generation]: return await self.dao.generations.get_generations(limit=limit, offset=offset) @@ -97,8 +110,10 @@ class GenerationService: generation_model.id = gen_id async def runner(gen): + logger.info(f"Starting background generation task for ID: {gen.id}") try: await self.create_generation(gen) + logger.info(f"Background generation task finished for ID: {gen.id}") except Exception: # если генерация уже пошла и упала — пометим FAILED try: @@ -125,6 +140,8 @@ class GenerationService: raise async def create_generation(self, generation: Generation): + start_time = datetime.now() + logger.info(f"Processing generation {generation.id}. Character ID: {generation.linked_character_id}") # 2. Получаем ассеты-референсы (если они есть) reference_assets: List[Asset] = [] @@ -146,27 +163,47 @@ class GenerationService: if asset.data is not None and asset.type == AssetType.IMAGE ) generation_prompt+=f"PROMPT: {generation.prompt}" + logger.info(f"Final generation prompt assembled. Length: {len(generation_prompt)}. Media count: {len(media_group_bytes)}") - # 3. Запускаем процесс генерации + # 3. Запускаем процесс генерации и симуляцию прогресса + progress_task = asyncio.create_task(self._simulate_progress(generation)) + try: - generated_bytes_list = await generate_image_task( + + # Default to Image Generation (Gemini) + generated_bytes_list, metrics = await generate_image_task( prompt=generation_prompt, # или request.prompt media_group_bytes=media_group_bytes, aspect_ratio=generation.aspect_ratio, # предполагаем поля в request quality=generation.quality, gemini=self.gemini ) + + # Update metrics from API (Common for both) + generation.api_execution_time_seconds = metrics.get("api_execution_time_seconds") + generation.token_usage = metrics.get("token_usage") + except GoogleGenerationException as e: generation.status = GenerationStatus.FAILED - generation.failed_reason = str(e.message) + generation.failed_reason = str(e) generation.updated_at = datetime.now(UTC) await self.dao.generations.update_generation(generation) - raise + raise e except Exception as e: # Тут стоит добавить логирование ошибки logging.error(f"Generation failed: {e}") - # Можно обновить статус генерации на FAILED в БД + generation.status = GenerationStatus.FAILED + generation.failed_reason = str(e) + generation.updated_at = datetime.now(UTC) + await self.dao.generations.update_generation(generation) raise e + finally: + if not progress_task.done(): + progress_task.cancel() + try: + await progress_task + except asyncio.CancelledError: + pass # 4. Сохраняем полученные изображения как новые Ассеты created_assets: List[Asset] = [] @@ -192,6 +229,37 @@ class GenerationService: generation.assets_list = result_ids generation.status = GenerationStatus.DONE + generation.progress = 100 generation.updated_at = datetime.now(UTC) generation.tech_prompt = generation_prompt + + end_time = datetime.now() + generation.execution_time_seconds = (end_time - start_time).total_seconds() + await self.dao.generations.update_generation(generation) + logger.info(f"Generation {generation.id} completed successfully. {len(created_assets)} assets created. Total Time: {generation.execution_time_seconds:.2f}s") + + + async def _simulate_progress(self, generation: Generation): + """ + Increments progress from 0 to 90 over ~20 seconds. + """ + current_progress = 0 + try: + while current_progress < 90: + await asyncio.sleep(4) + # Random increment between 5 and 15 + increment = random.randint(5, 15) + current_progress = min(current_progress + increment, 90) + + # Fetch latest state (optional, but good practice to avoid overwriting unrelated fields) + # But for simplicity here we just use the object we have and save it. + # Ideally, we should fetch-update-save or use partial update if DAO supports it. + # Assuming simple update is fine for now. + generation.progress = current_progress + await self.dao.generations.update_generation(generation) + except asyncio.CancelledError: + # Task cancelled, generation finished (or failed) + pass + except Exception as e: + logger.error(f"Error in progress simulation: {e}") diff --git a/main.py b/main.py index de7960a..86e4723 100644 --- a/main.py +++ b/main.py @@ -43,6 +43,7 @@ load_dotenv() # --- КОНФИГУРАЦИЯ --- BOT_TOKEN = os.getenv("BOT_TOKEN") GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") + MONGO_HOST = os.getenv("MONGO_HOST") # Например: mongodb://localhost:27017 DB_NAME = os.getenv("DB_NAME", "my_bot_db") # Имя базы данных ADMIN_ID = int(os.getenv("ADMIN_ID", 0)) @@ -63,6 +64,7 @@ mongo_client = AsyncIOMotorClient(MONGO_HOST) users_repo = UsersRepo(mongo_client) char_repo = CharacterRepo(mongo_client) dao = DAO(mongo_client) # Главный DAO для бота +dao = DAO(mongo_client) # Главный DAO для бота gemini = GoogleAdapter(api_key=GEMINI_API_KEY) generation_service = GenerationService(dao, gemini) @@ -113,6 +115,7 @@ async def lifespan(app: FastAPI): # Инициализируем DAO для ассетов и кладем в state приложения # Теперь в эндпоинтах можно делать request.app.state.assets_dao + app.state.mongo_client = mongo_client app.state.mongo_client = mongo_client app.state.gemini_client = gemini diff --git a/middlewares/__pycache__/album.cpython-313.pyc b/middlewares/__pycache__/album.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f878b7630035455702720501a8bd42c0f32587cd GIT binary patch literal 2709 zcmcguO>7fK6rTO_U)He`+xdZzIzNaQ5u5x&fut!3q)y_fSy@V3ORJ5&ah9&v?(7DL zR0-Nj6_tt_kRrk<;Y7I6Q`;L9(e@ZCl^nLBMQShHoTjKf^w2l{2@xeZpsHhg=6i4E zy?O7=do$j0Ivohwv8x{`2^&Jc5~AF=64-hdz#$;Sb z<3;tw9{eh_tyvfrQ4C>9K$sRN%m@_%9b>y`f!T-7vF9kv32e*~v&L*Od(6>IlMsaH zSVhc1P>~k6m=%23u|lxITAN@aIxsKTu`}ri@TO}}ot{fQx9_PHbeL9EGGr}2AE$vT zH-bq>wi=;ZL{o0HJcEv+C{n31nM8M}NPxzbz%U&W*+5K4h7_PpR+rV8!h}xL`TgcV zD5}d?4_!>?p@e*1R@0eKX5OE{>2q@2&_j~a9-ozP`*hj}rMANee4;j(nKx~ssA!5I zie}vlz68rNB)B@U<}%u-ZTy_y=@ zno=Y96Zma4Ll|0+AA%62C(E-t#SFUP(kgyV+8RsIwHt9Yd5ZpPe71ZRji&Ich_ZpW%1C>3^|%29eJx z<~1}!^)WM4fQ!Vp?*y=vU=RGZ_CW`wgI-oTtq4QWAh;$Ssp2&$DhZ&di#n0_W1_*d zom8dNbVBMk5C6O7<8Tj)V(_Gz2Bh}`D6VY=NuA{UWpuCV`-X2Da19Wkn|WWJH@U)v zf=SHa_GNchafSy8{X_8mDcGIok=vJZwZQjLy?@2GUf-Vc zw6F2)Pds%w&!Md6&=p(Wf_U%pz|Dd6io+X$;Hve?&=(V*O)QOM`Ga}d?d<%m=D>~A z_0-DH?Xg>9>opxYcgLE$1H`-y%a?9mTK5D2`|58@T$@-K$$Hy!-p(w1`^MJ@h-|dB z-JZBLad#x!dTL>4srK6Vqo%f1-+EI|ZhueS@m$N@?ym;E9JqHZ+coyX+u5#(HDPMK zYigseVcB)lwQ9@Ob>tb;{SLJT8~Ttpl=r@xtOGf#wmr=8N3#5p)##6W*9Kqx`*GwA zZdRkJ&P_LJ=>6*z&HGyy;1oVSaK=R~Q^PKHB?RccyJffr-S47?tJ(YAgH9kGP}H!O zeLyn=b3L%=!HGd13=gX*U_JB}`kLM|Jo6pT03RMnl39gJiUg=6U}#SU0+r-h)1;IP z&zeINQ|ZKf5Jz^*=_ Lpglt{6g2z;jchau literal 0 HcmV?d00001 diff --git a/middlewares/__pycache__/dao.cpython-313.pyc b/middlewares/__pycache__/dao.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b586fc316b44fca173cfb48bee0da4cb89799ff0 GIT binary patch literal 1025 zcmZuw%}*0i5TCan{Qyew1H@`fEhM@mw3iYuN}_TRv?Q)gI8 z;K4)>ja>BLN&XA}1dRuE6HdkhwM<+6 zPmaaH;cG%Ux7=FIa~=rkP~?u|Idv%-i_2?HHNc2IcXhEE6-V0WJKZ9JHdH_|8>D74 zsoPq`C~CHzfi**#wo!>;?|9L$W4%bij>}{{VI{-VobZqO?#FSbP_GHUs`WP~LzrlH zM^Lq4X~aVtNgdgjc3Eg}EV8uFP>#1Elenm;Dw_?j6w1nxs_?L;3N>fL@%&bywV7>6 zpXO2p;Z9dqg`8gYgF@ra31F`9^R3M=!MWRX1J1*-zo_O9;|c0Tytm-X$y3jhU4#&t z*r^$WMok2wdU#N@Lih=O-J7T;AdB;q;WpxbrL5cR0tU-02TKz({qwkim%NH+vBBDU z$sB@7NI=QBq^~ebEA{4-8Qz%&j!Dap4};b8nr&Btq%$7W7_(qhA{8(nMwAV<%~DxB7~mwDdLQ0=~e?dMsSpK z6^OvCauEd5U0x3y#rZDCW0XyUNxG4qBB0KrdI}wlnd3Xx4**RkrkPXk?sWjo`{GYB zQ8ne4taum~u21h@(#s1|eJ>wuwj31^N=m2Yt2{oGh-$CpIm|wy5hgB2)6U8qF5tfm dDub${GsgBHw+9!$!|8)qj7_~L{Qxw*+TT80>`(vz literal 0 HcmV?d00001 diff --git a/models/Generation.py b/models/Generation.py index 9476060..2f1f510 100644 --- a/models/Generation.py +++ b/models/Generation.py @@ -5,7 +5,7 @@ from typing import List, Optional from pydantic import BaseModel, Field from models.Asset import Asset -from models.enums import AspectRatios, Quality +from models.enums import AspectRatios, Quality, GenType class GenerationStatus(str, Enum): @@ -24,5 +24,10 @@ class Generation(BaseModel): tech_prompt: Optional[str] = None assets_list: List[str] result: Optional[str] = None + progress: int = 0 + execution_time_seconds: Optional[float] = None + api_execution_time_seconds: Optional[float] = None + token_usage: Optional[int] = None + created_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) diff --git a/models/__pycache__/Asset.cpython-313.pyc b/models/__pycache__/Asset.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97a91d87aa63bfe123bf9f3e371f7ecc3c310326 GIT binary patch literal 2071 zcmb_d&uA?Ok_v zP3xWvR3)gDFo#H~RH4Ckn<6U1e`P<&Bq{?S3t=R!H~)(*)-F_JDDaI;sr4$^;UpHz((z|Nqo-< zJL0;Qx74UvkE7_#!{w?`vbbCWT*kY}7BdkZ)pgsbTDr~^U9UQ3y+U$a*FUWrmA)mV z<9@*PDrMWU9W;keE*$ybIG0bKIkj+liI3~L>lt3Ts2iRK!MY0YS~&t#eH0`ik_P_%KMAw4cNczcv;+oRdKVy*5_&sNZ8|uFs>2vYt{Gxp>@f;T;E{layNIhLwA+9itWj2K66h9=uNMRJ$PCVt4 zeLfxVsem8Th70mrA#G$QyCCK83p8L{8-}E4 zPq4Zu*gDy;R1Kc=O1kM3F~^vDWQS3&op(IvDI80Zsd?b0Ee z?Ed7#sk>9Z6n{OG58lKZmolvtqh9g!6{F}m(BMiDEWDK_7U=(d44|~YECCh#v(3Xk zQeQ*ZLBi_;BUkD*Y7w*`W%Nt5 zU-k$LHUXRR~b%;u4=hgN_Ci5TPhiw?d4Ap^LP+Vq|a#)bDP5?;g(S(4>m-doCf z_JV~{criF@eH3ESKVzosv>)N zoOXu8DL3y$G=+gUh8Q(Bc1f6Vwx$gc;Xdn9u0M>jJw0t%a$e`S56ew&@q cKPfx11dn0vABOMhkN^Mx literal 0 HcmV?d00001 diff --git a/models/__pycache__/Generation.cpython-313.pyc b/models/__pycache__/Generation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3f8a9fbef5e2ee260bdc040a415d0a3ed9b2efb GIT binary patch literal 2172 zcmb_d&2JM&6rc63*BgJtaU3U3Fu^25$Z8Xds3Fh~Dj)<{gHdC0!qRH7CuWPY>&~oO ze6vK=f>c%JfD~@@R{n*mQZK#rlE_uN9jQW9RaM+tncn-}+93{7ZyjlW`{sSk%$xV# z^kT6vfl>S7Q|oV;kl%5#Khy!y`x1!9q(n+mf#`%0U1Cxe=Yc{%mznI-azW99EU2qY z_2)_EkyMgi|M0mRFAWGmZZrXDTQW8Nh^<)inl5#%Z7Sr=n-_DEP%hccZ4fBeEba>JM#Hrn+pGg; z1b0IMXPAEgqcF8r}CVvqDLlI(4+qUeQ zP^ykig}P;0by|H!iaAYahGCoA)G$QYFt(j)vyODsFg}8f4J2{H*s>UR>y}Mz2gIR` z&0_It@v>0XZWJ#Gb^ZL+YnRqU$}l)AYgG)>b(wX$=~8YOYy$SsgO7;!^P8M9p1)V+ z`6~UG)}2Pa(aJWM^C5(U=S^#|a>rzgw;eYRd!conKRD4+qs8Lj#RohWPXK&IywR!l zWiOIwuX(Z5{dIP9U*}LL2RBW6YY^1KpULpgX#E?$Z=f&7~ISF1y z2O1Ngks6#JKHyCb@x}(cDc~gsdI{u}(@+!Rko(EA5}Pj1fX|6n{M3Md#OF`?KBicv z7RaSV_~3G0jDe$~2v}92Vh%L9i1pLVVASM}Es~HucWKqY3pOh*Wd`U*O}`inKW9Y< za}J9^g^bv4xFX`x${k~WVqyhxqmJdKGRm8Emjz&1p@BvX7+g%!d$iKT{5P;v4NfbL zUFBlNY*@yB$Re(Dm)b^?o3+E$hiw4A-f4ix=f*REhAt>ADn=_h0_&)<49n%e4z+8 z8`v~)GrmdgC`+Mm5@8A zU8V5L@cbvG(7!-DT``yJ2LjVmC=}i`h7CffZmR+BMnAIt4QY*5)wH3-GYu5{K#FL; zRWJE39K&b+6$@SQp?ktaX73>QmzqWDaLl0``mfMAwgytHB#s&Nmn=!rb29fUNj)dW o|0D~4klE*Cb&ssRRAp(h!*|wR5+GiN6Vf%QGyM<2zkkMm0j|yi6951J literal 0 HcmV?d00001 diff --git a/models/__pycache__/__init__.cpython-313.pyc b/models/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4530e556a64ab3c68fe42028908fcbe78f0fc1b7 GIT binary patch literal 158 zcmey&%ge<81XsHnGC}lX5CH>>P{wB#AY&>+I)f&o-%5reCLr%KNa~iYerR!OQL%nS zO0j-QYFTPdet~{Lr9webepYI7NwI!nrfzaZVv%lAeu;i=eoAUiv3`7fW?p7Ve7s&k gnNFvV|!O} zsq^HAF;*z74-4e6ul;lSv~uZSF!qo)4|(r9M^X|wb)QBr=(|Vf>YjV<`M#qtm&*_s ze?R%D@tZ`*U-)u5l*9;kz&IifafCAYMqomL%#b=ibi-FrXx}%OmGMj9f3)X$fzSR*`Z8vWTsX% zN2%&1(NSl~19~Ib$TE(`jD6kA@a!GG^0kR)^;kEWpg7j(8u-4 z*ISnL)#mmV*Ab&vt}rj?vQ}M4qT{5&gK36O0FKD_fbrN8Pp$LW5mM_(&CXW|R5Z1I zTK&`qj+9!*lP?U1F41iwgLj^ovVBNztuEg@q{}x~Zyic2tEEG!w7O!-48LG10tR6^ z0?Obykl=gGJs)pa;H$-L-(!C9(XLh)Mza110 z+PhxUFM2?szu4(<<6*7Y^<0MK<}>NqFQ!)TQ2#DKp9~d}yZn20{zZ2F+1{({%!oPn zB0KlYioCHo(B*t)kRiszKZcyo4~VSiLx@A36Wh#k&2?L~1J8AN#&sdJZWHaS>;BlS zHItPI*WGI{KWH{uUaRf8;|jtxxNOH`L61v*z*rtm;M1<_2XL>tTMGi#xZe#tAGS}y z8keX(@ondb6WXsv&>~we3=<|a$4#r z$VlqV=;V|X--^_iq%$c~n~Te3E?$SHR5mHQ0vWIm5w0Q>5OM&qa3cbEF4h?`4%g(< z$$b;~CGN|(X$2q_;H6*JAFm(JzsgRX8yk-|j!OfTg5EULn@&-0R8I&(+HKQdSVlID z@E*eZ2p2M(K_8yTK0=s9xS*QoVh9+{{{#S3W4znH8$))gZ$*NUK`?$uU`hl7RxB9Y zJYFy*SQ#&vs^ZY93SyB*!~(xmrthn!#?^aXtJ3RuCvy-3Wa2#vxV&~y+xK3V3F^vSDS#=lk70sbtepzvvT^60^_m1Su>=ChoKGfKNcczJH3$&Z zI8FXFBp*5dY#>X(xq_Ul5^^q$a-JE>`2uFRh;U9fcK=wq@e0vBu&zF;;*{<)rYSJ} zWk_I3m^gjh{q3VMkOV>agG>*V ioRELIa!iK=jA6kL^1sxdmWBk3;T1i8`W`EF literal 0 HcmV?d00001 diff --git a/models/enums.py b/models/enums.py index 099622f..a0fd856 100644 --- a/models/enums.py +++ b/models/enums.py @@ -39,5 +39,5 @@ class GenType(str, Enum): def value_type(self) -> str: return { GenType.TEXT: 'Text', - GenType.IMAGE: 'Image' + GenType.IMAGE: 'Image', }[self] diff --git a/repos/__pycache__/assets_repo.cpython-313.pyc b/repos/__pycache__/assets_repo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3da758830d09a880d62da7a420aab4e594cf2073 GIT binary patch literal 5730 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 f`vRr(V>0 zdhptNkNV$;#mmy1#6xe@9D4P=*(4%3hu{3(oA>6;{N5W>DkX+Xcprjq8e>1m7%gK+ z1}~8uF_*bolQlHZY~s0QuAzgjxZX4xCYXvFO{$^Y?Pu ztx)O|oZ=Z7j+RcZ$%Dv$w)s3xVptCY5hds?tw|{o`9gGKv?}YbJ@EWQz|b-IR73*2 zB#5J-r?@2rsCYlClA%5{21`he*bW2DWsq|<&|S;T?HG4+u0GH9j6DrZ*8rezK~SWw(aymU1?Q+*Ov9R zcq_uVTkjs&U5NLDpUAowEK`-`R-DuU8-P(>Tf-VJlj@yA<5TH$);^v+nO#9%K3#A=FP$trJT$%+<^~wb933Wcg9^bE!Up!)!jin* z!VX|5dNYrniKt31lj5B;Lu@9iR8L~542Fyg@x z$p@0pmi+U6DF8qBe4wi3m4ekBg17_h7_wIhNsX)#Ox0oq_wSHW3i`GWmb8SdZG&SwK`QN$k7MhDrS@~H{0E^u=p&YQe^EVmn~QwOw$w^?GT-$qXrE-hCNMLresV-e#t)oSPu0R#hzV`gf7z; z2Ci{;Fz%y&qVWcPqa7qGuzv_R_yK?fm~I2~7ZAQHKoXs+1C*vUvkup#qtK*XS%7Vc z9Dt|+wmw8seT~|xX_w^s0nD1ixU)&(hDq8rPJcvgAct)r`J~0c9W56CM52O&sko?W zIYgA;QtNOX1V&%A?uG z%jL7v#lb7(aB?e{T;(dh;Jr61Ev^4*>-cEw_Y)saJiPSVWV!9N)v-1H-sIDk&QeS7 zLk?Kc-@EH0p_v-K1MzP92O96>|M)scNz0F+p2?aD!@CQHontL`UR_pAh7e`zI6(tL z!p8g-VBugsOf+|zE9+N2AIqkAreU8ZP+=T$l3D1Wmm5~s|BM2o1C}rz0mCr(D%7;fSA4A{Ur(hc zw$@ehM=HLilCQJU*|XMmFIi7?{iSgKdbk`O+6td3h0knEKRHznU)l;MO5sGs-@M1s zW8r7v8zILNd^z0D$JnnxoS{hA$gYr%z(co`Fqq$ z{iNlUOvb*@=J?UNi^B));?zQgRkS7jmH|eA<=7ajB=W4H&&4_-FcoJYXa41q^`w<* zZx<8Vs&JwGc8!}d#b5G_e9do8C-Ec@;9MpK5uUNV-l5iwy>zy<*je@TJ-rUYN+^e7 zZ9KUXt5!ykh>Tj*_ML_+;3$Lc% z6+h+6&$SmB7?$_r+Y?1BSuq$>jUIY_%?^AQiL*!m6$PcGQ8J~C@`2q+EfM2IS=MI} z>-zbn5-Fz%R5}XPX_snl)SS>&4Yf|gNh;1FL>iVHLD!8rxw{EPbQ#DFoUq%H4F2&vEnB_RrTaz@t_m8pg0i%T&9&)4T5#JMiuf583Oe`(AS1D$3VPTL6G-O-PuwC zsx4coP9Vw>X|CUFK!TQO#|!Gd#|M`)5 zr4iJc>+V&!ja@mu0z+M72XW5%XLezqWxuhJeO5lwHQxV&AsqFVdG;qmP&xhpL+QH% delta 809 zcmY*W&ubGw6rS1NZJNYH*ZgReq{O7Fw9%wd5UEf~6@-WsJ(b3!S?fZQEwdZZlR-f| zC>RD0{s*4z!9&2K_uj%n@t__A4`K_w`raf#9oTQ)eDCe|zIpR4{V82I42P!)ten$x zOoNcGc$h48930;37Z#V2WS(p&X|kbi$`yH(VwzwzVD*YL=JXxIn(DtQ^auB;&gbY` z8sQ)4%`(UnhM>deod$S8hLJ??#xQL5)<;n&_I!;cCxzSnn8{bA#~?6}8i_N&2YRzn z>(tw}hZZuK3`_B4IXfGLlXn83M~3OlyW;!8e!BEhc^zd*zAHaFpNEtsu`FzX@xbXc zYi`E|Vjc*FNe}G{xj7|E<9jQLD$zV&)0P3BX!+72RxTlki4YbUi7gh7%=^dy(`95s1C=d{QN`sd5omR&+90#wKLSOpH2{MVVt{&U@r#yPsM zcVL92yBDGK2ae04f%?$xGH=~dnFT~3@7O-W8L={gf<|K0R*zs@ki~IrnBv{D}wZV#;#(d zG+ejERHjZ0wB!&0ZT}n AbN~PV diff --git a/routers/__pycache__/__init__.cpython-313.pyc b/routers/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..282c6e25cc60afe5c4b193bebea1a7517f89ce2d GIT binary patch literal 159 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 Af&c&j literal 0 HcmV?d00001 diff --git a/routers/__pycache__/auth_router.cpython-313.pyc b/routers/__pycache__/auth_router.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a621a3724e22a4bcb93f91bebc0bcd19654ed8d2 GIT binary patch literal 4716 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 literal 0 HcmV?d00001 diff --git a/routers/__pycache__/gen_router.cpython-313.pyc b/routers/__pycache__/gen_router.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae783b4710f723655fcc6545f312279120eb6c85 GIT binary patch literal 27905 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 Nk>xA@iCi|0{Xd_FL^}Wg literal 0 HcmV?d00001 diff --git a/routers/gen_router.py b/routers/gen_router.py index 38d76e9..fdcfe6a 100644 --- a/routers/gen_router.py +++ b/routers/gen_router.py @@ -236,9 +236,6 @@ async def handle_album( for msg in album: if msg.photo: file_ids.append(msg.photo[-1].file_id) - elif msg.video: - # Если нужно, можно добавить обработку видео (пока пропускаем) - pass await message.answer(f"📥 Принято {len(album)} файлов. Начинаю генерацию...") wait_msg = await message.answer("🎨 Генерирую...")