From e7a42c9c1e56fceb0223e74e943157b105af1d3a Mon Sep 17 00:00:00 2001 From: KalpanaBhaskar Date: Sat, 7 Mar 2026 18:34:02 +0530 Subject: [PATCH 1/2] feat(monitoring): add prometheus metrics collection and grafana dashboard --- backend/__pycache__/main.cpython-311.pyc | Bin 2734 -> 0 bytes .../api/__pycache__/schemas.cpython-311.pyc | Bin 6245 -> 0 bytes .../core/__pycache__/executor.cpython-311.pyc | Bin 11054 -> 0 bytes backend/core/executor.py | 42 +++++ backend/db/__pycache__/crud.cpython-311.pyc | Bin 10458 -> 0 bytes backend/main.py | 10 +- backend/monitoring/metrics.py | 52 ++++++ docs/monitoring/grafana_dashboard.json | 149 ++++++++++++++++++ 8 files changed, 252 insertions(+), 1 deletion(-) delete mode 100644 backend/__pycache__/main.cpython-311.pyc delete mode 100644 backend/api/__pycache__/schemas.cpython-311.pyc delete mode 100644 backend/core/__pycache__/executor.cpython-311.pyc delete mode 100644 backend/db/__pycache__/crud.cpython-311.pyc create mode 100644 backend/monitoring/metrics.py create mode 100644 docs/monitoring/grafana_dashboard.json diff --git a/backend/__pycache__/main.cpython-311.pyc b/backend/__pycache__/main.cpython-311.pyc deleted file mode 100644 index 3ec214689e670fc1c635bcf699f4c4efec4cbcb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2734 zcmbVN&2Jk;6rbH4eNEjVHL6mohC)f0xB3YZANxaQ^cQw0i zTAxygM8zo*m0F=5dJ3TS58%KbVON$|tyHOk3!K<0^pX>A)*o?FRHTk)-p<$W&Aj)1 z^Za8X5l2vd*jd$n4I}giAGAue+1a}$B6JTClt)A$qArxgyeRO#q)R27$3Y+Kp;9;> z4*DTIQi|rIrC2^zis$2{u6$Q8AJ)4|iF`spG)y8?ToXxj8}Gs~k6LGmunRsda(3$LZY=V4*b%oZR!UuQlrY z(&+V6YrHD}23n)M0uy@;O#C%4U9ZHXTQ}|elFVT%NdGoE;^_?d;=i#2N3er0Vf*O- z8QK6|AjC$13B&qeeh?xzczvjK3)(|Q$SCQ1JhqF$pU@fwm@vE?wCs50Z2Sw0C=Mm^ z1;uvGU0ISZDVialvn*Xx6-P4-c~f(W@}lA>tBOtKB|~#GMb|39V*D?zGb?zJCLFrS z^PgQ^zN8VN(@lj@m`N-4EkjjJ!=X2wl42<9lx49Ovowq9nn7(ZPH$4R%#X9ZNQpX3 zQ*BRFG_F~L6ANV3lN8HR+pK_NCEmaVd*4ClA`;+@I34RmIP66MIULa}hhqV2ok;O0 zl1N{nFVQ{W$Ugk?zGajRRo?tmTcft67&5&<4ab&G$hM;}r)am&=uT6n}s4{ej=P7XrUia=FsG3>usO18QJ@R`fy^(+r34bdnWV7kq4P zlDz-+;{3`YwKp8oTDhRpo7#!1Cd8Vul~qOEpaxkfLCAB~EibA!ug!V@+09ja4&qPv zZJ`%Y6pB~llQle5$5T~2^%QsAUfN#TT6&6Oo%7FdZ0n=O??pJkjMeaX9gkOAbq|!H z{odd*&AZ4zy^4ILNeKB|*)dBE`d!f#XU$|KzGFW$n>(31xgVQRENzCEs$Gc(T}EM$ z4SOL+b9Cx;5o)VUvpAu=$PLPD-idNRq0DqI7VPKUz|u;8(Q&N#nHk+w6}@QM&in_n zr`~H%&(59V$L3~(>1&=a4eU)Qx^8Y3Os1`Cz}mjXPzB8pafp=N+5vPfnxs7}QiZ^U z_JtXhyP;Khiu?xJUIs*i%skl>iC_|XfPq%wO5&<%Ix+bE4W&fc0AC6N@tm^lVQ&1-h)r@3&)zJ_moWpCYzn&XL$i;AXoTC3qQ2v9dc;kQ zy1k=*EYuzLQA32nfr3h825ixt;vqXE&=AO8+MTMplbd-Dun5MH;GZ zc`-;622~*=WkogtIv{M#chR`}5d*p#z(SypO#|R8Gpr(ByK`OG)?fL zg#sskp>VkpYe!l8@ECYe@-SzDhb@yiUYJqcR7iH%ixdjr%0j`5tidY+9x`sSH`pBI zO6|;-Q%z7UTQUt$k!Q*Y*Q{ z;{kDlXYM5xLJ9}RdP&Vt^)jJ_Kre&4bN$Sd)*-R-PzP?}wsV?rOUgGixU1nwz@sA! z*^iznnWU`K1$G9YJf+xwgY+dq5PVS-u#dP1BVnwHMmwvE=BkIQi>9lG>oat&`hV3$ zZ&eRhI8E&L#klas{fLj?b0D5euv8Z^!Ky!oBFU|FH$LX}&bi%bH!~x1_}5#lzM_vE>5}W>FT@ZtEokIV8l%i>`Vp1??OWQISLB^0^tWD)yD0d zuBVUJ&}1D=R?(!3q=vek`FeV?hNkLhs*0u#rcd2nsG*5Eny8|QgW0*er)y}uj>fBK zoLd?CcJS-LJL%hF+he{M5rDuTxQ)ryM5eWAxF)3QLfRKYd>aV9Ez_RqMY#J;<%u+0 tm4?B@t;_u9;-0OKw=eEU_uu(x@s~^$FVyft9WOwqQT-6YBY+5A{J+)np?v@V diff --git a/backend/api/__pycache__/schemas.cpython-311.pyc b/backend/api/__pycache__/schemas.cpython-311.pyc deleted file mode 100644 index c1017ec9a161871f1c43b140962c8d5543da1ca4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6245 zcma)A&2!tv6$i;B36kK4zG>O=u$C2@o=A?8xYNc?(!{Q1xz@*6cH3|~#lXbM<^=o* z0D3HUCZ#$#=-}z_;Awcs!6&Dg^x#wfh91Eg9WZ;!Oi#VZW1Vv9dkc^t2udooc-q|` zu)x0edmq5}>2y+npZv|k%1_Yy4-R_2sM~q_*N`B5E2u(AP(x~13su9Va5YkjR7r_c zqorsF=ObFI8Y{(kpJ?%_SQ4v=Qi6}8TC$odrK(a%3JGChMNnhk2x^>)uOfo*BOK1J zl;)TSSR#OBIFH!fz)l6QX^zbTb~=E~sAsl>;@q?4w4j{&DDZV zziLv$Gzv;hg@Mti*9>YT|BIR}5=QP+43m)?m9ohqpVfAlxX~~x^_rp;Lrhc^lbV$( zErwa*bH$)*b(Lx?db2_`73MOVl?K%+HM(UgX47CP`2WZqlwI5Lf5aWU{TpPLWm&D4VLD=%I?KwkQmfTL-*6*Cmh~KLQ%6tHQOP>`rk()7 zhzd5yk-(BHvos+p>4@Un0c$ivhxCG>)u>rEgRfk~Vk{Cb;Cq=lF00gK+V^kVVIp2DmqHw1)Ty@Mf zH15z;4Fl=o9_j8k(93+JYvPojbnAKsx1U8Zh5`*+%;;0tpGGl*B9DTf8N(hvr#_3q zyKsmrxQ>1X1=sIc>=k+fS2z9wVuami&$e#tU2K2we9@ew`xgtX-?j6d`4@8* zS%B6h3-;o2>*n6A_Px&ej`8w(=jnG#`%}ez>5@g3p>@f!eQ62I{Qv_38CkBV>yA$+PcRpESZ*Jr zzZ39>>~jGD6qn#IJ_F&m5BT0CU%38%JrLI%t;$z`9IZ--q){jCqC$P1;&|imY%lF6 zeCP5s4=W_WUsFf=EB8wDSKqaR34fZ`DSd18y>EWNUOa_+mhzlXves-D4k&C6zJ3QLKfZD>WVp#;)g7bhSpKM z2Zyl-!f$bi(TjFwthLr!+g-Dz8H-@+k{LUfcijm*>Rgwcv&UyUzuF%^ZxL)=a^B8N zx0}!F;DN3aN|O-G=Q~E{;)_4+O9LmSz+<0(+)w&W$MHdYG#_y==-}_|zd#&A9IOp_ z!X60SNTwb^+}aOnytvQY4hcl+ClDurTPG$0bz+)kd~5Jfjn$)XHpK(D1RkGeV?hqh z4REOM=Y96*^RQk0A_$i5yFM0r`nw1$qF6$48O1mVmUH;lU(3*!VS2roJ%K?6>-6&e^!pZp(Po!y+Q+aBAAB_15IKfz z@Xq10wfio@qx)3f)_3ZUjDE~GHPYT1oWi8O%`jRJoLeu(PcR!d$&h#d8lGB5feFh% zYWS@&Z;^R>e99u&x@5{uX7-Hd#nwh^V;7FrhU0{|!f3T1rH@rh&ptiDG0xEFl>1>NUB$N^j}-DUNCf`qN6NaakR2(fE=T&<0BTvE+>uQc}|qV z9*;~s#_`e-(pOYz6Rm99KJ!=iA?Ku>-O$#Tk2)6+yK#M1|rPVV1)6E zYL6e$Kf>_%K%))+2_T~kKM*ov?uU#xjrHrDnCcXXQ7~kTO}mD?1wk0B(~l#wBkcLu zIfunv1G|-mjBdk3cwy(gqb$H93D+@y103Z$Yp^37g$l=rLJ^a@ogQmn-cMtB;8qBO zp*-V-dnfKY-h*q7RwCxPQA%_a=hoa&pUQyyh5;W``nt-m&>Dqc1M!6e9`Qj_eC8gD zyDUCRJ7_fL)%YIt7nA8G#O}YzOyW0>d;KgoSpMJ|>%}~O zJ!dg*Wzb1VcasWqw{h_TSM10g8dU%bKDLk}8{hAtlnL4jw&?4bwc5}`{6 zLQu4K-r($^2hB!s&7f#k-r($^2T5Y+)PWEb9k@Hp9(vGp63+~ZPT~#D9(oYmeG>0J zEIO-r_es3F3uU6AJnns1w2Qd+Jnr3va!Kq31?~uD@$N1@OF}aTLQvq2VD`|1auM87 OP&itG9R(pCCH@Dm%#u3* diff --git a/backend/core/__pycache__/executor.cpython-311.pyc b/backend/core/__pycache__/executor.cpython-311.pyc deleted file mode 100644 index fcd9e7b7af9b33246e95af9063f2b846c4d41349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11054 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 L{^U8egna%RcxKry diff --git a/backend/core/executor.py b/backend/core/executor.py index 11c9b47..dc79c36 100644 --- a/backend/core/executor.py +++ b/backend/core/executor.py @@ -4,10 +4,18 @@ """ import traceback from datetime import datetime +import time from typing import Dict, Any, Optional from backend.models.pipeline import Pipeline, Stage, Execution, ExecutionStatus, LogLevel, StageType from backend.core.pipeline_engine import PipelineEngine +from backend.monitoring.metrics import ( + pipeline_executions_total, + pipeline_failures_total, + pipeline_execution_duration_seconds, + stage_execution_duration_seconds, + pipeline_active_executions +) class PipelineExecutor: @@ -36,6 +44,10 @@ def execute(self, pipeline: Pipeline) -> Execution: execution.status = ExecutionStatus.RUNNING execution.add_log(None, LogLevel.INFO, f"Starting pipeline execution: {pipeline.name}") + # Track active pipeline start + pipeline_active_executions.labels(pipeline_id=pipeline.id).inc() + start_time = time.time() + try: # Get execution order (topological sort) execution_order = self.engine.get_execution_order(pipeline) @@ -60,6 +72,12 @@ def execute(self, pipeline: Pipeline) -> Execution: f"Pipeline completed successfully in {execution.duration:.2f}s" ) + # Record success metrics + duration = time.time() - start_time + pipeline_executions_total.labels(pipeline_id=pipeline.id, status='success').inc() + pipeline_execution_duration_seconds.labels(pipeline_id=pipeline.id).observe(duration) + pipeline_active_executions.labels(pipeline_id=pipeline.id).dec() + except Exception as e: # Handle execution failure execution.status = ExecutionStatus.FAILED @@ -71,6 +89,13 @@ def execute(self, pipeline: Pipeline) -> Execution: f"Pipeline execution failed: {str(e)}", metadata={"traceback": traceback.format_exc()} ) + + # Record failure metrics + duration = time.time() - start_time + pipeline_executions_total.labels(pipeline_id=pipeline.id, status='failed').inc() + pipeline_failures_total.labels(pipeline_id=pipeline.id).inc() + pipeline_execution_duration_seconds.labels(pipeline_id=pipeline.id).observe(duration) + pipeline_active_executions.labels(pipeline_id=pipeline.id).dec() return execution @@ -83,6 +108,7 @@ def _execute_stage(self, stage: Stage, execution: Execution) -> None: execution: Current execution context """ execution.add_log(stage.id, LogLevel.INFO, f"Starting stage: {stage.name}") + stage_start_time = time.time() try: # Execute based on stage type @@ -107,6 +133,14 @@ def _execute_stage(self, stage: Stage, execution: Execution) -> None: metadata={"result_keys": list(result.keys()) if isinstance(result, dict) else None} ) + # Record stage success metrics + stage_duration = time.time() - stage_start_time + stage_execution_duration_seconds.labels( + pipeline_id=execution.pipeline_id, + stage_name=stage.name, + status='success' + ).observe(stage_duration) + except Exception as e: execution.add_log( stage.id, @@ -114,6 +148,14 @@ def _execute_stage(self, stage: Stage, execution: Execution) -> None: f"Stage failed: {str(e)}", metadata={"traceback": traceback.format_exc()} ) + + # Record stage failure metrics + stage_duration = time.time() - stage_start_time + stage_execution_duration_seconds.labels( + pipeline_id=execution.pipeline_id, + stage_name=stage.name, + status='failed' + ).observe(stage_duration) raise def _execute_input_stage(self, stage: Stage, execution: Execution) -> Dict[str, Any]: diff --git a/backend/db/__pycache__/crud.cpython-311.pyc b/backend/db/__pycache__/crud.cpython-311.pyc deleted file mode 100644 index 82f8ab911909c4a5a55a93589be77bc42a06b2dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10458 zcmdTqOKclScHLx?B3YC~iqy~gQL-$Hk}Z2YKaW3SuPs~t+Zo%5Mc)2zU;%f?y6w z-m4~?Y?9tM$mEcM`0Dqn>UF(uRq0Qgn|%VLe>{{({%5Bk{1aBnsku~mG2#$}yMioC z3$iFXQexUM?MOSPog&tqDJkumcJZ>5a;M$X?sU_16R*2cp0sz`oAyon(qx*X{nP$* z^K>)sbEjI;t<$aPz;qxToDM=;lj4#+N~`QuymM~ZcT<{f<84F9bbZqy%AgGUt(dpuOK} z?*$6_I2HampWF|916CjIg5O5j0Q5)X?HpqWMr~FGZv>OM&xgZa0}gXe|lj+K7j;SVlOM$RrNG@ean7S+gHhDIji+4yW+ zReawf52Fr4I+awjhUe@O;>1%?(P%oas47&T(<8^TN;a8R3@<`iNoC_vm*G2?TvAfW zMP>4kL5^Hg63aLkDuMIa_`Fix;yRU?hYrtaB}rk&xcJhAixZQbPcqBX$4PQC~f)bOL(@T#WhGSx<;Zjj7RNVDQ&qOA@^#01` z1pl5``Aa7Dr80?lN_`&+Wu$gk?kpENx8^&yYMoKm8MQiI;8y{S zZj(O4do@mz@!6E3f{;6svLP+TLH3EPs0o_n8e%kERVZqQ;gc=#jru5ZkPac(i~uR1 zTM$GL3<5BmaalIp@G0qJ)@Y*2997hXsFMZ|BZwG8p0z$sMY)VbBD4p4T5CR?jzcT@ z1@#tyHQ}GzdUX=Iy;pB;zkOK`_pr>X!PC>Bi%6e5# zWn49WwKiYj*fldC4Ry&b%Ah;qO52o@t*FfG^~mu_kO|kP%L={DnQge|k|_{c&UC{y zm!xVo>M)tP4OS>gyU9NPyppvzOW%O*0YrHlz?$%!H2-Y(m%D#?LL&o=3>3&no{ZdI z`TE8N$!|TZkx51-3*}d6x<+X)co3#GA`yRx7kEc@B-h(RA37EIE>6y7X6uHJZ+z6S`yh_nyDD|UW8tC4MtY%7pmd9v&2+ZuU~k@pH@f1d2u$N@$UguM4DA-?sxww8JF3Ln8M8K? zJ1a-UdDS^9N5zq$vw}I@KrK(%)^bS@KC#@8T_FJ9ay+24JW#ESGE4Rm+*y%Y9IUT8 z?cxKNPEa`KO7P+m1{;yM#rf~sIZ^Ztv;#pGfNBM$z0k0-d6|=7DQYACg1Srl5TLE2 zsK!x0*XUOoema7fxZ{R4FOGmLcit|Vn>%sj29fMXC1q=K@*%8#x zNZ-eih<6VFI)eUtbbYVZwu80p088D}Rup`$h~BgLtNphK^K|J$V{GUhEDq@XBdmV{ zi#@-~O$?!%81f-r|G#>Kp|P(Aw833$a2IdUJ-(lLzw~}VZj)OCE50v%clK(Y0azdh zYg8OVV_&caWdFD7;#hUiaS%OAMPQJXYHbY{K1*pVG$_;xsVtrsWQ>(S{3ex@ViUg$ zN5MBEyA3ylGtkF5Rrc3t;!Xab8dlh2qFN$Ee*nFE;HMq}(3m|=SGcr^cg8hOKkRbO z({JC_*3hl2P6D^yHa)CYDaYKlJw=7%CdglJg8ql`Z*}Zj9qU5bifW;384M^}CgO?s z0LqW8I*)}L8jXgWYiKrVIR~=fhp(+m4Q53CI#z~kv9Ps&)xmJo%Y8XT)*8)L5W_=2 zj1aTSs^Np2W))Oi!;euZ3I?xvR}eItkz2r-%Dk zII4FHu#W9|YZq%B&_lf}v{i5KXYJ#9a1#q|)&t!vFsQe5-p&+5zA!|H0A3Xu$xvCLvDxcfRSk)s1o3>#V@>)lmUaWM>tPEJIdY2+9q#|q?Z9+-OWf<`VfaLLZ&Ej|Oj#Z73hlwWamV&@-s^OggG&$H4ioPf9zD#w_M z6@G)&M~yS}lpEzn_SiDT^dunLxg98lWER6Is_!h~T?K$vH?rQg{^8d%T4)yw?OHpf zH-(BqlWS1#---d49|ACacsm>3gT+BT5@nHHSnSgWM%lnlEcWVsBVWa^w8SLi%y?ubSZ?m z$11l!yEQ4yq;O6OH}vh-Dz2Y|)=aMh7ZJ);C1o_F;SL!@FJT!0AP%L3OC55qUg}nz zASKZM7;$*IA-yu@m+a2t^<#dR(VD-!4(6-%GfF&iY_p^!m6?wyi&=Ud4gs8BF&IZb z1^`^du(c*^gPRL}w`a$N4T7XScHo7<45Eg?P7 zS!|M8fL{P!B3L_G^p#_=cYb{4=9#rK4ViUSlRB7$mvlM_uATlJp5pIe$?xyF=zo7X zXPdz?G&gSu>witNkStEBT7zE%=lS2V~{j|P>E%)FDZuWYCN^97}7GlrWmcI9?0T& z&!4dN3~0c8Lh*m`1VAGdT$Pr@qyUO2fg-}sOgCJwR797Z6W@a(*3anTir8)`qWOg- zU6kko5J{5=K0$B=04OA$4{H_DZ7O7g0frZ}(wtYtP2!4Kx^rnRX{Omt7oqzu{M3&D zfN}=9^zZ;GXLC^R+M4Uy{%}{(>k5LB0(gmF?Re2&R?_!0shvsfIjOy&o?h3aZYFi- zq;5E#sZUSA}*k_%Zyo+57hK`k_LUol?2Tur8CgYDW$ilUcdT&bFY!1sM>FY23f`R} zXCi!Kqas~`;SdiZ|0&)OQ@raiO#+uDZW0u1LzBSmK>0ohhn8*!V)#^Q4-rFX8%it3 z7T~Qa1Fr}w47m?K6=wuF3U>d=fEFBL!J+#fKE0URvRB)(mjMj!T|1$d-UyCqq>qul z0vXPe;ro}^mUlPy(@RLR?Y-9Y-lJlb~dbA7R90f3U-_u;yj9=I4NAdI|EelP_Y`_Dpz}k8d~tX z>QXg+Nd_$#mi8Y=a#0IUf`w-D~QyZaZr ze|3!Y?$knWu+STY(4Kr~j~3d;Li-A#gZa=wEp&*54y~Pf-rfnJ&!&;0;Bt*|@I}k5 z9z7Vk+x3gCUyW+P&2Xnx2#)20WB=d}Gk;|LWZu8yw@yc!@1+3XUwmy}j{b5t3vDTc z#`2*tEwqh=wrRfY%(wl|l|NG+%|Dqhx&&`4kmZL*XFm2=II%4IQX%bA4hon%7Y(x z7)@p1u2hnfAY14;x!I%0YV$p&A4~Yg{henY(6yXW-lu;IRkTv-U&1~VouVif9S%_{ z3JBbSIFu6x>;H5C+HHTjFq&)h_guJ?d%Zth_%QeCzoMsA?7Y`h6d=ou@8|h55I(5Z zf?p&0Y zPfIy$0bz$Pn2imfkC+rf#wmpS+3s&nvcs2(0+ee(f2$b22TMVQD?s+_k9d}w{)FeX zV8kc3-FX+D*~GSon|X%gp!V!A&%Qarvs$ppAxR8VIbxk!yC)L88^!fpr45C zNN+oe17r_j1;{pBJbMOv3bowFz$wg^uJODU^g7`&2%hu+-UHdg9OuD&BfPQjn|`z0 a0Q#H7O-M0h>)D6LzJ`5);wu3+g8v1JCoGTv diff --git a/backend/main.py b/backend/main.py index 0eda646..85dfff4 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,7 +4,8 @@ """ from fastapi import Depends, FastAPI, Request from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse +from fastapi.responses import JSONResponse, Response +from prometheus_client import generate_latest, CONTENT_TYPE_LATEST from datetime import datetime import uvicorn @@ -71,6 +72,13 @@ async def health_check(): } +# Prometheus Metrics scraping endpoint +@app.get("/metrics", tags=["monitoring"]) +async def prometheus_metrics(): + """Prometheus metrics endpoint""" + return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST) + + # Root endpoint @app.get("/", tags=["root"]) async def root(): diff --git a/backend/monitoring/metrics.py b/backend/monitoring/metrics.py new file mode 100644 index 0000000..e4ce94b --- /dev/null +++ b/backend/monitoring/metrics.py @@ -0,0 +1,52 @@ +""" +Prometheus Metrics Definitions for Pipeline Monitoring +""" +import os +from prometheus_client import Counter, Histogram, Gauge, REGISTRY + +# Disable the default process/platform metrics for cleaner app-specific dashboards if desired +# from prometheus_client import process_collector, platform_collector, gc_collector +# REGISTRY.unregister(process_collector.ProcessCollector()) +# REGISTRY.unregister(platform_collector.PlatformCollector()) +# REGISTRY.unregister(gc_collector.GCCollector()) + +# --- Counters --- +# Tracks the total number of pipeline executions, labeled by pipeline_id and final status +pipeline_executions_total = Counter( + 'pipeline_executions_total', + 'Total number of pipeline executions', + ['pipeline_id', 'status'] +) + +# Tracks the total number of explicit failures, labeled by pipeline_id +pipeline_failures_total = Counter( + 'pipeline_failures_total', + 'Total number of failed pipeline executions', + ['pipeline_id'] +) + +# --- Histograms --- +# Tracks the distribution of total end-to-end execution duration for a pipeline +# Buckets: 1s, 5s, 15s, 30s, 1m, 2m, 5m, 10m, 15m, 30m, 1h, +Inf +pipeline_execution_duration_seconds = Histogram( + 'pipeline_execution_duration_seconds', + 'Pipeline execution duration in seconds', + ['pipeline_id'], + buckets=(1.0, 5.0, 15.0, 30.0, 60.0, 120.0, 300.0, 600.0, 900.0, 1800.0, 3600.0, float('inf')) +) + +# Tracks the duration of individual stages within a pipeline +stage_execution_duration_seconds = Histogram( + 'stage_execution_duration_seconds', + 'Stage execution duration in seconds', + ['pipeline_id', 'stage_name', 'status'], + buckets=(0.1, 0.5, 1.0, 5.0, 15.0, 30.0, 60.0, 120.0, 300.0, float('inf')) +) + +# --- Gauges --- +# Tracks the current number of concurrently running pipelines +pipeline_active_executions = Gauge( + 'pipeline_active_executions', + 'Number of currently running pipeline executions', + ['pipeline_id'] +) diff --git a/docs/monitoring/grafana_dashboard.json b/docs/monitoring/grafana_dashboard.json new file mode 100644 index 0000000..ece18f6 --- /dev/null +++ b/docs/monitoring/grafana_dashboard.json @@ -0,0 +1,149 @@ +{ + "annotations": { + "list": [] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [], + "title": "Pipeline Success Rate (%)", + "type": "stat", + "datasource": "Prometheus", + "targets": [ + { + "expr": "sum(rate(pipeline_executions_total{status=\"success\"}[5m])) / sum(rate(pipeline_executions_total[5m])) * 100", + "refId": "A" + } + ], + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + } + }, + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Active Pipeline Executions", + "type": "gauge", + "datasource": "Prometheus", + "targets": [ + { + "expr": "sum(pipeline_active_executions)", + "refId": "A" + } + ], + "options": { + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "text": {} + } + }, + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 3, + "panels": [], + "title": "Pipeline Execution Duration (95th Percentile)", + "type": "timeseries", + "datasource": "Prometheus", + "targets": [ + { + "expr": "histogram_quantile(0.95, sum(rate(pipeline_execution_duration_seconds_bucket[5m])) by (le, pipeline_id))", + "legendFormat": "{{pipeline_id}} (95% CI)", + "refId": "A" + } + ], + "options": { + "tooltip": { + "mode": "single", + "sort": "none" + } + } + }, + { + "collapsed": false, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 4, + "panels": [], + "title": "Global Pipeline Throughput (Executions per Minute)", + "type": "timeseries", + "datasource": "Prometheus", + "targets": [ + { + "expr": "sum(rate(pipeline_executions_total[1m])) * 60", + "legendFormat": "Executions / Min", + "refId": "A" + } + ], + "options": { + "tooltip": { + "mode": "single", + "sort": "none" + } + } + } + ], + "refresh": "5s", + "schemaVersion": 38, + "style": "dark", + "tags": [ + "flexiroaster", + "pipelines" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "FlexiRoaster Pipeline Monitoring" +} \ No newline at end of file From f863db350f73336e2aef675a4bfeba09fb330903 Mon Sep 17 00:00:00 2001 From: KalpanaBhaskar Date: Fri, 13 Mar 2026 08:52:44 +0530 Subject: [PATCH 2/2] improvement-1 --- backend/__pycache__/main.cpython-311.pyc | Bin 0 -> 2734 bytes backend/__pycache__/main.cpython-313.pyc | Bin 0 -> 5953 bytes backend/api/__pycache__/schemas.cpython-311.pyc | Bin 0 -> 6245 bytes backend/api/__pycache__/schemas.cpython-313.pyc | Bin 0 -> 15382 bytes .../api/routes/__pycache__/ai.cpython-311.pyc | Bin 0 -> 11656 bytes .../api/routes/__pycache__/ai.cpython-313.pyc | Bin 0 -> 10022 bytes .../core/__pycache__/executor.cpython-311.pyc | Bin 0 -> 11054 bytes .../core/__pycache__/executor.cpython-313.pyc | Bin 0 -> 12423 bytes backend/db/__pycache__/crud.cpython-311.pyc | Bin 0 -> 10458 bytes backend/db/__pycache__/crud.cpython-313.pyc | Bin 0 -> 9534 bytes .../__pycache__/metrics.cpython-311.pyc | Bin 0 -> 1471 bytes .../__pycache__/metrics.cpython-313.pyc | Bin 0 -> 1307 bytes 12 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 backend/__pycache__/main.cpython-311.pyc create mode 100644 backend/__pycache__/main.cpython-313.pyc create mode 100644 backend/api/__pycache__/schemas.cpython-311.pyc create mode 100644 backend/api/__pycache__/schemas.cpython-313.pyc create mode 100644 backend/api/routes/__pycache__/ai.cpython-311.pyc create mode 100644 backend/api/routes/__pycache__/ai.cpython-313.pyc create mode 100644 backend/core/__pycache__/executor.cpython-311.pyc create mode 100644 backend/core/__pycache__/executor.cpython-313.pyc create mode 100644 backend/db/__pycache__/crud.cpython-311.pyc create mode 100644 backend/db/__pycache__/crud.cpython-313.pyc create mode 100644 backend/monitoring/__pycache__/metrics.cpython-311.pyc create mode 100644 backend/monitoring/__pycache__/metrics.cpython-313.pyc diff --git a/backend/__pycache__/main.cpython-311.pyc b/backend/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ec214689e670fc1c635bcf699f4c4efec4cbcb5 GIT binary patch literal 2734 zcmbVN&2Jk;6rbH4eNEjVHL6mohC)f0xB3YZANxaQ^cQw0i zTAxygM8zo*m0F=5dJ3TS58%KbVON$|tyHOk3!K<0^pX>A)*o?FRHTk)-p<$W&Aj)1 z^Za8X5l2vd*jd$n4I}giAGAue+1a}$B6JTClt)A$qArxgyeRO#q)R27$3Y+Kp;9;> z4*DTIQi|rIrC2^zis$2{u6$Q8AJ)4|iF`spG)y8?ToXxj8}Gs~k6LGmunRsda(3$LZY=V4*b%oZR!UuQlrY z(&+V6YrHD}23n)M0uy@;O#C%4U9ZHXTQ}|elFVT%NdGoE;^_?d;=i#2N3er0Vf*O- z8QK6|AjC$13B&qeeh?xzczvjK3)(|Q$SCQ1JhqF$pU@fwm@vE?wCs50Z2Sw0C=Mm^ z1;uvGU0ISZDVialvn*Xx6-P4-c~f(W@}lA>tBOtKB|~#GMb|39V*D?zGb?zJCLFrS z^PgQ^zN8VN(@lj@m`N-4EkjjJ!=X2wl42<9lx49Ovowq9nn7(ZPH$4R%#X9ZNQpX3 zQ*BRFG_F~L6ANV3lN8HR+pK_NCEmaVd*4ClA`;+@I34RmIP66MIULa}hhqV2ok;O0 zl1N{nFVQ{W$Ugk?zGajRRo?tmTcft67&5&<4ab&G$hM;}r)am&=uT6n}s4{ej=P7XrUia=FsG3>usO18QJ@R`fy^(+r34bdnWV7kq4P zlDz-+;{3`YwKp8oTDhRpo7#!1Cd8Vul~qOEpaxkfLCAB~EibA!ug!V@+09ja4&qPv zZJ`%Y6pB~llQle5$5T~2^%QsAUfN#TT6&6Oo%7FdZ0n=O??pJkjMeaX9gkOAbq|!H z{odd*&AZ4zy^4ILNeKB|*)dBE`d!f#XU$|KzGFW$n>(31xgVQRENzCEs$Gc(T}EM$ z4SOL+b9Cx;5o)VUvpAu=$PLPD-idNRq0DqI7VPKUz|u;8(Q&N#nHk+w6}@QM&in_n zr`~H%&(59V$L3~(>1&=a4eU)Qx^8Y3Os1`Cz}mjXPzB8pafp=N+5vPfnxs7}QiZ^U z_JtXhyP;Khiu?xJUIs*i%skl>iC_|XfPq%wO5&<%Ix+bE4W&fc0AC6N@tm^lVQ&1-h)r@3&)zJ_moWpCYzn&XL$i;AXoTC3qQ2v9dc;kQ zy1k=*EYuzLQA32nfr3h825ixt;vqXE&=AO8+MTMplbd-Dun5MH;GZ zc`-;622~*=WkogtIv{M#chR`}5d*p#z(SypO#|R8Gpr(ByK`OG)?fL zg#sskp>VkpYe!l8@ECYe@-SzDhb@yiUYJqcR7iH%ixdjr%0j`5tidY+9x`sSH`pBI zO6|;-Q%z7UTQUt$k!Q*Y*Q{ z;{kDlXYM5xLJ9}RdP&Vt^)jJ_Kre&4bN$Sd)*-R-PzP?}wsV?rOUgGixU1nwz@sA! z*^iznnWU`K1$G9YJf+xwgY+dq5PVS-u#dP1BVnwHMmwvE=BkIQi>9lG>oat&`hV3$ zZ&eRhI8E&L#klas{fLj?b0D5euv8Z^!Ky!oBFU|FH$LX}&bi%bH!~x1_}5#lzM_vE>5}W>FT@ZtEokIV8l%i>`Vp1??OWQISLB^0^tWD)yD0d zuBVUJ&}1D=R?(!3q=vek`FeV?hNkLhs*0u#rcd2nsG*5Eny8|QgW0*er)y}uj>fBK zoLd?CcJS-LJL%hF+he{M5rDuTxQ)ryM5eWAxF)3QLfRKYd>aV9Ez_RqMY#J;<%u+0 tm4?B@t;_u9;-0OKw=eEU_uu(x@s~^$FVyft9WOwqQT-6YBY+5A{J+)np?v@V literal 0 HcmV?d00001 diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d28e32d26dd6ad92a280a9223b800000100c470 GIT binary patch literal 5953 zcmcIn&2JmW6`$oUKSYrdMT(+6?TsZ_qGCz@j4gk~vPH`xWl7~qjsr8+-vH16MgZ1+;7V5iGlba9yDd&K68ig zkU8exXAE#(h}W^t9J-G~_)z>XKFp&OdjzEd$t#x0>>7Hn+AT{GI-!5gYT0!M5(1|j z&mHS}!C3H=LkP{d@NjB4xnLt&QpyFWajRw5Lbni}X3T%jj1x|>|Lp!VOnnY$1d@yP zd`lW{2vP)5CPWfr@o_xlmXs`6l3iPoQi#&`K-QggM<=#! zdavi@`$|%kG+oC0b*N@n7Wmm>F|VX0T~P~D-kXwA;N^mjH~FHf6m))F#k`)A`D9F3 z<*B+R$+0NYEd_7D=I}fuM}jBc;)+Yx`L7*55eD+((l0y@%I!x$ImWI7S($)mNUFk(3EUW*M4wLXVkQ| z<(h_GHNEAZR^djaqB8Za1~zaJr>^LEnYh7d8Z1THGqRS(N|CM$*8y|I63&x0bGj+E zHxOuJl`s=gj`*cK*j`kzl7)pySG}2rIbcLZ%4?PeTvtv7J~bxfWH=Yr;3|3>yjoPV z0FkwCgt&2?afFrcr_&Cwmi0iEi+&0J-7P4F5f3@{yjG(jZIumtn_0H$jyHcKq9m=NV_?%P#zrf1(u%WH+ z0uTr|nQu54FJr7?@Gqw+5LZUlB?XL3nXl>6MiE@59R$6k0sAs=JthYWl!NrtgasDk zc2g<<>Dv@drNoi+r~@B`ZafBsMvcYux(7$#Lito?|=zACP^;Wwh--eGp z>FIko{6+3b-z&cz{A%zwhbnz1jJ^}weW!NZT$gvp>Grv5$nA9fL!(#n;mif*2lq5E zBCqF&t6l>!I9zg)sf#UdU6O#CVoQ5W_> zMLe@BE8^1Z%@}E?(ydrhSXf>%?;B53<>OHN4(&n-xhWLHSBDnO74c>0x&r^^t<*dy zH29=5Qt9N4PX0+~pb{E0LSs+DLtmyoPkr%rB|K?_C%41XJ8e#SD^7YV)G8m&I%nO? zmS@_Ev)&E0py=S}h3x*9@*2$A>x;G@TIRC7P+*#Vl{lXD^W-{=(=n&W@ySS|*N!U}< zERDzyFK?w#4*?LkaUa0qQ&2=XJVRy5F~ZSsj!)OSDbw(IT3n!GUU>hDE6LdPh1+{f ziRY*iE$HW;b`}cLb{-Vy-AgFStfNNgXgN6cs8kM2R)YhrlrkOt%vlD)o2!WBb>%+s zWy};qE~FJ1UIh3Cz!Q%s(%2)4TY*L_nxgIV;4R=hTuy}yf-9<6CoU}KRVhQ-@@iJh z%kRti=mp{yML4M_68AdfLGZG}ETCe#I;PQsL>m258_o1(Dy?D-Q_hnvYZNz98O;p3 zCgDg|3$N07g{D(B2hE7=F1@b+Sb=m{dxzk?u4L=J9ke>>I~c1a$cFY!3uwk*<_0)K z_Y`fN*0SqtJ%bVLF5FG^yIb`&$d@oACpb(&duRb){|HkzuqlA&X_K;rbV!*@3ncN; zR8L$^F3c}13BnDyQ?Unqoy6wC}_ zrpKDpBfi#q2C!^c+xZ&bn00>egNxZFANC|34QRJ-~r zT_Z-mGbgCriNr|oc;3hXO|z3S0*kQ6PL=1Z<*t7|M>C$zQW#J(k))G7uX9xhiC2K zf8QS+y!;~vFVz!-KYA+1uNlX$nFpJ*=l)-ZPkuS`+03KcmCO=u$C2@o=A?8xYNc?(!{Q1xz@*6cH3|~#lXbM<^=o* z0D3HUCZ#$#=-}z_;Awcs!6&Dg^x#wfh91Eg9WZ;!Oi#VZW1Vv9dkc^t2udooc-q|` zu)x0edmq5}>2y+npZv|k%1_Yy4-R_2sM~q_*N`B5E2u(AP(x~13su9Va5YkjR7r_c zqorsF=ObFI8Y{(kpJ?%_SQ4v=Qi6}8TC$odrK(a%3JGChMNnhk2x^>)uOfo*BOK1J zl;)TSSR#OBIFH!fz)l6QX^zbTb~=E~sAsl>;@q?4w4j{&DDZV zziLv$Gzv;hg@Mti*9>YT|BIR}5=QP+43m)?m9ohqpVfAlxX~~x^_rp;Lrhc^lbV$( zErwa*bH$)*b(Lx?db2_`73MOVl?K%+HM(UgX47CP`2WZqlwI5Lf5aWU{TpPLWm&D4VLD=%I?KwkQmfTL-*6*Cmh~KLQ%6tHQOP>`rk()7 zhzd5yk-(BHvos+p>4@Un0c$ivhxCG>)u>rEgRfk~Vk{Cb;Cq=lF00gK+V^kVVIp2DmqHw1)Ty@Mf zH15z;4Fl=o9_j8k(93+JYvPojbnAKsx1U8Zh5`*+%;;0tpGGl*B9DTf8N(hvr#_3q zyKsmrxQ>1X1=sIc>=k+fS2z9wVuami&$e#tU2K2we9@ew`xgtX-?j6d`4@8* zS%B6h3-;o2>*n6A_Px&ej`8w(=jnG#`%}ez>5@g3p>@f!eQ62I{Qv_38CkBV>yA$+PcRpESZ*Jr zzZ39>>~jGD6qn#IJ_F&m5BT0CU%38%JrLI%t;$z`9IZ--q){jCqC$P1;&|imY%lF6 zeCP5s4=W_WUsFf=EB8wDSKqaR34fZ`DSd18y>EWNUOa_+mhzlXves-D4k&C6zJ3QLKfZD>WVp#;)g7bhSpKM z2Zyl-!f$bi(TjFwthLr!+g-Dz8H-@+k{LUfcijm*>Rgwcv&UyUzuF%^ZxL)=a^B8N zx0}!F;DN3aN|O-G=Q~E{;)_4+O9LmSz+<0(+)w&W$MHdYG#_y==-}_|zd#&A9IOp_ z!X60SNTwb^+}aOnytvQY4hcl+ClDurTPG$0bz+)kd~5Jfjn$)XHpK(D1RkGeV?hqh z4REOM=Y96*^RQk0A_$i5yFM0r`nw1$qF6$48O1mVmUH;lU(3*!VS2roJ%K?6>-6&e^!pZp(Po!y+Q+aBAAB_15IKfz z@Xq10wfio@qx)3f)_3ZUjDE~GHPYT1oWi8O%`jRJoLeu(PcR!d$&h#d8lGB5feFh% zYWS@&Z;^R>e99u&x@5{uX7-Hd#nwh^V;7FrhU0{|!f3T1rH@rh&ptiDG0xEFl>1>NUB$N^j}-DUNCf`qN6NaakR2(fE=T&<0BTvE+>uQc}|qV z9*;~s#_`e-(pOYz6Rm99KJ!=iA?Ku>-O$#Tk2)6+yK#M1|rPVV1)6E zYL6e$Kf>_%K%))+2_T~kKM*ov?uU#xjrHrDnCcXXQ7~kTO}mD?1wk0B(~l#wBkcLu zIfunv1G|-mjBdk3cwy(gqb$H93D+@y103Z$Yp^37g$l=rLJ^a@ogQmn-cMtB;8qBO zp*-V-dnfKY-h*q7RwCxPQA%_a=hoa&pUQyyh5;W``nt-m&>Dqc1M!6e9`Qj_eC8gD zyDUCRJ7_fL)%YIt7nA8G#O}YzOyW0>d;KgoSpMJ|>%}~O zJ!dg*Wzb1VcasWqw{h_TSM10g8dU%bKDLk}8{hAtlnL4jw&?4bwc5}`{6 zLQu4K-r($^2hB!s&7f#k-r($^2T5Y+)PWEb9k@Hp9(vGp63+~ZPT~#D9(oYmeG>0J zEIO-r_es3F3uU6AJnns1w2Qd+Jnr3va!Kq31?~uD@$N1@OF}aTLQvq2VD`|1auM87 OP&itG9R(pCCH@Dm%#u3* literal 0 HcmV?d00001 diff --git a/backend/api/__pycache__/schemas.cpython-313.pyc b/backend/api/__pycache__/schemas.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..908061f84ec5b89d043dd0bcd26dc00717b498c6 GIT binary patch literal 15382 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 S*v4sSW3*z>UL*O$H~b$xkQovH literal 0 HcmV?d00001 diff --git a/backend/api/routes/__pycache__/ai.cpython-313.pyc b/backend/api/routes/__pycache__/ai.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51a0e7ecacf969a26dfb941139008a395b7c212f GIT binary patch literal 10022 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 literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/executor.cpython-311.pyc b/backend/core/__pycache__/executor.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fcd9e7b7af9b33246e95af9063f2b846c4d41349 GIT binary patch literal 11054 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 L{^U8egna%RcxKry literal 0 HcmV?d00001 diff --git a/backend/core/__pycache__/executor.cpython-313.pyc b/backend/core/__pycache__/executor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59746e54f2c03d80c3e815e427d0cbf67dd25e1b GIT binary patch literal 12423 zcmds7Yit`=b{@Wm)JUXc>Oo1i#@5U9L$a-hZ8>&hTaqQ&mJ*KXSSDkJCPxx2id629 ziA`@7K~XFepo>JC1dcZWtZaftK^Ca;qloh>4q6~eespI{6=bK{ZGZ+X@=sa2$fAoD zJ@*c0Mxv=W>n!@%3+X=3ocrK8_kQQxd#}9QK|!ebuMcATc2U&7;DQ>=h0MLzAoDuK zQHbIUobj{)86Y*CHjWsPkyYwU$OLuf)8-KivW!@fb;O2jq|I`g9aorRu016i3%noV~}W zj0)9G8oiFpb;lF2Sw0?1@a(`&>HXR|9N~XebxmIhM2*=`S#H$U> zT7&jYD4IrLJk*fDN0N!CAhTp4bgmCl)N5>DL1WMHI;{{WUfBoGobQ=%9_WaQ1F1=ODEXy*5}DWP;`0 zMhkF-_A=65v1vO)+MS!Wmy>qartKA^y>iobCuy(ZTxiRr-CHTUo+wC^^3x{K*VQo4 z%;QQCBurgBWuXOD;Ga$NiBv2ck6%;7Zx$tIc~m5H5kp})ae_6$1poJTK=e8l(As*S zjDw$wYju;9*D&feqH?I0t-K;Z2H7m|@d*J_vGUN#zMx;=kq|r`j?ab@;b4C?ev|rr1=VF#^Soh`5#Vta1$_hwbAW8T zn81fup(yyxacu;Cv#+wK8H$`KP^H%T+-gowIkVs3tI^A9j*PvqIxH2yKLZKAdNrq~ zJ1F4MsF&3oXE|)}HK`>%RRs_DvTChQ%c(UKZ00Iu!m@3%U$9xHS99yu&-#d~V&!Zl ze_N~idv07GzjX^WG)7Sg8>~dDIu1Sc+tlw8w4c`GsbYgX(SF+l64S2pP;(dlLOtAA z%5Oeu@Tu%n0!TUgUeI8{7}tLBvrke~83ik@%M%>r#bLcX1@@?|z792~r}}RL-)PeD zh^y^@?+)m^)f`vmq&TL_)T7HGtG0c&y!b79VV((lpj)H=L*%xct2k_Yh~)M@5@)Ot zht)6j)Nfbi;|J;55uKx&Q#A<7N(;4}3OuH^*r-#6upd^*u}&tAsjYhYLuDdxT;=6= zsIvr~+qK$M> z9vvK$&57^~k1&Fft?}?>J}$_{SX4H}5)s*qZ7F%nB%fNxKZzGnw&O}-oX8c0M!ryX zk!k@3qDW0twx1k3`^1@na|8VdA2hNlk$hfewDwRq1*TiU^g(cBQ^W6p}5(9F#2sKHr!RVQ3&b!1A04<3UG|6DquA2p^yb z&?SJ{m=Gc>3+Nj!q#)b1#giQ{c9aT{c{{c0kTPeQObld7wvZgcJH2e+1-zVC6AA%& zaJ^0WoR{lVQ5Yx~#vO&01|Spu6Cm)5)LISI$lf|~^GL4YkkoMKc3rmNghbcP(Z6xl zu2waQP5YNxKJtF(y*-#ca8lggFK!*kRt<{G;F^`H_S`r&$rB)Rd2?h|wN)rMWU zh67T=fjRpMU6-dTbF@dIJ#VzUOTSnCcDcBJbouJ9Uikcl<)qkoX@w5mw^H`z9PPbJ zdzYp@n*MNFqEF=LL5Ut*p@&wXgYln@zZ#fxteHFQXANI&+i`39=5%h`QEA)J+f&(X zr{>1;Om&XgDluE%h`u}Y-r2X$iampOp8M?jr`JX9a`se29E^%x{0cL1&q{6G^9r4> zV{>)8q`F;;hq86uuULLl+qhcaBDTW}Zac)*liB)y(cKTTtE|qsnk85B!oZ5FC-3s) zT)XbNb}fb_S0`k@aRRsdZi?;9H64sL)3xuzbesi(vw zR<~_m*#73k-0)xOD_xjR#e7A+YVR*2OWQw~SiB&*dh_+I?_SHbAGzCpB-?&e^c-8W zQ{~&fp)4>x*TbfFL1ND5n2Qo~afJ!4GR-+=*Ij1U($$Zye|TMDPUe_X5_4*W8Gf{x z=a@4hbLLBD9ZdcAYwdW}{~=)9__sR-*%34Kg}H33#q@=9z%kZj`c;z!3g|R6qf-!# zMhYku@)U))H(b8g4fY~fpGF_vyo&KG zNQ#1vfD$IKs|=+q%K+Z!Nbup^t|;_fpr|nH%3#xODQbe^g(}rTNtnkLAIe{|QFKo0 z_~XN-C~V+=^`ZPV>qKX>jz8Y!ioyo|Ll5O&!t!0mzl8m}fqz{^txwD8U+VJfHxWC}@-SN!)?lChy_$nHE1^l8jiV2nd|fJ)p8757SXpY) z0=AT9UD~MdFTj$rKH^$y=v?oMvj;Rg(Z9j#z&QeaDxFWW=G2j1w z4-qtr!EUBP;iiIaf(#K>A8!-F?iPgoAp{AdPMpKyi?F-_?1k|l>HrQX%+a|&-spza)V>OvvQqA0MI@mVEkD?!`3nXIn1J3qe!MxIuFPB<7bBZkDnVJ z8}Jo?o{F9z#aaxE1zDu6BN6AD>S#-&~)qY&kw(O z1fav3xx-%drPKX#=B3PAMuPk%78;(biKbXoL7Hefiw-{U_Ye={KNJ|b;B zGB>))?8z}*cbTqyV{5K)ztp%t-@xALyV;koWxv6&_FI~A*1R)jqNR z8}q~?s-QTB-vkKCtb38MPsv-BX;my;V2wB-b}MXh8;R& zyDdgGT8zHg&(K#r}t($Ly>&}t4@#mjF^dj}8W~r-N;pp3=V(++kF_gPFAzhph$0x<^ zsTDf5MpIR_HyY;~bCuhr%Iym~7l*Tzy>nKmuicuj_2jE-K;c}DIrCZt#Z962-;6KzO4|<1^}phIX>7HzX~FUy^ER`1UShkmjXh9Z`_kD5hXO|Z(o?t3 z-D&^$>Dyz=$Ha@zh~r_g>+%X6`O>*%t_)$AURqcG>VHR9sZx5m&NeWW)*YXwTQH$Q z*r3g3zu9jpNd+3N>ii>9SUV||2WRUrCoGSMc}r<6?T34OO>50XT5BO%yIa#*Rs%B| z6c=v9u``tDZo96#8X798zjl0!P98UadlnvZq^3|ZJvl}6ljt&0SFah(!ivjfihd~C z%oGq4DG_bUFy~a51zL>|e3Ef$s-4C|dr-I06(B>?n7uoM-Iq~*7H-Y?MC4kC>=$v| zjR)FW$)%nuPvv74L`;_K7(je#eZ776v zB3-&7K0PgV$5-eXKo&Lix#|w7x??e&t$rMwax_(4f8)&jnVh>ta-A&W|n}Uc8)j@BhYbsdB&u zQla57IG|r1dI>yW?w8NLbT(g6|3>e^Ua4W9SigU%>5k`48TfL*Yi4(>F_f!juKd9b z0GpQNuzvSXU<25vsbyj_^@l@VRo#RofL+fd=w@xg%9#_kQZ0U~-v&pj1r(lsM1^;$ z{pl${4J#bbC3}Q=y34Ze(56BC?U_Bg`s-%M2L0Eoy_uc5{_B=Z$qm3NM5g$eFwtM- z@HWd9<&Bg@@yFX@%7qWQ3KPiSyN(zr94NSK=*-ZndcSWkI?Y}Lr{u}mgDy$ik3sHDEdXHl6P&c==usNnw|xEVHu)L zHT{wpisnMoQfOMd6c@W^R_H_tO}7>w`X|6>nX~4r8gF#WcP!K_R%NU9%-O!G@x0Og zYJ9FNU+0-C2j@PNYhMlKtJ!>QOTMP@;i_KuM#CHB3s1f)EFOI4M+=FiA(1)y-KwtP z@}K+(sQNnOzL~0<*Q+{s>Pjd(yqU8C-hWh|RVj7eRFVC~XStOqbDO5j@V=7x9X4q4 z^C*VzQN5WKU5yogMM)Jdi}C`{lT#dF8=ffhIY8lmjupaLz}6wmHU*d&`)4xNr%)2E zjx&s^z}Sg|UVx?toDYg>VqOY%Cx@&JiUsvkln*@Pl-#zt@~!u;tO2d?HxO;Aco)UM zGr7Q|6qpn*PKn*I75YjE#RF8`cq2X^&(*X^HEjzoEXA@lkAr>{DIR>;*uL7dW8vVN zKg!oO00JjRDzoX4>VA#Gn)%A;!k)$Ji!&ng*mrB08Ky!G)E`3ztY3o<(&DhQP!RuMCzezs=LAs2Yy{GsTejIQaUcp9{ce=!^g2$5u`OzR& zj28+Po6Kye3_vGj%LeF&?-BI-9@#z-Ljr+m0-iS(0!0GwHebrPDgo*oup0^3um%Be zx^vF$@R#3GJvU5HQ_kHcx!V?=mE7Jrd)`@>bFz|?%{f~nXUoF)k}>P-66r32YrF<> zEAMsVN0r#i2#+cqq0me+nvUZ<6AC>G@0klN&QOR99zTdqB%#?B3PqEVPzd4ME`((o z;d2F@#Sz{+iQ^36c`7bCy#BN^feBvN1`Q(fPZz4WeQn3i!ou^RjaykX?ibL;H zWT?s_xiK-0s^MSw2BM!(|7afht=V8{U$YJyES?2=jl${T_;3AF(#dE>We|#S|to30|J%q*WafMU2_yi~V zFNjZE`21Lb6K(0nBZ35v2$FRp`WBa!)xc}utwTSBL~(p#1yVe{R`B$~{--QES#b>M z<=Vo7+79@2;Xy>lY~c~H^1G2=i~M*P-YOns;w)}dKf~{EwU0u|8(-zYZ@o$~lZ1D< zNd%pD!21sriwMfY2Yif>*AI#t7j6KR9}&HbbJ)TrzphL_rbyl~^cvKGkW&J_vsg13 z42ExvCWHANO&KbGO>O-rs_dVsHi>HcH!7N=qQ9mNervQEjQ>tS^ljA(CPVjcDI62t F{|$(Z{muXY literal 0 HcmV?d00001 diff --git a/backend/db/__pycache__/crud.cpython-311.pyc b/backend/db/__pycache__/crud.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82f8ab911909c4a5a55a93589be77bc42a06b2dd GIT binary patch literal 10458 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 a0Q#H7O-M0h>)D6LzJ`5);wu3+g8v1JCoGTv literal 0 HcmV?d00001 diff --git a/backend/db/__pycache__/crud.cpython-313.pyc b/backend/db/__pycache__/crud.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64483989a5984358ffb937a534e4e9956c0573dc GIT binary patch literal 9534 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 HsW$yLV)oqq literal 0 HcmV?d00001 diff --git a/backend/monitoring/__pycache__/metrics.cpython-311.pyc b/backend/monitoring/__pycache__/metrics.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f378721dd9efb465cc747bccc5924db174ed511b GIT binary patch literal 1471 zcmah|O>7%Q6rNr0`Y(2xrb*NMI9*}W$UsXERH6vAMJXf*B}7SF31hLW_l=hk`(w@Q zqBf_BGl$$Th=au;6s3n83#TA);AkWct36fX#BHnQR0)Y0+v`AUiMKOvKfmwox6g0q z{ZK6C5x0}S56RCNg#Hvi5<52pPp2h>zD9uR2qchgsV?z*%9fo}J>@8M#Zl|3ldh*F zBqN*x1yo2w#*zOTFhXo74;`b6PLFvDGV|DT`=uGfD6_(-Rm}(0k z@1Ux-_hx>JdJgtK!vWJauuqA}wA;8vT;dbYWm?Ol+7@YJo48op@Oapx#I^E&iDEaa zN|agk0@ueh%B>UTdlogENL@1m3rE@Qch}zE+1>sqnr{y!bbJ?^L0?+;J>RhR%DdvG zxq;KfRP$O|lB%7iMiWU;ClIBXZ}{2co%(s(m0eA?C*i!bkpjMHw8tGt?`SzdgUAE>#8gK3TjtZ%;& z_EYkY9^Z@>Mh!DepM3GJSF^p>H%DL53@F8}Z{O2s;JW<8jO%{27-e)FJX6;r*<;b! z_UJI`rcLRXK(!<02X zFzmMB8jahY88}>(HP(o~9yA;8+4wHGyzLo`pWsH*;5~Jr;f#)XgO7$7i7V}U&$4ej z9t1YNK`(H1F~ICP_i;*+q*#_EB}T%fk(9rm6IY1d>HXh|XHcoqSqqhmp;8Ui>HYPd zQhJh6O7aQfu7~F1NwoMmaH-2af%i-KO{^jtiRR<3r5cv^X_Plv^+{qnxw z+3mjZVD?Bo3LfF(JHOrO)#_q>{ORvCUPrx*|V8+?@#5L*;l1RW5h0hsveC z-m5)DtbriwtafL*{)644YmZ(&p5<5Y%hIn)y}~BfsR*5l(5Y}8jTg0C?$km>3zch0 R&C8)7Cp~f_zw~qv^@X- literal 0 HcmV?d00001 diff --git a/backend/monitoring/__pycache__/metrics.cpython-313.pyc b/backend/monitoring/__pycache__/metrics.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d433c2b3e78b76eec6b2513f8fa0a416f24c120 GIT binary patch literal 1307 zcmZuwJ#5=X6h2Z%{aJDy$Btt=at@K3K>2fq7-)+kKX6(*Hte`QJ3td63`L!kLn%_Y zBa6gS(Yb?{0%_5~V~Te0n09i}B})JWS%_O2AXByo>RO<8l1MkHZ+QIp@qORDd%V{z z7V|)^zyJA#{+-BVeXZ z0SvK$(>ODy&!_qhEYst&IET}7TEb>cD6{i;NU^wpi+C7+fJg8VJc^IvF+7fsnK_v; zF|YM~E*J3ek0HzVbdz{$4&do8G<*V=-c`S+%J)=5rXjs%@X2>-C^Ym6+WsiN&ip0` z9*~fuRT41jaCC#zsYe6qdmPn$hSq6|xYQ$P)t9u-sMpB9QH8BlpvWxvp%)M)a!Zs4 zeuLRfVJzBVgNW?LXNx!S=Ej#|vNf=<$V1|UJ!>oQ1KZs$Z>k6K!e*5){Lb^y?KG59G4zg`n5>eVZn$W%k; zD-v*lWHTD%psoKezKm7IK{pSpypm#BS-i;i-8{QVSWXg+AAeBmxA;V_u8OIoVYU;{ zum7_(-=14b{-P5yM!dj%gjnc#^2Qw2{Yp_}EUV@_mL+tbizBV%GFpyHrJkulVo(J` zg|rG871Z}Zr7JfVX|NPl z&Cgx(kY3pEZ7y%HS+!+Py_(rfuDK~kLyg46)+3SqwCUGEmt0}TrMntT{#=H)DNWNl zx~{=)8ff{y!1b5ldS?ujW+Qkqf|bY^-C1hGQa1xjdK*l3hQaiy2$mvvNq+j~mgJ`; zKhYTlXD+FHc)rI+#gU!zPVjhh_wtkRJ!3C?O7?F*yZvJ8PW$ftms@tbQ0?T5Gg4aa zjDXn-5j-u`0-oy&`QwG%vEAUw=HC3%5C0h75C1&TF04t#tW+Sm*-SZtD1!5G5i|M= KFvX6^6a5QBsE17e literal 0 HcmV?d00001