From 44619647911ce1ddcd510e156f539655140c6549 Mon Sep 17 00:00:00 2001 From: xds Date: Mon, 9 Feb 2026 01:52:23 +0300 Subject: [PATCH] feat: Add `created_by` and `cost` fields to generation models, populate `created_by` from the authenticated user, and implement cost calculation. --- __pycache__/main.cpython-313.pyc | Bin 8239 -> 8512 bytes .../generation_router.cpython-313.pyc | Bin 6019 -> 6145 bytes api/endpoints/generation_router.py | 6 +++--- api/models/GenerationRequest.py | 2 ++ .../GenerationRequest.cpython-313.pyc | Bin 3111 -> 3207 bytes .../generation_service.cpython-313.pyc | Bin 21250 -> 21344 bytes api/service/generation_service.py | 5 ++++- models/Generation.py | 12 +++++++++++- models/__pycache__/Generation.cpython-313.pyc | Bin 2538 -> 3133 bytes repos/__pycache__/dao.cpython-313.pyc | Bin 1171 -> 1286 bytes .../generation_repo.cpython-313.pyc | Bin 3967 -> 5147 bytes 11 files changed, 20 insertions(+), 5 deletions(-) diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 3c3528c202d7f8de365e32c2500348fdb552a926..7e90820705a84a094f374edfea083054b714f425 100644 GIT binary patch delta 2549 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 YcGvNF^N&u$iYPo0t5-D2C-n&IzYZU)hX4Qo diff --git a/api/endpoints/__pycache__/generation_router.cpython-313.pyc b/api/endpoints/__pycache__/generation_router.cpython-313.pyc index c9920bb405d5681b680310fcb2c2049b8f8f2db7..e785c8fa626022065f896e5b82f536f340746ab7 100644 GIT binary patch delta 707 zcmZqHZ!}Q<%*)Hg00cj|J2N8%7#JRdI55BiWqj`1sP4_A5yPp(5Tj_y2oW`53RaF0 z3Kovx4it+ zWekS8256oRnt2B4+?u+ZmoT?-s@~#BE-flb%`1s7Elw>;W`UUvq&a}N8Hhihn(V;a zuM)u=#ET>gWd<|DYz$_xWSV@FSDKM^@>gC*@mm}q1M?DdQ#BcHv4hz0nJJp=lP&lh z6>l-8reG9bbVNECxypfI_gPm0SC$O5S-E}p!ZFUsh;fBZ%N_zMYH zm;JLZ2wGhb%(>2;bAcu2B6rTuVj-ZqCPyhpWyXUtjE;&d2W43ul_wkUe_(W)tSBJL z<_eTAa-D1=@Pf@9#B|@RC1}sc=rK7<$OfonxsW`YH%Q!j@)@CYM#Ie}!jX)MMGior zTg;hxB}En>7E4NIa!C=$Sw&#;+$XOPQDpR`mnbUuKUJn)VG0%m+>W?-`X Qzyy?>yh-dVOA63z03G9+2><{9 delta 638 zcmZoPXx3N%%*)Hg00be6J2E*07#JRdI4}T~0dhW9Y*hDVN-<#!R*n%07LMT#7KsrL z7L5@!;RsfdV#s32Vl;*7Dbk8j3i5{;fS`j_V+4Z*Etz6Cl^A09O&K9FKoiwsRNg7%DzE|V_{*)ZBo77&(aa|emLPc{`!XEfM6S2&VU`WAC~YRN6;%)F8! z3y>tpMG%vmCyR?Js)BT^WcUmu86bY(u*uC&Da}c>D@p{4fg-H9cXO<02cyjmIg`)K zjLwW^ADLMg-5D>anSWpaF%GCh=o@h($ diff --git a/api/endpoints/generation_router.py b/api/endpoints/generation_router.py index 0c3152b..cfdf4e8 100644 --- a/api/endpoints/generation_router.py +++ b/api/endpoints/generation_router.py @@ -56,10 +56,10 @@ async def get_generations(character_id: Optional[str] = None, limit: int = 10, o @router.post("/_run", response_model=GenerationResponse) async def post_generation(generation: GenerationRequest, request: Request, - generation_service: GenerationService = Depends( - get_generation_service)) -> GenerationResponse: + generation_service: GenerationService = Depends(get_generation_service), + current_user: dict = Depends(get_current_user)) -> 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) + return await generation_service.create_generation_task(generation, user_id=current_user.get("username")) @router.get("/{generation_id}", response_model=GenerationResponse) diff --git a/api/models/GenerationRequest.py b/api/models/GenerationRequest.py index 45e9d06..2ad06cd 100644 --- a/api/models/GenerationRequest.py +++ b/api/models/GenerationRequest.py @@ -42,6 +42,8 @@ class GenerationResponse(BaseModel): input_token_usage: Optional[int] = None output_token_usage: Optional[int] = None progress: int = 0 + cost: Optional[float] = None + created_by: Optional[str] = None created_at: datetime = datetime.now(UTC) updated_at: datetime = datetime.now(UTC) diff --git a/api/models/__pycache__/GenerationRequest.cpython-313.pyc b/api/models/__pycache__/GenerationRequest.cpython-313.pyc index daf935434e13a9fa5a26b1cd8f671e0b7a2132a8..8dbd694a79d18f4138b8fb483d20eb0880a9e41c 100644 GIT binary patch delta 412 zcmZ23(JsmNnU|M~0SLZxcV-H)ZR9gzW!$vckyV>fCzz#3B8DSIkQFE!!x}8A4y1u% zl0Y#QsF)Z?EJiBGY;pmcsHBW3BajOOMY1tcKpo=9va$$SIk2n**gVN1`E(XdsmT}E zZVIv_=NFgU;z}+`O)N=GiBGDWynCl*9+Npn;Md5~mN`uz~jv2xec`vi5Om^X%z-Ti0 zDyN->6G$J(D@95mLL5XWg9sH6;Q%8{CR=kUGdfHzoB)>2K LsV^pzd3dA&Y9&}Q delta 369 zcmZpdTrR=)nU|M~0SMezwr8GU-N9B}O912qc3Flo(>9Oc^0!Mba@6 zF|5I&>Od|Ls%5~c#Xzcq#fxOqSu`ak|6sc*$CX@^npl#W5}#Odi>tIC1IBf_XVL-Gp)kosaUIfq9Y06iN> A^#A|> diff --git a/api/service/__pycache__/generation_service.cpython-313.pyc b/api/service/__pycache__/generation_service.cpython-313.pyc index 4d22de674462c30c7d7dc2fa91c6160fbba910f3..7bfe1a36f66e7cfed968431f70e0b71ed015387b 100644 GIT binary patch delta 1266 zcmZ9KUu;uV7{JeW|CP4uR@$}e|J$_%cC{LGUBb#>>0lw14My*p5ZkbIT^WPf>g^_K zG?SWQhhU(Kr<}UJX#u7={2-j zGN#i3)tPmu9a%%xB(fT+DQk;Ks>?&p3THucs_v{c#;G2(wT^Aet6MJBo;9(2cT7-y zy2qpXHSZLs1gmCKUt4$GFjp>UW7Cs)9^2a%iS2K&_24o`$SD#gLDs5Diac8aeZk!= z3u&%vJ2}U)Pex`~W3*e2;}lT4YP;VQ+)d+=C=zZ+O`4`MoTIj(HS+d(&qixeo|yGR;rCOGtn`+5-L1 z?*N|u?D1NpjH`Jf<-xw%q?y*H=Raq5?sm}kTa5HKk1Y};1=2|}kA94mVJ!ulM^j&IWwfeREDO|8=4i?q&40TgpE97PilVcMvYjO0RLd+t{h&>E-b0cYNHdj8U zSE^UPPQjoJT|uFEJ%kQ0REb zu`6P<-IbMfd1x(qa3h*rk0!s39!0ao_hXy$hbMc!vp#;0E{3))3>u-5XdRHrN(T)1 zg%wvqV8*HsSYXu;sLPf?iC=x9>#)q-cEBN*blaKWm{D`VVXstki>L>HCDmkk5X9O+ zj_G#@F#QgQs7pL6S2s-+iUlnG)If0tVkiyoGohVL>!rl712IhV%6I_y|hJqps$lkIy3sc(;h^eax+sU?N;Q4prn!F^gP)&w%wO)b+b8q4o=HPwj!fXkY5fwr2%!|4ESA H0E_(tRT&$6 delta 1152 zcmZ9KU1*zS6vvDL?#lFH>W$BN+o1Ts`+Ls$pYxpO z>B`rYNSGcl&GEEkS_jlO>Wm(3-WLz?V%Vro=TMvZMutfxuwmr~^ zWn21aTh?dVvwBQ1+k@=1!rM%L)*X|~faSICdO^$co2^;9$PS3?kfj984j!79j7T+L zA8wImB|I7Mdf$>5JHdKbR2;%&B}sfTE(iK0c^cgwLrU4O7H-8wrwwm8JR#|&o}x+@ zppj-VFhLY=Dk&K!8qSU;HgpL|H%ibi9OvW#a(Va${dhz$HVXax0!_87~3$;2Vj z(y0~3I@#{#K5O$eM>7_~%`P?5*2qu!DdJR%u7)`Mp>S9vCMxr@zW-m z&c>ot7v}c2Rv&~s3Y@S09a)2SJlrRue3mdsI6+t-aKc`Jx?`5-bCvu|CAV;fyGgDP zULnvK`D=tTgmZ*m!s`Nt#z)C63)Hpwd}*dIS9gm!&vTVRxisTDTb|1o`FTp36Ijp$ zPU2Me)RR&1rjH)5MYiSfEo)n$$Yzs&Tj{dtKSzvO{vm(ZoVP4KKkyK9R}!r5>@ zqFLK;s$_dWByH%CL`>Z{D3RR{WXDvp2js+X^_gC`7cN$(4qpf0_}9PyR4_J})GyL) zyaIJKIUYZHgkQo-gWd2Zemi(D@*V}>CtM(WK%lPfJ|s#*{csMS8k&S9TpIex^^u4f z{+(xB#KQ1*+7e}df;}VCuz+uj?1Rs$UyXe47`;LInm}DXTPX3H List[Generation]: return await self.dao.generations.get_generations(status=GenerationStatus.RUNNING) - async def create_generation_task(self, generation_request: GenerationRequest) -> GenerationResponse: + async def create_generation_task(self, generation_request: GenerationRequest, user_id: Optional[str] = None) -> GenerationResponse: gen_id = None generation_model = None try: generation_model = Generation(**generation_request.model_dump()) + if user_id: + generation_model.created_by = user_id + gen_id = await self.dao.generations.create_generation(generation_model) generation_model.id = gen_id diff --git a/models/Generation.py b/models/Generation.py index 88e87f7..ea4500b 100644 --- a/models/Generation.py +++ b/models/Generation.py @@ -2,7 +2,7 @@ from datetime import datetime, UTC from enum import Enum from typing import List, Optional -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, computed_field from models.Asset import Asset from models.enums import AspectRatios, Quality, GenType @@ -34,5 +34,15 @@ class Generation(BaseModel): input_token_usage: Optional[int] = None output_token_usage: Optional[int] = None is_deleted: bool = False + album_id: Optional[str] = None + created_by: Optional[str] = None created_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + @computed_field + def cost(self) -> float: + if self.status == GenerationStatus.DONE: + cost_input = self.input_token_usage * 0.000002 + cost_output = self.output_token_usage * 0.00012 + return round(cost_input + cost_output, 3) + return 0.0 \ No newline at end of file diff --git a/models/__pycache__/Generation.cpython-313.pyc b/models/__pycache__/Generation.cpython-313.pyc index 7353cf510046b6ccfd67b42b57fa26fbbe7dc4aa..0457de5e7803b2e77c002e89c1e46851b0580d8f 100644 GIT binary patch delta 1295 zcmZvc-)q}e6vywCCE4-s#&Kq;@sBue6NfgQ$Goj|NtO-TVv{~p6bf-komg>QWUIT9 z9ts8LF@cua6N5bLp?^X~*xm+x+VhOZVlFN1p2h}+f?EoMLGMwL;+6{Zx#xbry4OeN zT>0O~ud(1T5bz>)z4Gle?up%C41eF9U0N)8i8qB5qzGkS$xr-FES3YMAPJU2BvcBM za0}-HuW$)5aTtEbNgU7Z-{(5Gt#Bk=5h|jv6(oZ{j%F$<)>B`jSoRXH&ZN9*EfQ!s2GbCH*vKi{V zq_$MDzsfJ;N$RO;>sE8EQKQ}sqE_J**6-Rs3aT#(tDL-(wqH!+Svx0A;)H!syxA^- zcL6Z+6igQZOAK=md<}ptsMoqvb2hJy7xem9Uju`UeuPAFs=bB*S9jH2)+-&;s%W0~LsFwQ5-x-%y8DSja3dQxt0t|y(}E)C?wft>Hj`Mpa$xwu_^5{d1~Jt@DJ??(#1 z`wN56_+hW1pV@gb$p-QXAO?^CGXOUOwLR`h&3Qo?H0OO!L3A1bhmg7&Et9|@p!~Y7 zYs3#u0Khtg$e8134Z|c0Fo%Yb9N<$x9`G3h6*e`!YT6Io(ejrdeg!xOSO!3)$hQFa z;z=102YhfYr$GZi*2azfKixh&HAtklE^puJ7(2~ApB|(#TcwU?H_{FC`HcOSC!X!| z<>x}=B_{}xjygna7`l)+F$CeTT(ISp?1UX0E4G~!sJ{t+493E;VW=j7Az8-hYI%NF)5V7 Mk&g5lvBAmjE!vL@I{*Lx delta 810 zcmZ9K&ui0A9Khd8()4%Iy0*(~SF_Y^7TI*uI?LvCLqyUkg@_jeHLg(#T_Z24*Y4;^ zT5|Cyc=0IoHk4iV4-`c(AUlcRL4?-3@0$+U47^XiKi>D(d+($4b4nP+;}Jx^2X9|C znLuBV@T>m9&6)#>)kkrPJ@i33$nED2cRp8CGV8wQA9t_A<_h3M7^rK2b&7cqZU7*s7wJ52 z7>?Pf8;&1RnkQ*+W&qOw37`Oi6dr3k#1rdvqusU~!)aP=yIv>$QOuw&-Bp0C=K;_u zfqEX>Y&!%-^tdOM)gn>w!~hooVc*kg($tHf^MED5CBS9CGKCj-+OlY)D`2<+xC$r( zt^rmyfN+2079a=s?<8cf0TOiC7Y4t=QM~vyD-V~|VYa%b_ClYy>M8vTSDjn{MBOh< zxsRf2T`#r^Pr_c>v~ANN@Ere{%ywtjJ~1TR7TE;&=Olx4x+^cVPSbT-<_6iOUQCw_ z6a8i}#z#o`hSEo9?guJdgxX>1^7vFYRmOq%Lf zI3(_fDJ&4XEN0lj(#!oBWS%BVkqAh!5Qq>25yF%2GkMo@f|wwm6-fYzTcYvt#U+U) znaS~qB_&0fNu?#J#qseg8H%JpO2k0~4~zi0zE}ZBv@v{SVqrD9AaD4A0Z8wtIKg?* zKIl7$4HkfC2I=$D6uc#zn_rS&qz5MBC$D7ARTL~rEyyp{gZM2T#DV&@q_QA0FTIF! zvOJ4q9N3Xy%N2npAslpz!zMRBr8Fniu1E_g!3e~~nn2tSCH19GP d++|R`%b@XU zNz7zE#tcTD$>ofC9BDxDsSMGKlixFnPCmrw&7RJn$>^uaHra$pn_rXV7E5tzPFfK! zSoh>=rZhGXvsiQTb0$r(s~i%afjkCH<|09m0s#;qG})Zlo0|i~xZLKvaXQy2W9Wo1apelWJF_4&*Wdaj_bZ_`uA_$at5* j=qZE7T?Wm&3lE zt5!-a^#Xc{pz4Jxk%D?jNG{2(q*2Au#8OPGNR`s|&_g94tyB(GI}hxTcBJ>s%$s>L z-|WsmhremHzO-0O2;|crv#IyZt5zqLe*yOfiX#mxNMjVNv5J#~@G0yfSFnGqQZdDi zagm2C#SE5t&$7L`J;8Hsw&dSQ|vUZNeUMi0*vC=^A0Jp zW}h;8tM31b+YJ>(L-OM2M>C1k_!vi=rp8K6#iJZTL$ZR8LKT(S^O{M-)Je{XMpDg7 zWYOenZ32oxcu%EuU!ifxi?|UQ=`IE6 z9Oa~QOaw@qpK zz~w62oJwo7rbZcfO_~6zXh`Xa%gKwWaSet>T985)g~K$;PEE{d$500RZhC11@EZ2l zBuoU^wqg-n+1J;_=6YzM&_5L%HEZq3?sd ztL6HKOP8OzT2?P)O*vR(G5AeK9`pV z3)aCbyV-gyYu*-6Ae6U@#VxT65$x_@iH(%#BW@I#;%q>{XHF1STs9A7Koc{9IU-Uz4$f@e>1xERq0f001prIaL4v