From b474d12b7c3853f2b0b8c537b56bce6e88c74ece Mon Sep 17 00:00:00 2001 From: rtdurga <102053670+rtdurga@users.noreply.github.com> Date: Fri, 3 May 2024 19:30:41 +0530 Subject: [PATCH] =?UTF-8?q?Added=20a=20new=20template=20to=20import=20hugg?= =?UTF-8?q?ingface=20models=20by=20taking=20access=20to=E2=80=A6=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added a new template to import huggingface models by taking access token and model id as parameters * Added a new template to import huggingface models by taking access token and model id as parameters * Added a new template to import huggingface models by taking access token and model id as parameters * Fixed formatting by running the shell script fix.sh * Fixed typing errors and changed hugging face access token to be read from a Secrets Manager Secret * Fixed typing errors * Fixed formating * Fixed formating and typing errors based on python 3.9 * Fixed formating and typing errors based on python 3.9 --- CHANGELOG.md | 1 + README.md | 14 +- .../README.md | 8 + .../docs/_static/huggingface-model-import.png | Bin 0 -> 75489 bytes .../docs/_static/huggingface-model-import.xml | 2 + .../build_pipeline_construct.py | 288 ++++++++++++++++++ .../hf_import_models/product_stack.py | 216 +++++++++++++ .../seed_code/build_app/README.md | 12 + .../seed_code/build_app/buildspec.yml | 18 ++ .../build_app/ml_pipelines/README.md | 8 + .../build_app/ml_pipelines/__init__.py | 30 ++ .../build_app/ml_pipelines/__version__.py | 26 ++ .../build_app/ml_pipelines/_utils.py | 93 ++++++ .../ml_pipelines/get_pipeline_definition.py | 74 +++++ .../build_app/ml_pipelines/run_pipeline.py | 106 +++++++ .../build_app/ml_pipelines/training/README.md | 15 + .../ml_pipelines/training/__init__.py | 30 ++ .../build_app/ml_pipelines/training/_utils.py | 90 ++++++ .../ml_pipelines/training/pipeline.py | 119 ++++++++ .../seed_code/build_app/setup.cfg | 14 + .../seed_code/build_app/setup.py | 78 +++++ .../tests/test_stack.py | 2 +- 22 files changed, 1236 insertions(+), 8 deletions(-) create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.png create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.xml create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/pipeline_constructs/build_pipeline_construct.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/product_stack.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/README.md create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/buildspec.yml create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/README.md create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__init__.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__version__.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/_utils.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/get_pipeline_definition.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/run_pipeline.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/README.md create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/__init__.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/_utils.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/pipeline.py create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.cfg create mode 100644 modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cdafe977..519f952b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - added EFS removal policy to `mlflow-fargate` module - added `mwaa` module with example dag which demonstrates the MLOps in Airflow - added `sagemaker-hugging-face-endpoint` module +- added `hf_import_models` template to import hugging face models ### **Changed** diff --git a/README.md b/README.md index b5a8f23e..0699ccb9 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ See deployment steps in the [Deployment Guide](DEPLOYMENT.md). ### SageMaker Modules -| Type | Description | -|---------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [SageMaker Studio Module](modules/sagemaker/sagemaker-studio/README.md) | Provisions secure SageMaker Studio Domain environment, creates example User Profiles for Data Scientist and Lead Data Scientist linked to IAM Roles, and adds lifecycle config | -| [SageMaker Endpoint Module](modules/sagemaker/sagemaker-endpoint/README.md) | Creates SageMaker real-time inference endpoint for the specified model package or latest approved model from the model package group | -| [SageMaker Project Templates via Service Catalog Module](modules/sagemaker/sagemaker-templates-service-catalog/README.md) | Provisions SageMaker Project Templates for an organization. The templates are available using SageMaker Studio Classic or Service Catalog. Available templates:
- [Train a model on Abalone dataset using XGBoost](modules/sagemaker/sagemaker-templates-service-catalog/README.md#train-a-model-on-abalone-dataset-with-xgboost-template)
- [Perform batch inference](modules/sagemaker/sagemaker-templates-service-catalog/README.md#batch-inference-template)
- [Multi-account model deployment](modules/sagemaker/sagemaker-templates-service-catalog/README.md#multi-account-model-deployment-template) | -| [SageMaker Notebook Instance Module](modules/sagemaker/sagemaker-notebook/README.md) | Creates secure SageMaker Notebook Instance for the Data Scientist, clones the source code to the workspace | -| [SageMaker Custom Kernel Module](modules/sagemaker/sagemaker-custom-kernel/README.md) | Builds custom kernel for SageMaker Studio from a Dockerfile | +| Type | Description | +|---------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [SageMaker Studio Module](modules/sagemaker/sagemaker-studio/README.md) | Provisions secure SageMaker Studio Domain environment, creates example User Profiles for Data Scientist and Lead Data Scientist linked to IAM Roles, and adds lifecycle config | +| [SageMaker Endpoint Module](modules/sagemaker/sagemaker-endpoint/README.md) | Creates SageMaker real-time inference endpoint for the specified model package or latest approved model from the model package group | +| [SageMaker Project Templates via Service Catalog Module](modules/sagemaker/sagemaker-templates-service-catalog/README.md) | Provisions SageMaker Project Templates for an organization. The templates are available using SageMaker Studio Classic or Service Catalog. Available templates:
- [Train a model on Abalone dataset using XGBoost](modules/sagemaker/sagemaker-templates-service-catalog/README.md#train-a-model-on-abalone-dataset-with-xgboost-template)
- [Perform batch inference](modules/sagemaker/sagemaker-templates-service-catalog/README.md#batch-inference-template)
- [Multi-account model deployment](modules/sagemaker/sagemaker-templates-service-catalog/README.md#multi-account-model-deployment-template)
- [HuggingFace model import template](modules/sagemaker/sagemaker-templates-service-catalog/README.md#huggingface-model-import-template) | +| [SageMaker Notebook Instance Module](modules/sagemaker/sagemaker-notebook/README.md) | Creates secure SageMaker Notebook Instance for the Data Scientist, clones the source code to the workspace | +| [SageMaker Custom Kernel Module](modules/sagemaker/sagemaker-custom-kernel/README.md) | Builds custom kernel for SageMaker Studio from a Dockerfile | ### Mlflow Modules diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/README.md b/modules/sagemaker/sagemaker-templates-service-catalog/README.md index 163513df..d45ea112 100644 --- a/modules/sagemaker/sagemaker-templates-service-catalog/README.md +++ b/modules/sagemaker/sagemaker-templates-service-catalog/README.md @@ -26,6 +26,14 @@ This project template contains SageMaker pipeline that performs batch inference. ![Batch Inference Template](docs/_static/batch-inference-template.png "Batch Inference Template Architecture") +#### Huggingface Model Import Template + +This project template contains SageMaker pipeline that imports a hugging face model based on model id and access +token inputs. + +![Huggingface model import template](docs/_static/huggingface-model-import.png "Hugging Face Model Import Template +Architecture") + #### Multi-account Model Deployment Template The template contains an example CI/CD pipeline to deploy the model endpoints to multiple AWS accounts. diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.png b/modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.png new file mode 100644 index 0000000000000000000000000000000000000000..78033cb59e51ba2d4a71d7c08db2912d93a661f9 GIT binary patch literal 75489 zcmY&j&@EnyQ?$h?T3m`lpvB$YDemsUDN>-g6Wrb1T?-UeJo)4a%iz=z& z#{P!B-u%q&&)1{c5Qmw&_%C*THhG&*j%PUUpZgs2r%+K-Q;RTCz7xd=BgWz-cM{(9 zABK&E|DOwz2gIz{Jqmou)fltZg2x0fr1)HHu)EbY;g7ohfT85&qi)Ncl4UPdD*NhO zu&WkXXeo|zYQ%}o!NbZ`K_gO^hhDUOwmm&g3i*4fP4$XLEe?Qu!#$_RQv^IN^v}qj zh_QMUu02!i=;}XxjJ4apZlZNEPJP`tzplEaJr0K0aH(cunWjO0jDnORO-_y^Lh?`w zq#~#GqS@*EChL!?x}3t8#Gtp%`UxX!bI3w8@fjYETkQE}QIWg#c`B>{*Y%qp#-~#G zFm^dVb~Qxcq5C)j6~nf90~o6(24el)hTf?=g@-(l%p^|zOH9LDn8W@a%~MT2ZP4lg zmGCk3XQAZGa_L8BC(lCFB>Cmv?j)qlg5U7>rgSh@M_SYFjEecT_$YOy{6=WB5~|Fe zvRDIM*TKVlq{mzIbW^bMp%MKD>_n;WCtJ}a(+MgT%l;YHFA<*d@u}>T>vU2Z9W(hc z%(-)V+hCugDixDfJzwJzr*ID3us8dr+!4<}jONr6d7^vcg+HTWqIob8Q=WL7X>m9` zap^j5n=MkQ<*4}2SNV+ZL@(oO!JJlBm3_VaINwV^VRp2+v>KoG#kpHhj`&V#S-gbt z`IxeZt>|sO54cj4uc94qc5_G_??rdTsvq_RptR+DE|pwGTfw z7#vgP&-40*4RymZ=`RI{R zmC<9r6*G5_Pk%&!Ti7Z6FbW9Wq0q!pp^djQ< z6#pJ8z!~~jNX*$?yaKJ$bH>Sv?fxJ6xSK?edd;e94VN&tx8pBoxM5!=2R5Jhulov? zm?Vqz8UkiH{zln3-3^&ahN;`xKRKrv|KQfaos_{+cxJR0qA^^WG`PeB=OqF`+KEf# zA9K$56ckQ43f3hCt0&WmAKNyE`uh=VQ4&R}X6B4c%%Y1Qd2IYV2_!ZEePm4MicOOi zq2~pqxwczQ!xmN%zfMy5@9c?aj^~OOAFwG`f z+eHzMmrr1^3vKys%2)H`fO{?!)3-ntK618fxP0I?>2GB^K0P!c&R2+y+IM+g=e0om zeeS(0``Pl$CsOA5FP>x{6Gio4qAC!6zV@VbbYYRmWD8$MrXC}uoqvSVV4P*V%qQ7lNI2M=LI@L$)>yVcfKT2^M|SzW5Q)M ze`6Hn?t0EuNc~jJ(Ufl>LECS%64}PHd!vcva>4 zRf%h2g)o39#&2w{N^^$TnTlvjqw|Wej!KqEuz+D0uZoyEcdBS%6xcr-Puf6CNR=Lq z;#r=0Drw>)`H}C9*5}E;J8V~iHruG38YR_egfMCDV!Dh+Rtv9Ey$hNi^4Znc3@{0Z zP=L9{aSpd1B3FvF)`=0R*tyi#?lEQi|Eg^vqwNPc3Etk^e(EzgK!2!|RAc)(VE_j| zx)2&f4L|bU4`R5H{&Nws&Ak1_3TcJfZQV4CC zpu$#;=F9G>v3$5b%Ti`sVQBR8d0rBw0U&drUOa-}XO51;>4lNk| zQ_oaS(7WlJ@9(VEDCSA!LjIPimElXaxt%RrmNE-*lVc3qy5J3p$#-#&8y0!C$%~mR zNTo8sb(MOXFx=V>>1GQQ{VZfa_2*OicnrW`-QDS)CEtVFBvj$dVWBuk_crH0)a`y1uLAa5OgEiW6N(zH}c3o|jx z`jLGsmul)YZKn1nZA|9pK{e|@Wi2PkHgBR&VTkUNJ1PjXctDCuo7d5W!b)5Air;hO zQrL|h%|SR9pt4`+U$T}y;*@d2A0^{{0J!X~sn~dVam;<7gx1*K>y6u*(1Jtyjx!LH zD;6232^5beF1e#iV%CdrJ(=$r8j?8SG9IWu5RQZecm%AjdZa9PL|yNvaQSC?#aumy zCttLn2m2TYwezwYzPjMQ)p}SXFt1S;4V9kH{Vu8^{%xZ>HB~;AbPp#Rf?ZFDMMu}Q z-%!EYo=$S+dYOUcb4gp&`ArEa;yvpxnab=TsyL7ZyIlCXBL6IcJ?Rxi?iDH=*1&d z`3gaJ9XE1JeX?JRF@DOfC8oEI3a?lG+tNv&@ajeI;M77FyVwM)^KbZaM$?_wn|GQu z9B>Erv23xF2`OS<{m|43eZ;>m*6F4Se!D;#*elg!dCxkP5=+BzsMt;^Cn+rL;zP_O z)DdnXq!62rvUItqAt2-St8@lSjChGZPV;BUk%GWY z_|B8z*ztkWq<<90$49N2cpD!{|9W=8`*emp`%{oYl{1&s5-NI@KN-c7tCd}lzHMq^ z%$yA18h1v**?IF&x-opk=a2xyOXB_Y!LNX0(J|P7IHs=J_ToJWIG~rD@q5L=+fh*_ z0P*A2$@fW{!jzc}k>h8NS0hFYPYDxUknC`3l$LJgYRtej33gcI=ZgTUhKr!}bFRZb z2?Oe{5%Rs@C?h((MPfbOMfV;64x70z&-(K~*<2G}A|u;${6OviSiZM1zH4*Km;v$I z5Q~J#nh-OR*vrGLJeY38B5 z#d!dw{F1}LiG*ysmuk_~UUkDzacPtiuebgYq1}^T_sk1{r5lg$LD4X1Oi%1Gp4o4> zUn-nfanoP>@;jr@MyGFIjmeOUneNGaMJ$5`Guyp{`DB4!5dLc?YF3chR2r<#0lVKr zCM|H{cd-0pTO zyj`gB90~Tcgws2Ck;LEmNqnw3;3NYNPra1~90#|$o==bBuW2u_k4*)Wf0?TBMSiU5 zbq4YLxIitv+q=2>k_+c38X{)~Zw;=T2Q*!Jz5MdI_m_SC1K zw&lKlgvXDNcR0x*GeBq4G5@8$jQs4=s}r7a{TCdznjfg;ddBupBzxz^BzI2~;Ge0a zMwBC78K|sn@VW2zNH0p`K0eR!4er>v!QT=Po*ZAUPvdIy>m59eF2$zA!%V%f6k3UD zOmq;g6WO1nqNeJHJNUKm5(~#U6V-@6*x~=$7{f(kMO0uKr)6NNTAA_rY75_#@%8RV zv-<^Udq;<>9Rrsn5ymCA*edd)K6bj9xd`*YFY9jMX*l zJu$yUZ!?Cu6#Bn)55N^WMR>hXkYegX3iS%ztmgmb+M}gY`qoFl14?gmI55HWr*D~q zCi(b&YIh*~XG#R1YSfgXYhbxE1<(o}IL@ACC``-W{MQ8b0=o-hxE{(X;EPj4^~M!d zKHbzI$Zlrn9$>pj7OtX^xA$D7J_xt-%=hO3iC%(P)aa&1lIr?AfqLU$G7Ee59q*s6 zeBDlh$byw#dctjKkel1#pI=(x!!MTC5g_CzdKo8S-AywLN72;W{rbr!vhb98M%)03 z@@SgZuh7b{z=a6#;T#ggKbRMMZhQWT>_aOXN z*iTG_^s&W9m8U0+k|gA6Nn2CV8doZ;JMjS!RDC$<*^d5RRlJ68+yDXa43-YF6~!PV zgZ3jPKI}3L*xV0jiXu9VB*j`iA7+tiHdT}{vLbs2_@~1n-+fxB4)n$$(oCsCAOEEW za6GVNJXWje?+4a$l_L2a`rvYrhGC^7rnGejvLv*QWY1w|X-u0HlEeo$b^@)J1QEvIqU^b{@ zJPq83K6KcgMWNyj)h{zEI~DmF7{2D6JbskX9h0ptIs@Q>i9KHk$IkU;NLrJE}Akr>D6+TF7vvC26^p|1u zb5~Y2_M2fJB)G+PAG01Kk-@IkHc^mFRfE7`IoqKt$x}X3siXR0laMVz&GO%hIh*Ew z&C9^AQ4AFzqP{{&vN`MZhxBaS*5>7r?wA8^{D~Y9zhAE{B~l;c?KcBJDzZ~*6DOxM z>QjFrT3zLJ*O6f;lU7J^fPXxro_+>^epicXYNhmQ8|PfTW%)OF_hL^?Ia5?G)YozK z7-GKTF*Sz02?jn^0qEjl0_fm;=FicG9!sP6DQhvMqiiKe#kV1Y=NUJ8ZIcH`Nq}V~JL1m5duo7NAglx{jFnthH2+Gs=Ik6+o7mhN^z9)`;|+P$mXqX*rRLXFb%ZbS2BPSE&|7oVB3G>tRAFxOptr(;aUIN|$NIXuuX|)n8WFEpgo6OSbuziqn|J}v@uJtTvHzlKcyi?tJ`9Xj*JHZi0`r(}L;TqoxK0 zAmEz$C;OUtHra^9YMG-_Le=m9GBua`R&V7?=f?>X#x`$}BB1(L+9Z%b^Vu7$2@B~A zAAY@7Ph4?U8TkaK`_rtB?!7m|LY1qybH;<=!;Qz<)nHANg3Bj@jj?mDshT6Xf`O-h zc{V!`Ub~=`l+_SMJZeS=%V8vrTC)q-k#f8|tSaxQlJD^@d)s9-eKFV1+zc_Tk%MT1u(VC~v`L@jRDOv2yS?)|_+tZk`+Sr>AE^zmPXX|E&kW1{jIIlIz zNyGR_2_DFK6vHNT5CpRQ`MG*1tS9CR_@x_#OLW!ph`(In>1L4F20^ieA65 zA|8Xec2qAygOh}f6p~Lfr#%jL2#4#{m|7^Lz@$e5tb6cBrIg2_g zF$U+!P8Hi`@`2AWzM6v{`Q%V6 zN;}MV5MeB;+QfBF4ilDOZl8)sr4aqgp+33GA;G5$XivEAhu_J{8AM-gh&F~L^>}-j z2;P#EKD6p6op-3P|Gu_gSb-*wKf(h09vR(yX)pNEq?mnAp?$tl%*!6qmQ?u#5m0(~ zYQLah^`zzWlZqoQ0L?g4YbGC_Bw{F5?h=*O{o6=ZmDM^PUMKDB0{ zgQei%OoPXGXGiHzEJ-L+mp5z=n^HA*Q*eAD`m?&>`%m~AG@A$E>o;9X*1EMWk~WP0 zSQ|$N;m?m0v*h$uP=p%|ZFO_i%G3in_<>-aR+pndQbDgU`pT&95U0HSbV?Fqu2PNa z-f^STA&Wn^7niSb*?&95NUVPyPB4d1?d|Rv6l;!t;H|6qCHJBrVR+Mn+yf+tF;6e5 z|6&?h(+syg*|d{#$rELyl8=d*eg~l>zL_1>M*=?kJb}WWcZUmkr>kve81#fO=5@xQm92;Y{JgB~yHC~?X z!WcJL0&aC8W;J}sKK1ccHQ45KhGKdoRECOt7>oJB_p)+l&IYh2?K_$DYyp-Y><57+ zE>SU)ygN$~F~J^We+gIilhym0P3l6}^&rp2n+G-j*|0r_ zNyAa#zYYQJT|FEif5URN&}eTou~?^xSwK+mXs_UZ*_C0_Vj^z4{s!v>R-Z?Q@eE!L zywERkvbGqUXLnjf%%#aqz2(?w8CaJ$5S)=DDg*7mbpw*`A9;Q62$Jm4LsSaUH2Fq8 z@@9U7S@!+{r-KoE^!H(-)W^!wrYw>J&B?7qKH_FfrMzyg|F+8A|+UeEvZW(AVP*|oi5rHaXLQ(RRu$rMeaLSD&{dr`O+c4ub0$bw8<`v?_ZE zM%#%<-m;upE@0l{_ima`S2*)L@Bg##oOz&-)xRV_?$kZZll}E^Yk#iBL}InY`JIS} zNLW-kl-RDm)yU=rQ>0+vYT=C9LbG8CoXRuX#6E>#>DTz+;WX#x}i4St;KTEH*BA( z550DF8k+iDiheZvNe5q&(Q>(=Dys-9NvdZG=`kc^w83C!Tcl@dg5`p3@Wp==l3Eaj z4oJ@YjNwL@txSEe#uKF>0vi?AmBK<*U61@mdeRDDd z5#8yc@uVQ!DydlD`7$4lmkc97&fC{G38b1Np*_p}6?W3rgipl9ZOSn&H*<(}9SkL@ z4xx!M>DtL!x;q9ZH}@_3ted62!2}w~tXxDW`$qJI_5xvpt6zI0C4Y3iM*wFmjhFvG zoqxwM7L)X9$uDI5M3MAm`+i^OPzHLCiL0Re-`jdgrzPo2aY~Iyux-4Jehw0C2qe(Ld)-Od0z5BkjocM-@>_i+NJKe6>Zr?dm%xd9UH80 z&_VZ9EGvqC@S`)3e+8?kX(sqR7}kOGW~c7y|e3 zcWul-GKtw|HoZ%LaL8|o$XzT{(@3+JL$Za#>3tao_ql#=HdVA6Sw3|h#%_G(+odfB z2vUm_sZG1AVML%NC_ykw3aZ!M0N>3f`Gf4xz|H97S9+(fhLE>~+i}7y^u)317pL_{ z$YB2fpg3fBd>o;O_p*+eFxgJx2njsW-H&oDg#RS)O5`Kd zfAm+s)iQw)Z8gp8TU~1-T}DhMRs09sG=Faelj_%DgFX)@CsxYwR3j3U5K!I zstpXLv+1fxoH{VtZ?fd=7ctP?)GqxF=7W%=l%TYw_8iyb9K+T3l@EAq)zZE^n(Q*K z9aUsC;6`pAKP1ci1#&+&w32z`mb0ao>>WtMqiAi%)rZ4!+c|hV=iPxF|0zNTWA6w( zD_bM8Q0oq@+`!F415MY950j4qvyl+JsS$c6jM%JycpGUL78^{MtvF*PDc9=vx8y|H zw7lAom>-dh>xM|bi=dNOk;Dkq8HbS=L{boe@~&1K@*Gry3b$ECSrRB8G+)F1&dQDW zecydwFF|dg#$}#7Enkb-w+;DPSs-7ny2)L#Pz@m)zp!?UP36#C@)X4VL5j1GF?Q|S zMsDYt0X!i6KGb-Fev66_N-nlmI#a@UUiLYs??}}+GrR-O#@Yh=KI5=CI+D+ikB)Rt zEtBiE7x2*sii;_#6e$l`prk1&z@?=SJoQ%T$nwIUtwM0GD)b(imm?|j!9zCh)@K>5Aipn)+=aUhn zLFMtom5RwWJcK&`ThoJV=}92h0E6UD2n`R6j9pgWtZ-7P!HSOG`F(m^+%e_MhkwOM znAcFb3;3GPpiv3F+nTE~AWLR3fSkNWkf1ncJ2qi1ze!WU;*Dt7x$XyMI@_40 zfOoarS%?Xf?U(S=D*q^zA4^;084(?{VX&TIM!24?m4si38u)N0DFV@59B>)>6H$4R zG2UexFH1NRuLWGby}{YF@37)Z;Y-j8lKNufR4pd~c z938R^VObwyrzL)68HK#SU1b&Zz+B3j>vORT?+0DeH|o0m?rpQo-xHx7CsJCz7at(r zJ}PBbR&Z=zuW@@9wI^a}<4=Y6PhSW$T@NVX@+j+{wVD_FO8;MECOW3@g>_C-Y9^`@ zqrljbfiZ0u9&q6@HJ>!Qgtn4g`%4iPu+6r4mACnSwE#Z*+`*wWzZ`T^Sy_ob$>>@o z*ebK1iIkN4irYaW9s2w#7 z1*)9{_2WAZaNoW&)8h@$5ZTS5(a%Mavyj;ITyUEEQ>84(yV>}(S-l z;Y?b~C|ddz9*eJW0`QfQQMSg`QUAwS9Cp>lknwPR72iYNNptfvfjdF2$qJAT_e}|U zu7SE8ocKl&?i#P{dD1Osmt)=V+s4KiD zOl#xs@z%iF_`v@}@Ni6X2l`QJqBcoh-#pzeClxDtP9Zv3Prkox2mlk$YevOakD zH|r0G4Vt1gYM^{8f(ywp?IO^yiP2(X1~xog%3{8cK#IzwG4!gS-}yo>#1eo2{_Xb- z%=&K>?^>gj{9tvLWAmPYF>};q^WlQ(ln+0G=ppZN1aMw8EJ5Hqr$L6Y`&Hug*zIti zM&_LLM=4t8Gizk8D0J6E%E;5V^`7*#O1aX#`v}XZ1RZK^Z#%aNLkF}-}m61Jd%h`jj z&_DnVjt+snV^)aMlTwyzR)P|a`U(R#hZ#olDSW2DM+sX!g9*o z+dU;>{PZ51cw<0N3{qMSQ8$m`>zcJ_rbwS*d)3Mqdi$2zA^@{+4xSlKj`T)XfyU~= zkIYtX_in?<>zkOWO+Ip>El2pw>H5l!j4$H`uOtL^qQ|$u;Vqlcc{%nREkW-eweeb> z-+Dhd+&dx2>!L|0R*z4NE-&LoSuN+d&dSoIXyBK9AOi)LxdXRS+zIuc`mGYu`I_kd z#r^#FCaqR{pb$p6&?kYQQL~YWy2ZQQ=o#nAtkSL_flvx}8roAoAQ?2bE3d@x@wHPq z4BMHLP;C322n$|=hsa8I1KWQIWSl(JKvEk-A*Fpr3nQCp9g5)>MbWbHOW=d+qyJuBW|-~4$h zLd}Z#aw=q>;$5uI$?L$k6z4UiW98Glie)JLEpPaQm>*^(P@r@bZ!mrcOcAuQD#?c= z9-JmR=(bewx)r;yF*I}5YQPzhb@gvOKCxzW;fF77_|+3e`QUv|r1wks5mZ&_usL)x zsC5eTlF)AW(U?JKP((eL$?xB7P+CVwdO-sZK&{MFUXsiEMjbo-7q_2UKDoA4jr;Gg zPKvhQs#`@ae)eb5?{wiy$uT>_3?q95tyqY{9aeA+cX%F%Ba;-oN92nn|LmCKY*fTcqZURFuX%B zO(>YYO3FeuvRqk!Bj`$FI#r`+vnt0tC1V;N6AUTvJ>`sp0li^hUFNkKRAzNvOGUaZ zANA{GQvPO0ZV)dqW?#s+!n09q|4qGSan4VX!Ai-rUsXDlb;dDU_5P3NEoB-6;8^1W zW=EOY_#I5Te6m&mZ6luR>*Gu8PBI3;obKI!)|V{C70TABFl4hZ=hbOa02JJ#xXHS1 z&qDa|1ik|~=!|A|kW!eWj|zxy5%~4F$s`M<#u7a!1hoBZG5xSv?O4&>y%hpDv`k#BBWijTF1b58QP0`Yetj=LzNedE+^!Qe zb@uB?*8!;|^hXN(>@=`bOgVe9@^TiNX?e7EI_lppg2*a%9bl%HX&h)`Zttl3x{T+;?%X@uIp}u}Eu8GDki}fW-?bt-|F7)aFQ=UGqv;Yv6HjvaG=aXzAz3m;Z)=u+#P069{f`@t@-;%#D)gJiHWt6wpP5 zpuY`$v`yn>znneD6n~vj6LQl0g1GBvr@)2+bR$FWDO?k|_BJ9pGLVPQ{3BjC<60R4 z=7F>}x%B^DpB%rQo&7m7jWB%tBNVUhn$nwVf8A|gnWudoaZ}-gp_y6WP1{G9jE4(u z-Ddn8s<(S)dBVRccYA4m&PD;%$Umw2*{Laig zJtQf1DEM=Q9;qK6gb^gD2wJ?wRplfz&zM zN}V)=^QK8;ww^cKt_r6z&~hBmH3jty*f*<4E2glp@b+E2!6SJXCa{?gJYg{~Gq1_6 z=O*2DEXj!93F`_Z_nzliz3)n zzYmC2cN^Ha#4IU-lG7ihVmqUv=)K&>BZ7*)UkNJWYf$a>_ct7H@keY*!~(VLBW~5c z$njA?=h7oFm`*JReJ`a*faS&oDo_G&G5{nf@x9tnJwcJNLzzL z{~sat%kRqs!`1;}D%a$7CgRd|V_D>pwEn9 zw_k66ygkDCRPV>+__7&*$=reA|9XP@+1iW)i=b(m1F$vmqS^9XDM!rO3TSKEI^A<% z)Rkkdh|fQWJ*WBW*T@S0{4k7ByF^)V((R6%-3f`y{VZ7dHfhU|=f~~VyXw9B?$MXR z{lm0@fm<+>@Y}VBTM%!}v&{+_6?CkY`kKP_`Sk}%+_7kzhSgJExZmezGl}&5Xyh14 z(G{T#odVXbuhTMT2L})M=1HK>2w?D^VzT4G?=C-`zjFj@gst)|I0|C_?$rY7dbf*R z^{Q(Me8se}WmjZ~Sy+b2Xu+i!s`~X&ZnGmPEA%ogD^Lj()mS_s%p~L7l(a z{PVsvh|C8_#$cZ%p;_*gT;Evm#mFH_+@aCbFeu^XLf>$GZwEOlT6x7F_0*HK!N?kS zYOoz`@=0K5an}3JEGxbNUH<+Px{mYS?iafi4S(IVQI@qYfBT!gm~R>MJn|l3215VM zH3$XX&7gq^1 zc~wU?>}u#NA7ypj?^nnH;RtmU-0KZXV{8D3+-9Zdw?)7Ly^&3CrW^d?# z%+cF=rT4uNcDTby1(rn>;@i>yEBk94YKn%1L0yC(so+b<;zmCD8|A07<#tczPUW%-|Y7tKXA5qRm8+KXkIUrSq_6iZXS z!UL?sP7DrS6hruiXiK4Yr29kAXF>&GY{mjG zo3#xoF0)~H*kIfkskDa#UT3F%H^U%_FJ z?F;f_EI?_uY4iAqYqq{3Q#bRE*=?Vob9YoA2%-K=jH{_cC4-oK`!dOxT&o?w>S?gM zg)}67r}BHxVl#sxCQ2=hWe!dWwF(?C^v+aWuToT$#(34n7#3gNaq7zO9L3MWKrQKg z(|leo&en|FesmGTPFX_J!0LO?qhxQp3yyb6(xcznkHrY}4FlTbiE)Yk zFv%~)Qsl66kk{0z-9@ePF=lmImMEyH&{6%}dh|R0yG7KhC0e_6H6an>P0v`|1BmQ|Quk*~4uFwC3FU zC`Jtus^-Pk&s_u~*m!%O>s%NVEp17jV&!ZU{-FLzeSW4@rV5mEr+U}bU;;n_ z3bhYx=B*Tm$j?3UJb#;NODGE%iPD}Gq6G;G57k$TnWsH&n#5orVit|^K5*s0lanssbk~5|_enq07I&0WnNJeayZr{T_ z#A#ivI~Kl=PMoj8>p+Q6)`UPeO-qzMhq`#Q zSEv`T=tv$G`uK|^1C<%7;KZ(xk4m}6j*ShMKC2?8mpj2KgR)WpgdsmQWd(_vd`nkb zmt6f70=}^rFBPfXud5uitJ-{Qn@cBA>esYq6%}&WB(Fa`Gc9ufKbvri0v<$@-hTP8 zoNWy9!2_kb>$skWM!XMt$V*w4@23_N>>$t+xICOt>HPI1h6P{;IcH8~(}j-aVDdHy z4T3{xEp{=7rdr0r_h@CMAb+oK6fnHHx3;ge6#>IrsGe1){P@2bRy?$p;{4lNn5s?G zuv?SnZC;~ahk~Z-JKKNw+4Jib0cQ2}ATNSgNoRJ_>H$ZM5kvhKB@yX^ zktT)9#X9xy^G5ksMQV2u-PcQ`plE12Yfw{H-P?2iJ(fQ{o;<+Yz=!Q^=KUjrpG1sw z1Die^uzoKJ_!&p%zm*9_C@m3FBYDj7s}87BOCR^+OzH7tHwE@*Q>{nXf)E=a>$%_< zX@6Prz%&~FJZ^#eo6r4Q6zO+KE0ej~zrGu_u{eX6rkh22Aa3LwtsO!Vi6Jyi!7i+@ zHX{1|4pRt#e|+(Q%&v39wB?9?KZn&U>L zY_*&k6hP%EsPeb1b&kX69w8~@kM)E`hQM3=J%^Vtvnri+5ZmnQr68SrnWqOCqZkvQ z4Fl0=CtA2-u#AWF{AMoC6iQ3gpUJNA5yZu`F*th17YrCYY`C8aepBu$GuT*dO=&wt zb@f&$-{{1{1iScF+-CT1mVCa+7RO5(P4&;0&k_pz6<0B<^y-`=he`{Met~~jw^uAF zXEKYLiZoCPCluO)_W?lgi4TrL5KWUnY6cBiF^%o=kzNJuocAaDy{}?D%{eQb|9;V6LpQ{%3Tb7AV zB{0XM<|mdcS8gmMH;8c3;8{pK z%{HyJa0(Y@~9~57(soE!2SZq@n4u8GPO8Xevf)}IFrM+E?2WSS&H^{M4i1PKU(R6Gl@5MCf2QD?5WsrPth z0_I2d6!L2gN^#jI{|7QD-MDL>1xChTWvcxle%wg)*caR24H@u5fC!2@dvRRkP+8_s zSxuzH1Y)wh;mh~!TpACYbaCM2lUkdwqm9MJ&l$J1?GQ`~5vMcETf%7N$(G_h^UWM4 z`WbuJB1YD+-`GO8S!7(8P3&r{t#&fM%N-&LcDEg^UVOj0<*XaA9y=6Yu z=UMoVjnirdT#QVC#vwnN0?JA|PX1upEJE~f=z9*(t@sTGHQnMi3f_o(UYH{phSSI3 ztO*DT>D{C#(yKlfEoBl!gxHkwHBNr(Iu|-*67oyTZ&ELAw$O%EE$f$0Qpi9JS@@(i zgJ1^_<@X%W!k{$f9e!Yb4i{feWlSve(AdhDJz1`0f_UXy)ShOzQUC=o4a9M^rtajB zl_H^Y8VwBRQ*E7$R(7X!gJ`EZr05h@Ms8U-IV^(Y;E@}!!$pnN14-pX$ahn!B+%{= zYmPaT0*#SfX))_zoyy>xcr9z1#64 zOiMXq6HdY$Hv&tKHgWnJY%RJnEEHw%?fXDIt+ud>zA39QxoS|q zmKHs3Q3&dDi=OY@PMrqkbUKYTV#!$LGRQQh9P(4yKbNc=Th?NPN?;5lHYI7%JzCD4 z6=dsU`KIylu?(-VQcd<-Hvr@_FsF+pf?jq-iyFu0LT!Luz!xU&bre&Z8bd4C(s#Q; zY1E$%^#yd30zg54Y9N2hgP5OSr(57rbPk^Fe9Lw_fBmF1697B*y~CYHxK!Uww&!Ov zMBqI;%8t3;k8mm6$AOASUKs$#W!_~*9uZz*7`~pqB!~s1f{ZuD?%!a!;BK=)RIH4N zX-gkot~Ks(qmMu{`9vqtC64FK3&ZKM?fnQ$=!cOS6zNt-{lLZJDuj!w;X6(DZ^wWd zKU%GfC6lp$<7JLUpN9XF!`!z3Ag9Qw4O{hA3)bqHik2g4$bY zEpVVZ=evRQtm&+A@Q|p9EWAcN6I0u6fdo58*-Hq%y zppQ1_>T`;wW!!-{8_+_(nbS4*T)I*(oFR+7!^e~H@&erJFx(IGXOIMTvBqFOX}q$C(nMO(;PHkxZ*x~^XN z%CS`KF0T_gR4w>B-q)0Kx*~`}pzR=gKK=2LYW(&&*t#h!GQmrbor7cGDziX1?vsG$ z6>1;Nb7?*Z5QB$5#R#*FBsB^#k)14EZD1zd>Q*41f0HOre$E3~(~i<6zn0ACC%aBg zbbU`}BwXeufCuQM62G`j1(1?xK}VcZHG~{z@PXfU7XVGn`;4!_H_3@>>pZQvU>6|8 zp8L^1DEJmg!y>B>XzTJ7|yX^VM-KXK>e4NJD-USDuen_{Y0hMQ(_`=2o zX#&ZsSJbm*bCEokIDsK=P@SZgCv+2-rPHB&*;CVuQ=;mN>z^vG?^wTuQAQ3MuO_K& zI^7Z3uEwL9wby%7mh+iVZq6TD-uB>WKc_%({dw_MYDrlt4q8;|!rZCbq%Yp#y{KZ?sS{Gz0F!H9u$*v)O&{SOb5Zc>VYiK9{1icYA{;=#JfS6 z%|iNh)l?{IMR^X>r_hXzb3ALR3`U?vz+H-e-IY)_I^z81y@%TIsf1 zlk3JDVK3n8PA@BZw}Rd(MY1rYO^t%JJvJQBvb9fH19nMND30yvM=<%h7a$Wqp75@} zz-s*V!Q)DrU(jlrj)NxZ$9XIV{`1z%9jnOG;);29oK}cpIjhW=_lZ7FA=6{f?PjN z=ZaA9Ob{$I_5sb|#P~i=;j(l5gVN=KphR+cPg3R&7pab!0oxiI40vHjM?Y52O0si- z1Zpm=j$*per23wp3N?qkj}38I7&qf`LvqqKr)07^n?F}DcX9H{wR8~*WEMAfpRgFU zpTzRoql_{MbXc_mwNAZg@ zhvT89rpCXvmKFN_dvt73*_}Rv6M~ZCN3~)aWd_@jLPtD+;X44wxN%SgcjxkH>bE=2 z%|Qww`LAy@Yj7O{#f(RclH+Gq(Z8-Eh<}zM1V`Qr3g{6WEjZE46LOH`cy9Ub-wkd>5|$x_Rj@A$3SIgW>C z>m!)WMyXvI1$l~fkRYIo$$7`P5(P4jDHpWcNGc@KnFy~m+{G4Ggx8Z(`|Jfm6Q9W! zth%H5+p~t&OQ>*io&6;WjavDp>rpBj2((W<4%Hd1SmDiZ;C?6GoKfdzulAtu981-D zx3^bBjFZ7U>wVbj=Qk7~zVr`@ON-N2-G?y#oU3K-YF3hKui6dWk}e%UAMY_R;+fpf z;8r_`{7GRes@*B1P5vBgOme!*;@peVdDT+l2!!P!9sJhZwrmg2Q?}o_!wCqY1zBpv z1%Fj~>53R9s<8h8`Pj_DrEm2fw~jj3subxF+kba)Vrz4@)xun5R28o_tx?=ZvVlVp zJky$-vh9tf+nc)T>V^b#RX@|-fCT73G^!?z2Y%7JWzq;8kh2)|ebnt?)qiemKiWGp zo5*)RP$>SV=}M0)2ls(S{SSHORGDg3*Q@W(%tB743QqaKU34DiosX^t)ECVTusn3> zqi2f;99Q%@Pa=nNSs4-wB^e0xpLucx36t!jXtzxtwicf4Dh}et^o$ITi7wI?bhm$C z8A~nCAiEw{wjuvA;wM+{xpStl2S3U|TK#c%$_+Xe<$e?hFSpRX(}aIO-<-UiE!Xj& zb=KgY(I6_I8sqi;#KnZVs$EOUM)$=UUf|!QZSR!wfFE}o-Qj=fV; zrDpDU?24c^RJy@ZYJ3{s8ga|Rlr;Qwm@*UOrsb{C*cC@WVObCYZ z6cIqUDwX+GZu{Z|FY#%akt1r&VYdsliVq4c!W26=lU`bc#eD)!tCf&)ci)N;sElAO z$PKbbGSooo4yePplw)<{gcj7AbKt)_uvou{x7ZX%kH-`t5A8NzZRe_QG$hAG8!q|_ zl&?r>ssGv)-fLVa!>fg4kZtZKh`BpH>JJ^a9keOT{JX3i&f6$`lEJ_jb3P25SyD&Z znV~JNxn2JOktMdVE2nOlG_Z^@|GqZW)3`qsc|Q(`#eUu{aob#ZL6zF{)?v=Rx90Dur^qB zCUHP3)0ELyzu&|6p=~ibiPYL*xmBNw2YtPb_1y|g`8OjnQFg7gkB$C?o2JRtyMw^c zleE3!!{$ovTnXD9;txKFe$?+AexHA9Wnx*XZK`NRC1Y}4hwJN$Ro*@tZPIryEsVvw zKbu6yB3;QTwUB%y_KxG1M0ofTqM)?iolP#Wi4A01ciw}+ukof}W}qWXyy$Cp40Qf* ziw<`qyfX)d@vet2((xqw3>HL7(Ha~Vkc~pVC+|#RUKN2|Kk(@s&B%qNcRG^}_Sx67 z!w?gyv~8xtnf>M@o{$yCr>~dlsP?HsyXQTKmQ54kjk*NJ&EyGhK2MF^A?X{By$;ys zmp&U;ylk_*{eZVvjZGmgkSOn|go%0cTP224Xmcx=)WER%>QViq#TOGnGED0E{D?m= zAiL~bH~+i?*&`|@O56tmFiPF%pg^+|1YPUmjkj5y@IGwq?*02fEfWsT2^vH`5#mR5 zwbNZnYQV&yTyb@P`apHWE{nJYL(*=|TJana1=;mW2d$lDM zK`fN?9+v2|5R#FD@!eJor+9Y^F9@OJ^8yH!FVt*3i+piv%y#li^HeROwC!5Uu~Og* z`TfgKOo*R$o@3ep`h{?LAJrtRVYdUc&)mH&Ko+`gxyV4j$l4i=b_kgd&YKMqItWl* zWtaUJ3#7nkZ0zXO**@iAkk?-5&@$Kb z5Yp{zAfuyGqeE3a*U8fmselqvX!TMRHUsuHUk7HR{crY>vLIT50 zhvyetKN3eC373s}FQDj#d~UFa(D*QBvOn%x)!2EUn-eTmwCbm@4C2}&K)O6rc^NOe zCLb$w6AD2bNXk@4b#6J=9>+UpktA8}De3A|@pz(c?HcMS1}`@~C0hQ&30TlR056t; zH}NjCp#-uuC%?Zrs;8w8xr-$W*3?o2-8Ka9heH3)vyj1 z-1e92B7YJuY}Zt1o4u~|E4TEri#Wc{(a}>jNN=H8*o~0do?dyjl=MO2N4;UtQOf>| z`q^|}f~VbPnB7<$+QYQz3+cmjh@}C3-1~t+D4;oly@qdjj+r0l`8=}8rS^%N;;up` z)h|0QQScSXJE)Q0sCGn_Ks(J;baH|#mK9FAaO9WmhVS+mSg&OX{$uoPDaabbjE6HN zSNqti$?Y4KPqsn)_t$~uuTBSjN43UqOGst1)g$xLw#$lTOV36W3`F0^iB>g@h27ks zwYd3lc8F>!qwo@AMxqn@`pCy_Nf!dp3SaK6>3^P%7 zW8|VRKF>W@tB+gTB_$smE&i%BIvMQ^rPkRGQh#(*%Hs(xdTR#nX_r0yqZFGb8ST~a zWkV^er6f*BU<4dN0>GySdy)F@uUJN7p2(sg6WNm=X>qXfot}_b>8JPH;4!vcBO<0YpdcGM*c;Ym8lVyid{Y`!QQP(gvb) zve2!bDDNdBFaB*!h`4^hyHcn*xDv+9}`ExLrLy5 zSI_Upz&;+<(Eo#nv582AGGrFY18(8+*za61Vbto{~_9VI{V^!UrqW!bO z$gX?qxult;N6t5V?RE#`GPrRg#V1L30>XbxQ>B+roc@_bR$#psAGT|m+3CvzgKukp zq^zVvKPc?PFk6M+D3GB*%<@qSqVssgQB3@Tjf@<3{fS6C&MIp4=Qh1&a-%aJQ5w(V zp7hmdbQe&{aB5R3=kYuB?z_*w-u_f0Ok&idnUz$(I;SGw0t@FWVN(qfdcLprDMNxB zu=)Z)1^|y{qN>LTKkmkjC}?{cxM%VeqFJc?(hlVHoQBKT_9v=r2egu{qotm}{*=;` zImx`iq?pT{N)`o@!HXXV%X>jK*9kCpIvWtjKNq3*Aqc*oOp9^@2v@Qynx0Noig1=T zZnHd&PX2hFXOCuOiMlG`7JuA{pJ90L`)kXNbWI{>4)llE zbj+$)u=3VK|ECdd0xD`#e9`TT_Ax%!>&*Ox<+t`?1OD(81odB^e-IXI7Ecm8e8dmh zi>lQ-Y6_@Rl#wA_6Tq0lBqU4J5=ecC^>LprY=?m~(F|lzqbRs}m782;ZSd;;QY2By z9_LYRc42zdYIiC`d-YvZtSIVR3naSE$A1+^rAQ(&wO}?|@=xZHwR=KVF3Ui0EXEn& z*)T);a{bxY4UB3cM-v(BEGe@{)a2wASz?Bx69rUc&ct*6bjceq)*LEH=9w=O=i&Smc5~;m8S=qpeogj)ziQOFWu^n>y4Hy=fj4 z==w5k{`z@^hCTnRQSx^3crECA{K7{LbmrFPC1t}(55txm-LAaRBb>Wag>Tm(m=~-y zlA2NsTu#dVdF}1NN~1AF7=*YXkFOJfKg3wI2>E}iWILWQHLNz%!TadQ7~NmXaMh!> z5*?uW@XNfa&R+BZzwp4Mn)fJXz+3_~2d5gW!uMZ_(rKo?qqk$lw6@7U*uL;49c(5M zT5G4K0|Y=_`y($V70+J+4dr_muQ(A33F}+Oi)s}ImrsNaop+9-r}aS9DeteY zn0EljSg9Z;Z^6QWLmfh;lHp>VVVrJ4e;Z+R{}KEt=HkN5{8d(VkFzF5n?Op7YTwOU zzO0I!{gWU|Eczm~_HMiPB*(|qdh6of>)+2U9;HaHntHA1gaxLvEvh8XqRslX>v_0_qwSh z9OnIccqPUrM3JQgJc!4x2xzauK^w?h1?# zxYlhAr2KCr^^mLK6)MB%jA!n?Doz;Gi$gALTaP zm>j!3Gg{Hg`zphCPisaZ*6@>)jJs!0Iei{O21xLLlJEOTkay@hGw9jkTTele%^M$- zMSY2wH+XcR)aq$z(G%wPE@OZ+KjW0?qQkv-f>zaj_otB`G* zmR?TesBH5%lz#uQ=HF!J!aP?=(i%H=Z^*`C!~`WtHO3gFu_kUMlPBh}LlsV|Vj9W;n6LHv!J4!OE1GIDDHT`;DI*V{*kY@_6CMh6-t_GgKc?zLZO7lb8rFeEK>q za3ABtd~xu^Cc0C`nMKQcQEP)aj8n4VhOAD-nR-MjhXM(Fi43uX`{2`bM-j1Na0uCA zdyh{etqT6PselvFUqJ-zGocHwGAy@mg)>wbiziB?J>Q6&ny|5tS8)@T9@F9d9OVKo zMqO|{k9n#8Y?ucSdY7u<<(@(2zb5s(w%HF9D!zQK$_VlsV*2%be%$*W#IyKuhCZGi z?t9pGmiOy!uOG5|1Lh}+#k%wU&PI^CzoX3lD{c7mrjP$3z4_5LRN_SVt%r_n&rj+i zLD8OvMwBYb{RTAaJucSxYi}e59*>;~#NB9mZ^`TP*IwpoiYo6z8=MSF+dG5Rl z0oY=SLpgJ^%F%j%vEEP?%~cs_J5ay)t>Tj7Kw^Hfz4fYhbmH3Nlb~%PWl&t1WtcRZ zdJSOG5~ok1zar}hvm36kH9D6YH~ZQ0dI`uF z>AB&7Gl|N69XKPjOM;BJkKQ=a>u+Xn`T zI`gBDVJ$MhE+j@bo(A{3K}0cH3kM;fah-&9oqtB_Y1F@pG7||O?e<|u8Ue(w_PCSa@*!E>{-WCP7?$bIR&7y#?0Rf!K~;H`p)N_LC+5)8M4 z)tS8w^ew;?aM<~jZ37X>=!)LVtgO5K*{)-!-$zpzg)2;vjx+p-o-;b|no8pv(QsOw z#k=X~vRHX3KkMMi={>ELDYir}G^9-b;ag5J>_jN=LO~vAGHkVb>eU346(Ei3@GP38 z5^L7`hpMFxuvrAjsGP8@9W-sI*}YIVthJ}nwE(D2MPsyt>VKwXB0vhn%4IF4*yz}c zVy5TJx@-niepoCz^=vg$9OR>K>ujBQt3T@dJd54d>Phxb z#;t`kS>T2gI8te_H+eVXR7jTdr#B!B`q|yqkz2CO&m9J`C*ZN>5ii#d3wFyzH2nU9 zU?IGFKHu!pL0J#6+Bl7=#|KfA83I9(x6#ow7jE#)q+EAQ#1mhg>!`niFrp$an2Z`l zSiG}qpOM)CLYkCqxsLR1B4ht&?fOVkwh=QY^5-`|tbB;o*l*oN*K3ZY&Ib|6b_xS$ z$7JcX^Z4-gz6#IP2u^BeC~A0owXkd3Rftzbn=fKFS+4Z-USLC^L??$j3VaCO_*$`g zh^3&MO5k2IRe?XUW0Xvg;IIW2mI2kH3U-n&Qk%J+#*Vynj;SYq(4cUk0UFA?Hun4o z9wfs!iE_S9f`JZ}xO689QrOm=KF5v# zTGz$y+$m8dYND?>O}cv;zLY4X5PH!h9iQtO_S9(49p#s1d|^W{cG&IL?sPTljH95Q zYCuDr>@%^sC{Yw&-VwGVrX*a6N+(*kkQOs+XLJ<3$d8jWfQ@^9==UDL`j;=cO22~$ zhe*eZ<=gogMX7P8=q|I(w#L!D#C%eKKV4=SmAhtlg#}X)w&FbIMnJG`l_Y$$UKiUfhv`80ki;8JCC?W} zKvh~MuooIBUtEtf(gTQmnqQfa45`|MPHfjjWq84<>FA@VwHXsB0GS?yr1O%tV+rK; zqZIcAFElS-%~W%_iyBYfQQtfFi&grIJ|7xiu34={pWybX1#!76+zIj$mC0qaFThxa41+HLo-&V~8D8}?E zy?Y6@nCtW}C?HSi3Ot1}>!No9({QxzRxHFnQNifSq#tUHJUl% zf_k@~tKD|I>YQKf@Kx zWc*&xV2eobnTaDA4z%CqOCFI8xsNNlELw0p^VS=(1u7vW>uD(k2fCipv>$~+Z-b^N zcP9p2@qCTo*5SNWk75csvi_FS^Zs54DlOp8whaTuU9M!(UR?19hyKMYyz9?!G>Fr5 zjU`9Y;(C3gDI(bTS*4##!w+Ji0$Tn5m_z_2$5;n~oXuj9FpL|~D_|;WVm_UKWaO)) zGtTjv;BCud;vdb+_of`>Uqc*>Srr~tC~GTFs0?UaHpr2{ME>!_~%3c$w0ChSGW_tCh$y{r(7$pJTS?j>R`ngqx{(cQu0j2M|USn#>F zj1{&6uRcv-Yo7cajC9tZjts@Jw)PQaUA*Q26d7gq;bD0aTfwp8IndtH9Dc;Y;~n&7cuFdyq1Z=_WmJ7z9ZXwfFJU>Imnef^X5_PH;Y8xtB$%m}tE}7?FxO zPr=gYdg03-g*3wI@3E9&YW=AnRlX1<5b>y+ts;wc4Zsfp+F5+K(Fp>Cpub(juOjyX zl1^sm_kRzkSpMCH+8*Zj0)L`eVvfa&I@9fw;>CqLUU{tSgLq7P_^2xNj`a+~@)!03 zCbL>&IrHH^;t$)CA!3(Y&IOfx&Wje_r5eZb@w*aNM0{=3+;yYI^)Cg=qQ1}1YGyw1 zn5++T3QrlBd*#;;+YBii?`wT%BDA>H=y19#R`l^M$FX7B!z6LCB)zlMYBR{nuQLok z9Q-Q>>vfj9(pqQSJ6vV$kEDEZhYm?M@1qCZFZe4Ey9SM`R3|;U{(9t1&-JU)Kws@CC56d2;zDLZnJ&(~hPgu!L{0g$|<}24oXku+BeffKwAyFx#B=zC$ zZ#1CzL}eKpH?J4PeGUc14Nt96*`Ok?8BsHj>P6Fbh zK;5SG>0Ro%@8SRzU(j3m^NjmXlLGO;uGWs&ec4858iTE?e5s^tCRzV5Z& zjPkbyUX5qF9jzK6v40ZQsN(%KQkz%UEsH~LzOqDR`(5!I>&~xf1)1Ml2v|%VghE{0 zx5OfA9SfAdA}>^WhF3HF__MchXn1?-P;gf_ajNgF$|A%HjwbMPHIV~LaUuar5r`=_oSXf!fbyUUz8oh#*)2jg zH2GY*(#cANzpNurIwkbW;wkb0asinl)q@m~0C!k+p16{h@d?gaDU-R?MOF{Fm)(C% zFL3N!tbLUVYNX>B2Bb<8^)4YhQEWAO1v8!W-x%@fA-~($)do?=_o!|Ay`4enrxUe4 z9M=k|d2e2#c+S_!j6aiHB=8lRR&5>h-Wy!d;7h`F&9*KYy~qI=>9Ad`SxDvWk<=%$Jy`>d)Us)cm{UqQ_t?gCnzyuQ8c})1X3m*F|wBLaZvdw4`M73SFQY% zI$0mJb^BdwlrMMORA!m!v%Vv}HCsZo_<_rDzC zYACeWVWX+gbX6|qw`*fPfYl`VPMVE@OC(64zgf(6g<(<&fMN)FlKM27@ZR)+{7oM) ztZgFD^X{G$n-QuSTdF;T?ZZ5st(2Ewh7D$A|S&% z3e+=X;M?Q@eg(I2e`@YRijl}R@qoM9BXQX?e)6x222Qb#LH`R8S!^&`Xw_|8uvqr~ z$S+#tsKk&S_yRtd6i5MI>QyKnl(-0BMr2Y^Ph;=}N-#I|{EzrF3oiw;2;IFd)WFv` z6Puc9BL&g`Ym-aErbmrLm(YZl&>&hCmp#Wv z^j_Njwt{UFj4PHZMKjsrQ`xl(A^5$$YskrRCj(1}s?|=ron|vRDE!v*pCfy+c0xg+H0S+|CFHgWL~^Jj=tbcm0^m>JOGnT_xnrqv(=Sj_>3C% zR+(I7KgXL7Ag!0EwsQM`eF(5V!hU=Lm~#$=mmA?5@i3UF6WC>Hx>iYygc}qK;pDX> z*|$=PndXJ^7DEuATx+&stry^&G0%aRG?pE#ND-bI=(?L0^REB^%y-YF0rH3Xf+u@jvZeF+M7JsV? zJDJTA)c;oPwv|>fSO}m{YtiFATKJtt4$ka!!p`(S9f+Ou*gj@_%H22ZwcomN*Wzt> zualJ}`8xp-Fhaj)GC=!KjFpSssd#-aKUP*UP-`^3vsv#zEmRDX`NX<;l3Rgu`*2Y+ zd%)qw7Rn22^*m^#ZrNa|Kow6rc9^N@R4Fm zNn@P#8<#S*Z zQ}!vRa%ed$mDgk^FDtYDuUpd z78kSq4H{dCrjivk{Ng29h~8Nq=<@4DJ)Wiej56ew7QQyygh4>fEf5dWx?%qX0-DZ# zq;Jg;sUd|&M#L@Bwy~*nd%7c3q05SLD3tbFUadB!YH_xRuEy1{R@95THP1u_1uS3sG>S2%s0xbok7Pk#<>*tTcf?;~1xQC{mcQJM8xioke4M3> z5v?6?-Y#clv_*}~=G7hxO9jb>l4)|w9D;W6Gq^p`875?hvgm2Q@Ol`i?wDTR@!@g( z6Zcwg#252bTZ~Aq8kj$BWGc~hO{CKiv6_<9ygu`WhtNLeiYLn{Ojkw1zZ2T=?p@Fp z+FpA}*!$bz3F>&vW3*7NfFZ?n^1Hd3w^S}wlWhYX@=?VR$;qK!X4*O3b8EFY5T1Ja zKt0m&@(P*xP(QQSGNTZ1K~V(>&2hQu$>owvQ-ORT^`8f2Gq9Iaq1s#f%p zI8^%uqIryRQa4pOo(N!?>NS zl93d=A+K%hOdb>6U=%|z5TJ7KZgq_`zd&m4^XS8+k%5K5 zFh|9`Mn1lr#|6s$2RA0+SfFAl0wVvOPSb|%0PEp(SI?B5$LO>yV?3N8iKgm}`E_}2 ziuHG=$^1wUR;Bl(AcB!Z@f2AI5}F1nOCLHb=Mgq=RJvhec@w)NMT#1h_mBiWRMy^sad!}$JwD3sLK_`PD$gvS&P z?>|ft`N&5*m%aVn$f@u|eD-4HG%OKpCU@+n|5)fAo~vN4>Zryie;qeom~&L9TRFdm zo5>e*Td0%;7>L%TMMWRid)H-Acy~MnFUrzlYM@ zUXEIMIKEd8$dNR$z2UnDFK+j{nQPrt7P46!2-pjVgy@NES^OxA%%_?RH#I`IqopF5|t+NBOj!x_4sgRnXiZu_4@Vxpo)HD1JNY9^YI#faPAqQnVuM(dg1 zDwTder1$VnvYyA# z&FrhtXq7a>Dw6y3)4Ue<-$SWmiFU7Uop2v<>1li_f_Ex<>fh!U#!F-2G+b_)Czl(v z#L8un*tAw$R6$ZudrQM0hpsne2WOaFIoJ^@ARqvUK*)1Xx#|qR6J!%Pj;$j>HMpJ0 z31KR=DS8WzB+)&nTg4wp$2`_uX7?NP3ij>nbbNOs`W#Ma?Fnq(L>L_TPrJYls>Yo- zZ?4vo=Mq*L|JF8yt%7c%YT#&E+sO6$L7KWiN>t;$#G2o0&^a5dVVS5%INQ1#hd z9*9qtUGc~Ud>PsKJlQL;QVuOethl0-EM=p=u*=61Nj(-MzEEz!q-n-a#)$Yt4p)nY z@P9D&-P<%#E?peRdHbhC16<=PZCxqbU<58boop1~ib*%3vlONGoJi$%2IY^|OX)lC zLA=9&9u&U>aK;dQQ!^VL{bBo+ZTL>a$86QN@)6b1N0V}$LNU*k4a}?E?1DHbn~8mH zRWB8_t{aI3uFP8gnQa333O`chi<55@miJ)wSB-$%Ln<~qpK4-j16h;#xY!o9o7k7i zL(E6P^^6Z|Cxq>L0iQbO6P(rBr*~JoB-U_o1f-xRRHDJ&LAjv2mlNH5v zPkT&gAbM&}cEK--o^Sdik({SVRVqYZ*40H?;bO9YwZ>AYqQD>B9d<*>T*4qzA&P4^ zJm^)uJd5GCxY-akOJ!U{6PfN*$Kwpb8W*FkoCeTxJ%N}&di9qB$YxGt_~S8cyv9*ef5O1F3&M7? zlq}~&5hLQpoMG*881aAe8FA7dh?`zPY&_?i;;NLI42A5BCrD?+4#qjtVYeVC*YweZQQW1XJ_Rxr z#%zt0muFxHK60*UCQ*vJrNs?zL84eDh_!ZaDCg(waQ!(Xl$M;s(f#1OcaO*I5wFnp zs=@ZYg6Ce`s$8X4Y?;=fL!0GwpEE7{VW(LC_{{BQho*y&56ip76|E>r@l|45{PZ{h zpT#_PKaj+F-7Md5=zbVymaBe`Je6K@CnOXemXlf?D84mVGqCFgI(uCiN(4I=KcnhK zeLD&Lp3dZO!j5$zKnnUnn>_fN!qK@XcgHiHRh&KFGHj?bzj!VlJgGE*a3vh*IM}i) zbY1H{s8e%E?yk#I5109f6rB&AUaU@=fk>R^92eh*!|OkXM0V%qQ=LXbrdD&EWtS@e zpW+w5iB|gHJt0S}T14KF%Jzdm`H+efG`@1*olrb!zuzP1kDZ$R%6A1)7ACEaVO-@(KI8VQ47|I~|m2i<^0eYMJQ�??<+EIiJhIzyKg;4nllA> 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%0Axe^MwEDyB4zAET;$uNv>nQw=>nH(E|A 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 HI9&T5pE=#a literal 0 HcmV?d00001 diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.xml b/modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.xml new file mode 100644 index 00000000..5d8a06ab --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/docs/_static/huggingface-model-import.xml @@ -0,0 +1,2 @@ + +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 \ No newline at end of file diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/pipeline_constructs/build_pipeline_construct.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/pipeline_constructs/build_pipeline_construct.py new file mode 100644 index 00000000..9da4e612 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/pipeline_constructs/build_pipeline_construct.py @@ -0,0 +1,288 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from typing import Any + +import aws_cdk +from aws_cdk import Aws +from aws_cdk import aws_cloudwatch as cloudwatch +from aws_cdk import aws_codebuild as codebuild +from aws_cdk import aws_codecommit as codecommit +from aws_cdk import aws_codepipeline as codepipeline +from aws_cdk import aws_codepipeline_actions as codepipeline_actions +from aws_cdk import aws_iam as iam +from aws_cdk import aws_s3 as s3 +from aws_cdk import aws_s3_assets as s3_assets +from constructs import Construct + + +class BuildPipelineConstruct(Construct): + def __init__( + self, + scope: Construct, + construct_id: str, + project_name: str, + project_id: str, + s3_artifact: s3.IBucket, + repo_asset: s3_assets.Asset, + model_package_group_name: str, + hf_access_token_secret: str, + hf_model_id: str, + **kwargs: Any, + ) -> None: + super().__init__(scope, construct_id, **kwargs) + + # Define resource names + codepipeline_name = f"{project_name}-{construct_id}" + + sagemaker_pipeline_name = f"{project_name}-{project_id}" + sagemaker_pipeline_description = f"{project_name} Model Build Pipeline" + + # Create source repo from seed bucket/key + build_app_repository = codecommit.Repository( + self, + "Build App Code Repo", + repository_name=f"{project_name}-{construct_id}", + code=codecommit.Code.from_asset( + asset=repo_asset, + branch="main", + ), + ) + aws_cdk.Tags.of(build_app_repository).add("sagemaker:project-id", project_id) + aws_cdk.Tags.of(build_app_repository).add("sagemaker:project-name", project_name) + + sagemaker_seedcode_bucket = s3.Bucket.from_bucket_name( + self, "SageMaker Seedcode Bucket", f"sagemaker-{Aws.REGION}-{Aws.ACCOUNT_ID}" + ) + + codebuild_role = iam.Role( + self, + "CodeBuild Role", + assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"), + path="/service-role/", + ) + + sagemaker_execution_role = iam.Role( + self, + "SageMaker Execution Role", + assumed_by=iam.ServicePrincipal("sagemaker.amazonaws.com"), + path="/service-role/", + ) + + # Create a policy statement for SageMaker pull + sagemaker_policy = iam.Policy( + self, + "SageMaker Policy", + document=iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + actions=[ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents", + ], + resources=["*"], + ), + iam.PolicyStatement( + actions=[ + "ecr:BatchCheckLayerAvailability", + "ecr:BatchGetImage", + "ecr:Describe*", + "ecr:GetAuthorizationToken", + "ecr:GetDownloadUrlForLayer", + ], + resources=["*"], + ), + iam.PolicyStatement( + actions=[ + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:Decrypt", + "kms:DescribeKey", + ], + effect=iam.Effect.ALLOW, + resources=[f"arn:{Aws.PARTITION}:kms:{Aws.REGION}:{Aws.ACCOUNT_ID}:key/*"], + ), + ] + ), + ) + + cloudwatch.Metric.grant_put_metric_data(sagemaker_policy) + sagemaker_execution_role.grant_pass_role(sagemaker_policy) # type: ignore[arg-type] + s3_artifact.grant_read_write(sagemaker_policy) + sagemaker_seedcode_bucket.grant_read_write(sagemaker_policy) + + # Attach the policy + sagemaker_policy.attach_to_role(sagemaker_execution_role) + sagemaker_policy.attach_to_role(codebuild_role) + + # Grant extra permissions for the SageMaker role + sagemaker_execution_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "sagemaker:CreateModel", + "sagemaker:DeleteModel", + "sagemaker:DescribeModel", + "sagemaker:AddTags", + "sagemaker:DeleteTags", + "sagemaker:ListTags", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model/*", + ], + ), + ) + sagemaker_execution_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "sagemaker:CreateModelPackageGroup", + "sagemaker:DeleteModelPackageGroup", + "sagemaker:DescribeModelPackageGroup", + "sagemaker:AddTags", + "sagemaker:DeleteTags", + "sagemaker:ListTags", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model-package-group/{model_package_group_name}" + ], + ), + ) + sagemaker_execution_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "sagemaker:CreateModelPackage", + "sagemaker:DeleteModelPackage", + "sagemaker:UpdateModelPackage", + "sagemaker:DescribeModelPackage", + "sagemaker:ListModelPackages", + "sagemaker:AddTags", + "sagemaker:DeleteTags", + "sagemaker:ListTags", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model-package/{model_package_group_name}/*" + ], + ), + ) + + # Grant extra permissions for the CodeBuild role + codebuild_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "sagemaker:DescribeModelPackage", + "sagemaker:ListModelPackages", + "sagemaker:UpdateModelPackage", + "sagemaker:AddTags", + "sagemaker:DeleteTags", + "sagemaker:ListTags", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model-package/{model_package_group_name}/*" + ], + ), + ) + codebuild_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "sagemaker:CreatePipeline", + "sagemaker:UpdatePipeline", + "sagemaker:DeletePipeline", + "sagemaker:StartPipelineExecution", + "sagemaker:StopPipelineExecution", + "sagemaker:DescribePipelineExecution", + "sagemaker:ListPipelineExecutionSteps", + "sagemaker:AddTags", + "sagemaker:DeleteTags", + "sagemaker:ListTags", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:pipeline/{sagemaker_pipeline_name}", + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:pipeline/{sagemaker_pipeline_name}/execution/*", + ], + ), + ) + codebuild_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "s3:CreateBucket", + ], + resources=[sagemaker_seedcode_bucket.bucket_arn], + ) + ) + + # Create the CodeBuild project + sm_pipeline_build = codebuild.PipelineProject( + self, + "SM Pipeline Build", + project_name=f"{project_name}-{construct_id}", + role=codebuild_role, + build_spec=codebuild.BuildSpec.from_source_filename("buildspec.yml"), + environment=codebuild.BuildEnvironment( + build_image=codebuild.LinuxBuildImage.STANDARD_5_0, + environment_variables={ + "SAGEMAKER_PROJECT_NAME": codebuild.BuildEnvironmentVariable(value=project_name), + "SAGEMAKER_PROJECT_ID": codebuild.BuildEnvironmentVariable(value=project_id), + "MODEL_PACKAGE_GROUP_NAME": codebuild.BuildEnvironmentVariable(value=model_package_group_name), + "AWS_REGION": codebuild.BuildEnvironmentVariable(value=Aws.REGION), + "SAGEMAKER_PIPELINE_NAME": codebuild.BuildEnvironmentVariable( + value=sagemaker_pipeline_name, + ), + "SAGEMAKER_PIPELINE_DESCRIPTION": codebuild.BuildEnvironmentVariable( + value=sagemaker_pipeline_description, + ), + "SAGEMAKER_PIPELINE_ROLE_ARN": codebuild.BuildEnvironmentVariable( + value=sagemaker_execution_role.role_arn, + ), + "ARTIFACT_BUCKET": codebuild.BuildEnvironmentVariable(value=s3_artifact.bucket_name), + "ARTIFACT_BUCKET_KMS_ID": codebuild.BuildEnvironmentVariable( + value=s3_artifact.encryption_key.key_id # type: ignore[union-attr] + ), + "HUGGING_FACE_ACCESS_TOKEN_SECRET": codebuild.BuildEnvironmentVariable( + value=hf_access_token_secret + ), # pass secret + "HUGGING_FACE_MODEL_ID": codebuild.BuildEnvironmentVariable(value=hf_model_id), + }, + ), + ) + + source_artifact = codepipeline.Artifact(artifact_name="GitSource") + + build_pipeline = codepipeline.Pipeline( + self, "Pipeline", pipeline_name=codepipeline_name, artifact_bucket=s3_artifact + ) + + # add a source stage + source_stage = build_pipeline.add_stage(stage_name="Source") + source_stage.add_action( + codepipeline_actions.CodeCommitSourceAction( + action_name="Source", + output=source_artifact, + repository=build_app_repository, + branch="main", + ) + ) + + # add a build stage + build_stage = build_pipeline.add_stage(stage_name="Build") + build_stage.add_action( + codepipeline_actions.CodeBuildAction( + action_name="SMPipeline", + input=source_artifact, + project=sm_pipeline_build, + ) + ) diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/product_stack.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/product_stack.py new file mode 100644 index 00000000..3c148579 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/product_stack.py @@ -0,0 +1,216 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +from typing import Any + +import aws_cdk +import aws_cdk.aws_servicecatalog as servicecatalog +from aws_cdk import Aws, Tags +from aws_cdk import aws_iam as iam +from aws_cdk import aws_kms as kms +from aws_cdk import aws_s3 as s3 +from aws_cdk import aws_s3_assets as s3_assets +from aws_cdk import aws_sagemaker as sagemaker +from constructs import Construct + +from templates.hf_import_models.pipeline_constructs.build_pipeline_construct import BuildPipelineConstruct + + +class Product(servicecatalog.ProductStack): + DESCRIPTION: str = "Enables the import of Hugging Face models" + TEMPLATE_NAME: str = "Hugging Face Model Import" + + def __init__( + self, + scope: Construct, + construct_id: str, + build_app_asset: s3_assets.Asset, + deploy_app_asset: s3_assets.Asset, + **kwargs: Any, + ) -> None: + super().__init__(scope, construct_id) + + # Define required parmeters + project_name = aws_cdk.CfnParameter( + self, + "SageMakerProjectName", + type="String", + description="The name of the SageMaker project.", + min_length=1, + max_length=32, + ).value_as_string + + project_id = aws_cdk.CfnParameter( + self, + "SageMakerProjectId", + type="String", + min_length=1, + max_length=16, + description="Service generated Id of the project.", + ).value_as_string + + staging_account = aws_cdk.CfnParameter( + self, + "StgAccountId", + type="String", + min_length=1, + max_length=16, + description="Staging account id.", + ).value_as_string + + prod_account = aws_cdk.CfnParameter( + self, + "ProdAccountId", + type="String", + min_length=1, + max_length=16, + description="Prod account id.", + ).value_as_string + + hf_access_token_secret = aws_cdk.CfnParameter( + self, + "HFAccessTokenSecret", + type="String", + min_length=1, + description="AWS Secret Of Hugging Face Access Token", + ).value_as_string + + hf_model_id = aws_cdk.CfnParameter( + self, + "HFModelID", + type="String", + min_length=1, + description="Model ID from hf.co/models", + ).value_as_string + + Tags.of(self).add("sagemaker:project-id", project_id) + Tags.of(self).add("sagemaker:project-name", project_name) + + # create kms key to be used by the assets bucket + kms_key_artifact = kms.Key( + self, + "Artifacts Bucket KMS Key", + description="key used for encryption of data in Amazon S3", + enable_key_rotation=True, + policy=iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + actions=["kms:*"], + effect=iam.Effect.ALLOW, + resources=["*"], + principals=[iam.AccountRootPrincipal()], + ) + ] + ), + ) + + # allow cross account access to the kms key + kms_key_artifact.add_to_resource_policy( + iam.PolicyStatement( + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + ], + resources=[ + "*", + ], + principals=[ + iam.AccountPrincipal(staging_account), + iam.AccountPrincipal(prod_account), + ], + ) + ) + + s3_artifact = s3.Bucket( + self, + "S3 Artifact", + bucket_name=f"mlops-{project_name}-{Aws.ACCOUNT_ID}", # Bucket name has a limit of 63 characters + encryption_key=kms_key_artifact, + versioned=True, + removal_policy=aws_cdk.RemovalPolicy.DESTROY, + enforce_ssl=True, # Blocks insecure requests to the bucket + ) + + # DEV account access to objects in the bucket + s3_artifact.grant_read_write(iam.AccountRootPrincipal()) + + # PROD account access to objects in the bucket + s3_artifact.grant_read_write(iam.AccountPrincipal(staging_account)) + s3_artifact.grant_read_write(iam.AccountPrincipal(prod_account)) + + # cross account model registry resource policy + model_package_group_name = f"{project_name}-{project_id}" + model_package_group_policy = iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + sid="ModelPackageGroup", + actions=[ + "sagemaker:DescribeModelPackageGroup", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model-package-group/{model_package_group_name}" + ], + principals=[ + iam.AccountPrincipal(staging_account), + iam.AccountPrincipal(prod_account), + ], + ), + iam.PolicyStatement( + sid="ModelPackage", + actions=[ + "sagemaker:DescribeModelPackage", + "sagemaker:ListModelPackages", + "sagemaker:UpdateModelPackage", + "sagemaker:CreateModel", + ], + resources=[ + f"arn:{Aws.PARTITION}:sagemaker:{Aws.REGION}:{Aws.ACCOUNT_ID}:model-package/{model_package_group_name}/*" + ], + principals=[ + iam.AccountPrincipal(staging_account), + iam.AccountPrincipal(prod_account), + ], + ), + ] + ).to_json() + + sagemaker.CfnModelPackageGroup( + self, + "Model Package Group", + model_package_group_name=model_package_group_name, + model_package_group_description=f"Model Package Group for {project_name}", + model_package_group_policy=model_package_group_policy, + tags=[ + aws_cdk.CfnTag(key="sagemaker:project-id", value=project_id), + aws_cdk.CfnTag(key="sagemaker:project-name", value=project_name), + ], + ) + + BuildPipelineConstruct( + self, + "build", + project_name=project_name, + project_id=project_id, + s3_artifact=s3_artifact, + repo_asset=build_app_asset, + model_package_group_name=model_package_group_name, + hf_access_token_secret=hf_access_token_secret, + hf_model_id=hf_model_id, + ) diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/README.md b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/README.md new file mode 100644 index 00000000..2e986925 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/README.md @@ -0,0 +1,12 @@ +# SageMaker Build - Train Pipelines + +This folder contains all the SageMaker Pipelines of your project. + +`buildspec.yml` defines how to run a pipeline after each commit to this repository. +`ml_pipelines/` contains the SageMaker pipelines definitions. +The expected output of your main pipeline (here `training/pipeline.py`) is a model registered to SageMaker Model Registry. + +`tests/` contains the unittests for your `source_scripts/` + +`notebooks/` contains experimentation notebooks. + diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/buildspec.yml b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/buildspec.yml new file mode 100644 index 00000000..5603158e --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/buildspec.yml @@ -0,0 +1,18 @@ +version: 0.2 + +phases: + install: + runtime-versions: + python: 3.8 + commands: + - pip install --upgrade --force-reinstall . "awscli>1.20.30" + + build: + commands: + - export PYTHONUNBUFFERED=TRUE + - | + run-pipeline --module-name ml_pipelines.training.pipeline \ + --role-arn $SAGEMAKER_PIPELINE_ROLE_ARN \ + --tags "[{\"Key\":\"sagemaker:project-name\", \"Value\":\"${SAGEMAKER_PROJECT_NAME}\"}, {\"Key\":\"sagemaker:project-id\", \"Value\":\"${SAGEMAKER_PROJECT_ID}\"}]" \ + --kwargs "{\"region\":\"${AWS_REGION}\",\"role\":\"${SAGEMAKER_PIPELINE_ROLE_ARN}\",\"default_bucket\":\"${ARTIFACT_BUCKET}\",\"pipeline_name\":\"${SAGEMAKER_PIPELINE_NAME}\",\"model_package_group_name\":\"${MODEL_PACKAGE_GROUP_NAME}\",\"hugging_face_model_id\":\"${HUGGING_FACE_MODEL_ID}\"}" + - echo "Create/Update of the SageMaker Pipeline and execution completed." diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/README.md b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/README.md new file mode 100644 index 00000000..1f7850d8 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/README.md @@ -0,0 +1,8 @@ +# SageMaker Pipelines + +This folder contains SageMaker Pipeline definitions and helper scripts to either simply "get" a SageMaker Pipeline definition (JSON dictionnary) with `get_pipeline_definition.py`, or "run" a SageMaker Pipeline from a SageMaker pipeline definition with `run_pipeline.py`. + +Those files are generic and can be reused to call any SageMaker Pipeline. + +Each SageMaker Pipeline definition should be be treated as a module inside its own folder, for example here the +"training" pipeline, contained inside `training/`. diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__init__.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__init__.py new file mode 100644 index 00000000..ff79f21c --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__init__.py @@ -0,0 +1,30 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# © 2021 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. This +# AWS Content is provided subject to the terms of the AWS Customer Agreement +# available at http://aws.amazon.com/agreement or other written agreement between +# Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL +# or both. +# +# Any code, applications, scripts, templates, proofs of concept, documentation +# and other items provided by AWS under this SOW are "AWS Content," as defined +# in the Agreement, and are provided for illustration purposes only. All such +# AWS Content is provided solely at the option of AWS, and is subject to the +# terms of the Addendum and the Agreement. Customer is solely responsible for +# using, deploying, testing, and supporting any code and applications provided +# by AWS under this SOW. diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__version__.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__version__.py new file mode 100644 index 00000000..660d19ee --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/__version__.py @@ -0,0 +1,26 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""Metadata for the ml pipelines package.""" + +__title__ = "ml_pipelines" +__description__ = "ml pipelines - template package" +__version__ = "0.0.1" +__author__ = "" +__author_email__ = "" +__license__ = "Apache 2.0" +__url__ = "" diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/_utils.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/_utils.py new file mode 100644 index 00000000..12a5b559 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/_utils.py @@ -0,0 +1,93 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# © 2021 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. This +# AWS Content is provided subject to the terms of the AWS Customer Agreement +# available at http://aws.amazon.com/agreement or other written agreement between +# Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL +# or both. +# +# Any code, applications, scripts, templates, proofs of concept, documentation +# and other items provided by AWS under this SOW are "AWS Content," as defined +# in the Agreement, and are provided for illustration purposes only. All such +# AWS Content is provided solely at the option of AWS, and is subject to the +# terms of the Addendum and the Agreement. Customer is solely responsible for +# using, deploying, testing, and supporting any code and applications provided +# by AWS under this SOW. + +# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Provides utilities for SageMaker Pipeline CLI.""" + +from __future__ import absolute_import + +import ast +from typing import Any, Dict, Optional + + +def get_pipeline_driver(module_name: str, passed_args: Optional[str] = None) -> Any: + """Gets the driver for generating your pipeline definition. + + Pipeline modules must define a get_pipeline() module-level method. + + Args: + module_name: The module name of your pipeline. + passed_args: Optional passed arguments that your pipeline may be templated by. + + Returns: + The SageMaker Workflow pipeline. + """ + _imports = __import__(module_name, fromlist=["get_pipeline"]) + kwargs = convert_struct(passed_args) + return _imports.get_pipeline(**kwargs) + + +def convert_struct(str_struct: Optional[str] = None) -> Any: + """convert the string argument to it's proper type + + Args: + str_struct (str, optional): string to be evaluated. Defaults to None. + + Returns: + string struct as it's actuat evaluated type + """ + return ast.literal_eval(str_struct) if str_struct else {} + + +def get_pipeline_custom_tags(module_name: str, args: Optional[str], tags: Dict[str, Any]) -> Any: + """Gets the custom tags for pipeline + + Returns: + Custom tags to be added to the pipeline + """ + try: + _imports = __import__(module_name, fromlist=["get_pipeline_custom_tags"]) + kwargs = convert_struct(args) + return _imports.get_pipeline_custom_tags(tags, kwargs["region"], kwargs["sagemaker_project_arn"]) + except Exception as e: + print(f"Error getting project tags: {e}") + return tags diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/get_pipeline_definition.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/get_pipeline_definition.py new file mode 100644 index 00000000..53535920 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/get_pipeline_definition.py @@ -0,0 +1,74 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +"""A CLI to get pipeline definitions from pipeline modules.""" + +from __future__ import absolute_import + +import argparse +import sys + +from ml_pipelines._utils import get_pipeline_driver + + +def main() -> None: # pragma: no cover + """The main harness that gets the pipeline definition JSON. + + Prints the json to stdout or saves to file. + """ + parser = argparse.ArgumentParser("Gets the pipeline definition for the pipeline script.") + + parser.add_argument( + "-n", + "--module-name", + dest="module_name", + type=str, + help="The module name of the pipeline to import.", + ) + parser.add_argument( + "-f", + "--file-name", + dest="file_name", + type=str, + default=None, + help="The file to output the pipeline definition json to.", + ) + parser.add_argument( + "-kwargs", + "--kwargs", + dest="kwargs", + default=None, + help="Dict string of keyword arguments for the pipeline generation (if supported)", + ) + args = parser.parse_args() + + if args.module_name is None: + parser.print_help() + sys.exit(2) + + pipeline = get_pipeline_driver(args.module_name, args.kwargs) + content = pipeline.definition() + if args.file_name: + with open(args.file_name, "w") as f: + f.write(content) + else: + print(content) + + +if __name__ == "__main__": + main() diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/run_pipeline.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/run_pipeline.py new file mode 100644 index 00000000..f0e12338 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/run_pipeline.py @@ -0,0 +1,106 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +"""A CLI to create or update and run pipelines.""" + +from __future__ import absolute_import + +import argparse +import json +import sys + +from ml_pipelines._utils import convert_struct, get_pipeline_custom_tags, get_pipeline_driver + + +def main() -> None: # pragma: no cover + """The main harness that creates or updates and runs the pipeline. + + Creates or updates the pipeline and runs it. + """ + parser = argparse.ArgumentParser("Creates or updates and runs the pipeline for the pipeline script.") + + parser.add_argument( + "-n", + "--module-name", + dest="module_name", + type=str, + help="The module name of the pipeline to import.", + ) + parser.add_argument( + "-kwargs", + "--kwargs", + dest="kwargs", + default=None, + help="Dict string of keyword arguments for the pipeline generation (if supported)", + ) + parser.add_argument( + "-role-arn", + "--role-arn", + dest="role_arn", + type=str, + help="The role arn for the pipeline service execution role.", + ) + parser.add_argument( + "-description", + "--description", + dest="description", + type=str, + default=None, + help="The description of the pipeline.", + ) + parser.add_argument( + "-tags", + "--tags", + dest="tags", + default=None, + help="""List of dict strings of '[{"Key": "string", "Value": "string"}, ..]'""", + ) + args = parser.parse_args() + + if args.module_name is None or args.role_arn is None: + parser.print_help() + sys.exit(2) + tags = convert_struct(args.tags) + + pipeline = get_pipeline_driver(args.module_name, args.kwargs) + print("###### Creating/updating a SageMaker Pipeline with the following definition:") + parsed = json.loads(pipeline.definition()) + print(json.dumps(parsed, indent=2, sort_keys=True)) + + all_tags = get_pipeline_custom_tags(args.module_name, args.kwargs, tags) + + upsert_response = pipeline.upsert(role_arn=args.role_arn, description=args.description, tags=all_tags) + + upsert_response = pipeline.upsert( + role_arn=args.role_arn, description=args.description + ) # , tags=tags) # Removing tag momentaneously + print("\n###### Created/Updated SageMaker Pipeline: Response received:") + print(upsert_response) + + execution = pipeline.start() + print(f"\n###### Execution started with PipelineExecutionArn: {execution.arn}") + + # TODO removiong wait time as training can take some time + print("Waiting for the execution to finish...") + execution.wait() + print("\n#####Execution completed. Execution step details:") + + print(execution.list_steps()) + + +if __name__ == "__main__": + main() diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/README.md b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/README.md new file mode 100644 index 00000000..11c532c6 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/README.md @@ -0,0 +1,15 @@ +# Deploying HuggingFace LLM on SageMaker Pipeline. + +This SageMaker Pipeline definition creates a workflow that will: + +Retrieve the Docker image URI for the HuggingFace Language Model (LLM). + +Create a HuggingFaceModel instance with the specified role, image URI, and environment variables (model ID, GPU count, input/output lengths, batch processing limits, and access token). + +Register the HuggingFaceModel for deployment through the RegisterModel step. + +Configure the content types, response types, and instance types for inference. + +Specify the model package group name and set the initial approval status to "PendingManualApproval". + +Create the SageMaker Pipeline instance with the RegisterModel step and pipeline parameters. diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/__init__.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/__init__.py new file mode 100644 index 00000000..ff79f21c --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/__init__.py @@ -0,0 +1,30 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# © 2021 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. This +# AWS Content is provided subject to the terms of the AWS Customer Agreement +# available at http://aws.amazon.com/agreement or other written agreement between +# Customer and either Amazon Web Services, Inc. or Amazon Web Services EMEA SARL +# or both. +# +# Any code, applications, scripts, templates, proofs of concept, documentation +# and other items provided by AWS under this SOW are "AWS Content," as defined +# in the Agreement, and are provided for illustration purposes only. All such +# AWS Content is provided solely at the option of AWS, and is subject to the +# terms of the Addendum and the Agreement. Customer is solely responsible for +# using, deploying, testing, and supporting any code and applications provided +# by AWS under this SOW. diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/_utils.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/_utils.py new file mode 100644 index 00000000..933302ae --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/_utils.py @@ -0,0 +1,90 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import logging +from typing import Any, Dict, List + +import sagemaker.session +from botocore.exceptions import ClientError + +logger = logging.getLogger(__name__) + + +def resolve_ecr_uri_from_image_versions( + sagemaker_session: sagemaker.session.Session, image_versions: List[Dict[str, Any]], image_name: str +) -> Any: + """Gets ECR URI from image versions + Args: + sagemaker_session: boto3 session for sagemaker client + image_versions: list of the image versions + image_name: Name of the image + + Returns: + ECR URI of the image version + """ + + # Fetch image details to get the Base Image URI + for image_version in image_versions: + if image_version["ImageVersionStatus"] == "CREATED": + image_arn = image_version["ImageVersionArn"] + version = image_version["Version"] + logger.info(f"Identified the latest image version: {image_arn}") + response = sagemaker_session.sagemaker_client.describe_image_version(ImageName=image_name, Version=version) + return response["ContainerImage"] + return None + + +def resolve_ecr_uri(sagemaker_session: sagemaker.session.Session, image_arn: str) -> Any: + """Gets the ECR URI from the image name + + Args: + sagemaker_session: boto3 session for sagemaker client + image_name: name of the image + + Returns: + ECR URI of the latest image version + """ + + # Fetching image name from image_arn (^arn:aws(-[\w]+)*:sagemaker:.+:[0-9]{12}:image/[a-z0-9]([-.]?[a-z0-9])*$) + image_name = image_arn.partition("image/")[2] + try: + # Fetch the image versions + next_token = "" + while True: + response = sagemaker_session.sagemaker_client.list_image_versions( + ImageName=image_name, MaxResults=100, SortBy="VERSION", SortOrder="DESCENDING", NextToken=next_token + ) + + ecr_uri = resolve_ecr_uri_from_image_versions(sagemaker_session, response["ImageVersions"], image_name) + + if ecr_uri is not None: + return ecr_uri + + if "NextToken" in response: + next_token = response["NextToken"] + else: + break + + # Return error if no versions of the image found + error_message = f"No image version found for image name: {image_name}" + logger.error(error_message) + raise Exception(error_message) + + except (ClientError, sagemaker_session.sagemaker_client.exceptions.ResourceNotFound) as e: + error_message = e.response["Error"]["Message"] + logger.error(error_message) + raise Exception(error_message) diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/pipeline.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/pipeline.py new file mode 100644 index 00000000..25775259 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/ml_pipelines/training/pipeline.py @@ -0,0 +1,119 @@ +import json +import logging +import os +from typing import Any, Optional + +import boto3 +import sagemaker +import sagemaker.session +from botocore.exceptions import ClientError +from sagemaker.huggingface import HuggingFaceModel, get_huggingface_llm_image_uri +from sagemaker.workflow.parameters import ParameterString +from sagemaker.workflow.pipeline import Pipeline +from sagemaker.workflow.step_collections import RegisterModel + +logger = logging.getLogger(__name__) +ACCESS_TOKEN_SECRET = os.environ["HUGGING_FACE_ACCESS_TOKEN_SECRET"] # read token from secret using boto3 +SECRET_REGION = os.environ["AWS_REGION"] + + +def get_acess_token_from_secret(secretid: str, secret_region: str) -> Any: + # Create a Secrets Manager client + session = boto3.session.Session() + client = session.client(service_name="secretsmanager", region_name=secret_region) + + try: + get_secret_value_response = client.get_secret_value(SecretId=secretid) + except ClientError as e: + raise e + + # Get the secret value + secret_value = get_secret_value_response["SecretString"] + return secret_value + + +ACCESS_TOKEN = get_acess_token_from_secret(ACCESS_TOKEN_SECRET, SECRET_REGION) + + +def get_session(region: str, default_bucket: Optional[str]) -> sagemaker.session.Session: + """Gets the sagemaker session based on the region. + + Args: + region: the aws region to start the session + default_bucket: the bucket to use for storing the artifacts + + Returns: + `sagemaker.session.Session instance + """ + + boto_session = boto3.Session(region_name=region) + + sagemaker_client = boto_session.client("sagemaker") + runtime_client = boto_session.client("sagemaker-runtime") + session = sagemaker.session.Session( + boto_session=boto_session, + sagemaker_client=sagemaker_client, + sagemaker_runtime_client=runtime_client, + default_bucket=default_bucket, + ) + + return session + + +def get_pipeline( + region: str, + hugging_face_model_id: str, + role: Optional[str] = None, + default_bucket: Optional[str] = None, + model_package_group_name: str = "AbalonePackageGroup", + pipeline_name: str = "AbalonePipeline", + project_id: str = "SageMakerProjectId", +) -> Any: + sagemaker_session = get_session(region, default_bucket) + if role is None: + role = sagemaker.session.get_execution_role(sagemaker_session) + + # parameters for pipeline execution + model_approval_status = ParameterString(name="ModelApprovalStatus", default_value="PendingManualApproval") + + inference_image_uri = get_huggingface_llm_image_uri("huggingface", version="0.9.3") + llm_model = HuggingFaceModel( + role=role, + image_uri=inference_image_uri, + env={ + "HF_MODEL_ID": hugging_face_model_id, # model_id from hf.co/models + "SM_NUM_GPUS": json.dumps(1), # Number of GPU used per replica + "MAX_INPUT_LENGTH": json.dumps(2048), # Max length of input text + "MAX_TOTAL_TOKENS": json.dumps(4096), # Max length of the generation (including input text) + "MAX_BATCH_TOTAL_TOKENS": json.dumps( + 8192 + ), # Limits the number of tokens that can be processed in parallel during the generation + "HUGGING_FACE_HUB_TOKEN": ACCESS_TOKEN, + # ,'HF_MODEL_QUANTIZE': "bitsandbytes", # comment in to quantize + }, + ) + + step_register = RegisterModel( + name="RegisterModel", + model=llm_model, + # estimator=xgb_train, + # image_uri=inference_image_uri, + # model_data=step_train.properties.ModelArtifacts.S3ModelArtifacts, + content_types=["text/csv"], + response_types=["text/csv"], + inference_instances=["ml.g5.2xlarge", "ml.g5.12xlarge"], + # transform_instances=["ml.g5.12xlarge", "ml.p4d.24xlarge"], + model_package_group_name=model_package_group_name, + approval_status=model_approval_status, + ) + + # pipeline instance + pipeline = Pipeline( + name=pipeline_name, + parameters=[ + model_approval_status, + ], + steps=[step_register], + sagemaker_session=sagemaker_session, + ) + return pipeline diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.cfg b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.cfg new file mode 100644 index 00000000..6f878705 --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.cfg @@ -0,0 +1,14 @@ +[tool:pytest] +addopts = + -vv +testpaths = tests + +[aliases] +test=pytest + +[metadata] +description-file = README.md +license_file = LICENSE + +[wheel] +universal = 1 diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.py b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.py new file mode 100644 index 00000000..a27183bb --- /dev/null +++ b/modules/sagemaker/sagemaker-templates-service-catalog/templates/hf_import_models/seed_code/build_app/setup.py @@ -0,0 +1,78 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# SPDX-License-Identifier: MIT-0 +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import os +from typing import Any, Dict + +import setuptools + +about: Dict[str, Any] = {} +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, "ml_pipelines", "__version__.py")) as f: + exec(f.read(), about) + + +with open("README.md", "r") as f: + readme = f.read() + + +required_packages = ["sagemaker"] +extras = { + "test": [ + "black", + "coverage", + "flake8", + "mock", + "pydocstyle", + "pytest", + "pytest-cov", + "sagemaker", + "tox", + ] +} +setuptools.setup( + name=about["__title__"], + description=about["__description__"], + version=about["__version__"], + author=about["__author__"], + author_email=about["__author_email__"], + long_description=readme, + long_description_content_type="text/markdown", + url=about["__url__"], + license=about["__license__"], + packages=setuptools.find_packages(), + include_package_data=True, + python_requires=">=3.6", + install_requires=required_packages, + extras_require=extras, + entry_points={ + "console_scripts": [ + "get-pipeline-definition=ml_pipelines.get_pipeline_definition:main", + "run-pipeline=ml_pipelines.run_pipeline:main", + ] + }, + classifiers=[ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Natural Language :: English", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + ], +) diff --git a/modules/sagemaker/sagemaker-templates-service-catalog/tests/test_stack.py b/modules/sagemaker/sagemaker-templates-service-catalog/tests/test_stack.py index 53666e89..de5c3805 100644 --- a/modules/sagemaker/sagemaker-templates-service-catalog/tests/test_stack.py +++ b/modules/sagemaker/sagemaker-templates-service-catalog/tests/test_stack.py @@ -76,7 +76,7 @@ def test_synthesize_stack(stack: cdk.Stack) -> None: template = Template.from_stack(stack) template.resource_count_is("AWS::ServiceCatalog::Portfolio", 1) - template.resource_count_is("AWS::ServiceCatalog::CloudFormationProduct", 3) + template.resource_count_is("AWS::ServiceCatalog::CloudFormationProduct", 4) def test_no_cdk_nag_errors(stack: cdk.Stack) -> None: