├── .gitignore ├── LICENSE ├── README.md ├── assets ├── LargeScaleRecsysHugeCTR.png ├── OfflineBatchRecsys.png ├── OnlineMultiStageRecsys.png └── aws-terraform.png ├── benchmarking └── README.md ├── cloud-deployment ├── .gitignore ├── .terraform.lock.hcl ├── README.md ├── modules.tf ├── modules │ ├── dns │ │ ├── dns.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── nodes │ │ ├── ansible │ │ │ ├── config │ │ │ │ └── ssh.tpl │ │ │ ├── inventories │ │ │ │ └── inventory_re.tpl │ │ │ └── playbooks │ │ │ │ ├── default.yaml │ │ │ │ └── playbook_re_node.yaml.tpl │ │ ├── aws_ebs_volume.tf │ │ ├── aws_eip.tf │ │ ├── aws_instances.tf │ │ ├── inputs.tf │ │ ├── outputs.tf │ │ ├── provisioning_re_node.tf │ │ ├── security.tf │ │ └── variables.tf │ ├── prometheus-node │ │ ├── ansible │ │ │ ├── config │ │ │ │ └── ssh.tpl │ │ │ ├── grafana-datasource.yml │ │ │ ├── grafana │ │ │ │ ├── cluster.json │ │ │ │ ├── database.json │ │ │ │ └── node.json │ │ │ ├── inventories │ │ │ │ └── inventory_prometheus.tpl │ │ │ ├── playbooks │ │ │ │ ├── default.yaml │ │ │ │ ├── playbook_grafana_dashboard.yaml.tpl │ │ │ │ └── playbook_prometheus_node.yaml │ │ │ ├── prometheus │ │ │ │ ├── docker-compose.yml.tpl │ │ │ │ └── prometheus.yml.tpl │ │ │ └── roles │ │ │ │ └── grafana-datasource │ │ │ │ ├── defaults │ │ │ │ └── main.yml.tpl │ │ │ │ └── tasks │ │ │ │ └── main.yml │ │ ├── aws_eip.tf │ │ ├── aws_instances.tf │ │ ├── configure_prometheus.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── re-cluster │ │ ├── create_cluster.tf │ │ ├── extra_vars │ │ │ └── inventory.yaml.tpl │ │ ├── group_vars │ │ │ └── all │ │ │ │ └── main.yaml │ │ ├── inventories │ │ │ └── inventory.tpl │ │ ├── outputs.tf │ │ ├── redislabs-create-cluster.yaml │ │ ├── roles │ │ │ └── create_cluster │ │ │ │ ├── tasks │ │ │ │ └── main.yaml │ │ │ │ └── templates │ │ │ │ ├── create_cluster.json.j2 │ │ │ │ └── join_cluster.json.j2 │ │ └── variables.tf │ ├── triton-nodes │ │ ├── ansible │ │ │ ├── config │ │ │ │ └── ssh.tpl │ │ │ ├── inventories │ │ │ │ └── inventory_test.tpl │ │ │ └── playbooks │ │ │ │ ├── default.yaml │ │ │ │ └── playbook_test_node.yaml │ │ ├── aws_eip.tf │ │ ├── aws_instances.tf │ │ ├── outputs.tf │ │ ├── provisioning_test_node.tf │ │ └── variables.tf │ └── vpc │ │ ├── aws_vpc.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── provider.tf ├── terraform.tfvars.example └── variables.tf ├── large-scale-recsys ├── .ipynb_checkpoints │ └── HugeCTR_DLRM_Inference-checkpoint.ipynb ├── 01-Large-Scale-Recsys-Deploy.ipynb ├── docker-compose-cloud.yml ├── docker-compose.yml └── redis-confs │ ├── redis-1.conf │ ├── redis-2.conf │ └── redis-3.conf ├── offline-batch-recsys ├── Dockerfile ├── Offline-Batch-Recommender-System.ipynb ├── docker-compose.yml └── requirements.txt └── online-multi-stage-recsys ├── 01-Building-Online-Multi-Stage-Recsys-Components.ipynb ├── 02-Deploying-Online-Multi-Stage-Recsys-with-Triton.ipynb ├── client.py ├── docker-compose-cloud.yml ├── docker-compose.yml ├── feature_repo ├── feature_store.yaml ├── item_features.py └── user_features.py ├── models ├── 0-query-user-features │ ├── 1 │ │ └── model.py │ └── config.pbtxt ├── 1-user-embeddings │ ├── 1 │ │ └── model.savedmodel │ │ │ ├── keras_metadata.pb │ │ │ ├── saved_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00001 │ │ │ └── variables.index │ └── config.pbtxt ├── 2-redis-vss-candidates │ ├── 1 │ │ └── model.py │ └── config.pbtxt ├── 3-query-item-features │ ├── 1 │ │ └── model.py │ └── config.pbtxt ├── 4-unroll-features │ ├── 1 │ │ └── model.py │ └── config.pbtxt ├── 5-ranking │ ├── 1 │ │ └── model.savedmodel │ │ │ ├── .merlin │ │ │ └── input_schema.json │ │ │ ├── keras_metadata.pb │ │ │ ├── saved_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00001 │ │ │ └── variables.index │ └── config.pbtxt ├── 6-softmax-sampling │ ├── 1 │ │ └── model.py │ └── config.pbtxt └── ensemble-model │ ├── 1 │ └── .gitkeep │ └── config.pbtxt ├── requirements.txt ├── revisions ├── 01-baseline │ ├── 0-query-user-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 1-user-embeddings │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 2-redis-vss-candidates │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 3-query-item-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 4-unroll-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 5-ranking │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── .merlin │ │ │ │ └── input_schema.json │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 6-softmax-sampling │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ └── ensemble-model │ │ ├── 1 │ │ └── .gitkeep │ │ └── config.pbtxt ├── 02-stage-consolidation │ ├── 0-query-user-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 1-user-embeddings │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 2-redis-vss-candidates │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 4-unroll-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 5-ranking │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── .merlin │ │ │ │ └── input_schema.json │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 6-softmax-sampling │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ └── ensemble-model │ │ ├── 1 │ │ └── .gitkeep │ │ └── config.pbtxt ├── 03-feastless-feature-store-setup.ipynb ├── 03-remove-feast-sdk │ ├── 0-query-user-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 1-user-embeddings │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 2-redis-vss-candidates │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 4-unroll-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 5-ranking │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── .merlin │ │ │ │ └── input_schema.json │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 6-softmax-sampling │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ └── ensemble-model │ │ ├── 1 │ │ └── .gitkeep │ │ └── config.pbtxt ├── 04-vss-feature-store-setup.ipynb ├── 04-vss-retrieval │ ├── 0-query-user-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 1-user-embeddings │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 2-redis-vss-candidates │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 4-unroll-features │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ ├── 5-ranking │ │ ├── 1 │ │ │ └── model.savedmodel │ │ │ │ ├── .merlin │ │ │ │ └── input_schema.json │ │ │ │ ├── keras_metadata.pb │ │ │ │ ├── saved_model.pb │ │ │ │ └── variables │ │ │ │ ├── variables.data-00000-of-00001 │ │ │ │ └── variables.index │ │ └── config.pbtxt │ ├── 6-softmax-sampling │ │ ├── 1 │ │ │ └── model.py │ │ └── config.pbtxt │ └── ensemble-model │ │ ├── 1 │ │ └── .gitkeep │ │ └── config.pbtxt └── 05-vss-retrieval-opt │ ├── 0-query-user-features │ ├── 1 │ │ └── model.py │ └── config.pbtxt │ ├── 1-user-embeddings │ ├── 1 │ │ └── model.savedmodel │ │ │ ├── keras_metadata.pb │ │ │ ├── saved_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00001 │ │ │ └── variables.index │ └── config.pbtxt │ ├── 2-redis-vss-candidates │ ├── 1 │ │ └── model.py │ └── config.pbtxt │ ├── 4-unroll-features │ ├── 1 │ │ └── model.py │ └── config.pbtxt │ ├── 5-ranking │ ├── 1 │ │ └── model.savedmodel │ │ │ ├── .merlin │ │ │ └── input_schema.json │ │ │ ├── keras_metadata.pb │ │ │ ├── saved_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00001 │ │ │ └── variables.index │ └── config.pbtxt │ ├── 6-softmax-sampling │ ├── 1 │ │ └── model.py │ └── config.pbtxt │ └── ensemble-model │ ├── 1 │ └── .gitkeep │ └── config.pbtxt ├── sample.json └── sample_32.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipynb_checkpoints 2 | *__pycache__ 3 | *.DS_Store 4 | 5 | data/ 6 | # python venv 7 | **/venv/* 8 | 9 | # Local .terraform directories 10 | **/.terraform/* 11 | 12 | # .tfstate files 13 | *.tfstate 14 | *.tfstate.* 15 | 16 | # Crash log files 17 | crash.log 18 | 19 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 20 | # .tfvars files are managed as part of configuration and so should be included in 21 | # version control. 22 | # 23 | # example.tfvars 24 | 25 | # Ignore override files as they are usually used to override resources locally and so 26 | # are not checked in 27 | override.tf 28 | override.tf.json 29 | *_override.tf 30 | *_override.tf.json 31 | 32 | # Include override files you do wish to add to version control using negated pattern 33 | # 34 | # !example_override.tf 35 | 36 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 37 | # example: *tfplan* 38 | terraform.tfvars 39 | terraform.lock.hcl -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Redis 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /assets/LargeScaleRecsysHugeCTR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/assets/LargeScaleRecsysHugeCTR.png -------------------------------------------------------------------------------- /assets/OfflineBatchRecsys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/assets/OfflineBatchRecsys.png -------------------------------------------------------------------------------- /assets/OnlineMultiStageRecsys.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/assets/OnlineMultiStageRecsys.png -------------------------------------------------------------------------------- /assets/aws-terraform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/assets/aws-terraform.png -------------------------------------------------------------------------------- /cloud-deployment/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # python venv 3 | **/venv/* 4 | 5 | # Local .terraform directories 6 | **/.terraform/* 7 | 8 | # .tfstate files 9 | *.tfstate 10 | *.tfstate.* 11 | 12 | # Crash log files 13 | crash.log 14 | 15 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 16 | # .tfvars files are managed as part of configuration and so should be included in 17 | # version control. 18 | # 19 | # example.tfvars 20 | 21 | # Ignore override files as they are usually used to override resources locally and so 22 | # are not checked in 23 | override.tf 24 | override.tf.json 25 | *_override.tf 26 | *_override.tf.json 27 | 28 | # Include override files you do wish to add to version control using negated pattern 29 | # 30 | # !example_override.tf 31 | 32 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 33 | # example: *tfplan* 34 | terraform.tfvars 35 | terraform.lock.hcl -------------------------------------------------------------------------------- /cloud-deployment/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "3.75.2" 6 | constraints = "~> 3.0" 7 | hashes = [ 8 | "h1:lcSLAmkNM1FvNhqAEbh2oTZRqF37HKRh1Di8LvssYBY=", 9 | "h1:x0gluX9ZKEmz+JJW3Ut5GgWDFOq/lhs2vkqJ+xt57zs=", 10 | "zh:0e75fb14ec42d69bc46461dd54016bb2487d38da324222cec20863918b8954c4", 11 | "zh:30831a1fe29f005d8b809250b43d09522288db45d474c9d238b26f40bdca2388", 12 | "zh:36163d625ab2999c9cd31ef2475d978f9f033a8dfa0d585f1665f2d6492fac4b", 13 | "zh:48ec39685541e4ddd8ddd196e2cfb72516b87f471d86ac3892bc11f83c573199", 14 | "zh:707b9c8775efd6962b6226d914ab25f308013bba1f68953daa77adca99ff6807", 15 | "zh:72bd9f4609a827afa366c6f119c7dec7d73a35d712dad1457c0497d87bf8d160", 16 | "zh:930e3ae3d0cb152e17ee5a8aee5cb47f7613d6421bc7c22e7f50c19da484a100", 17 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 18 | "zh:a19bf49b80101a0f0272b994153eeff8f8c206ecc592707bfbce7563355b6882", 19 | "zh:a34b5d2bbaf52285b0c9a8df6258f4789f4d927ff777e126bdc77e7887abbeaa", 20 | "zh:caad6fd5e79eae33e6d74e38c3b15c28a5482f2a1a8ca46cc1ee70089de61adb", 21 | "zh:f2eae988635030de9a088f8058fbcd91e2014a8312a48b16bfd09a9d69d9d6f7", 22 | ] 23 | } 24 | 25 | provider "registry.terraform.io/hashicorp/local" { 26 | version = "2.2.3" 27 | hashes = [ 28 | "h1:KmHz81iYgw9Xn2L3Carc2uAzvFZ1XsE7Js3qlVeC77k=", 29 | "h1:aWp5iSUxBGgPv1UnV5yag9Pb0N+U1I0sZb38AXBFO8A=", 30 | "zh:04f0978bb3e052707b8e82e46780c371ac1c66b689b4a23bbc2f58865ab7d5c0", 31 | "zh:6484f1b3e9e3771eb7cc8e8bab8b35f939a55d550b3f4fb2ab141a24269ee6aa", 32 | "zh:78a56d59a013cb0f7eb1c92815d6eb5cf07f8b5f0ae20b96d049e73db915b238", 33 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 34 | "zh:8aa9950f4c4db37239bcb62e19910c49e47043f6c8587e5b0396619923657797", 35 | "zh:996beea85f9084a725ff0e6473a4594deb5266727c5f56e9c1c7c62ded6addbb", 36 | "zh:9a7ef7a21f48fabfd145b2e2a4240ca57517ad155017e86a30860d7c0c109de3", 37 | "zh:a63e70ac052aa25120113bcddd50c1f3cfe61f681a93a50cea5595a4b2cc3e1c", 38 | "zh:a6e8d46f94108e049ad85dbed60354236dc0b9b5ec8eabe01c4580280a43d3b8", 39 | "zh:bb112ce7efbfcfa0e65ed97fa245ef348e0fd5bfa5a7e4ab2091a9bd469f0a9e", 40 | "zh:d7bec0da5c094c6955efed100f3fe22fca8866859f87c025be1760feb174d6d9", 41 | "zh:fb9f271b72094d07cef8154cd3d50e9aa818a0ea39130bc193132ad7b23076fd", 42 | ] 43 | } 44 | 45 | provider "registry.terraform.io/hashicorp/null" { 46 | version = "3.1.1" 47 | hashes = [ 48 | "h1:71sNUDvmiJcijsvfXpiLCz0lXIBSsEJjMxljt7hxMhw=", 49 | "h1:Pctug/s/2Hg5FJqjYcTM0kPyx3AoYK1MpRWO0T9V2ns=", 50 | "zh:063466f41f1d9fd0dd93722840c1314f046d8760b1812fa67c34de0afcba5597", 51 | "zh:08c058e367de6debdad35fc24d97131c7cf75103baec8279aba3506a08b53faf", 52 | "zh:73ce6dff935150d6ddc6ac4a10071e02647d10175c173cfe5dca81f3d13d8afe", 53 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 54 | "zh:8fdd792a626413502e68c195f2097352bdc6a0df694f7df350ed784741eb587e", 55 | "zh:976bbaf268cb497400fd5b3c774d218f3933271864345f18deebe4dcbfcd6afa", 56 | "zh:b21b78ca581f98f4cdb7a366b03ae9db23a73dfa7df12c533d7c19b68e9e72e5", 57 | "zh:b7fc0c1615dbdb1d6fd4abb9c7dc7da286631f7ca2299fb9cd4664258ccfbff4", 58 | "zh:d1efc942b2c44345e0c29bc976594cb7278c38cfb8897b344669eafbc3cddf46", 59 | "zh:e356c245b3cd9d4789bab010893566acace682d7db877e52d40fc4ca34a50924", 60 | "zh:ea98802ba92fcfa8cf12cbce2e9e7ebe999afbf8ed47fa45fc847a098d89468b", 61 | "zh:eff8872458806499889f6927b5d954560f3d74bf20b6043409edf94d26cd906f", 62 | ] 63 | } 64 | 65 | provider "registry.terraform.io/hashicorp/time" { 66 | version = "0.8.0" 67 | hashes = [ 68 | "h1:m9zlQLy7VbjhI4unRTXvrkdV6gcHu7qegEorLWx3pAM=", 69 | "h1:sT/5WKFSUol4n0ShXDFMlv2ufVHKMk4SLBUDV1ffsX0=", 70 | "zh:02eabf4c6239c5b950cc99bb214b2c55e8259d911bcb1a1b26988a0227fe10d4", 71 | "zh:05220f907b274347dec0ffa8383becc6a3640324bc5d60e2b938d5429ed81f5e", 72 | "zh:14165bc5a859c9d617fda2cedaeec1b7a20f8590969faa24aa34c1fc273c23b9", 73 | "zh:1abe696cbe17c070ac98745a357760827bc49ff8a6647b9e1a5cb52010edcbe0", 74 | "zh:20ec0ad2dec862fb6412047f4855bbd79d1a2e18a149088b337805f9b3766974", 75 | "zh:3d70d4836b35b4ec9477d49685f6773cc765aea679d19cbeeeb485e2185f620a", 76 | "zh:4137272743250ac557dd8c2ba92c93aa21cf9c85edfa7fbe07a3a94c9e9783a7", 77 | "zh:525304ba8fd0abcc1d767b47114b6dfaf74d2a0afe0eaa656a38e81cc2651313", 78 | "zh:76241458be0613fabcf347068af9ed846f829ba4e683e10beca631be26312db2", 79 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 80 | "zh:85f2b4caaf0485c5346a576a2c7a5b1e155b1b72f95f70bfbc80e233e6916622", 81 | "zh:f93d3b0b6553f5a438312ff2b46025b67786f7b76b1ea833a4c72cb29edc1ad2", 82 | ] 83 | } 84 | -------------------------------------------------------------------------------- /cloud-deployment/modules/dns/dns.tf: -------------------------------------------------------------------------------- 1 | # DNS Record for Redis Cluster 2 | 3 | # DNS record set from existing hosted zone 4 | # A records are aws_eip (elastic ips assocated with each re instance) 5 | 6 | # Get a Hosted Zone from zone id 7 | data "aws_route53_zone" "selected" { 8 | zone_id = var.dns_hosted_zone_id 9 | private_zone = true 10 | } 11 | 12 | # create A records 13 | resource "aws_route53_record" "A_record" { 14 | count = var.data-node-count 15 | zone_id = data.aws_route53_zone.selected.zone_id 16 | name = format("node-%s.%s.${data.aws_route53_zone.selected.name}", count.index+1, var.vpc_name) 17 | type = "A" 18 | ttl = "300" 19 | records = [ 20 | element(var.re-data-node-eips, count.index) 21 | ] 22 | } 23 | 24 | # create NS record. Requires an existing R53 zone. 25 | resource "aws_route53_record" "NS_record" { 26 | zone_id = data.aws_route53_zone.selected.zone_id 27 | name = format("%s.${data.aws_route53_zone.selected.name}", var.vpc_name) 28 | type = "NS" 29 | ttl = "300" 30 | records = formatlist("node-%s.%s.${data.aws_route53_zone.selected.name}", range(1, var.data-node-count + 1), var.vpc_name) 31 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/dns/outputs.tf: -------------------------------------------------------------------------------- 1 | #### Outputs 2 | 3 | output "dns-ns-record-name" { 4 | value = aws_route53_record.NS_record.name 5 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/dns/variables.tf: -------------------------------------------------------------------------------- 1 | #### DNS variables 2 | 3 | variable "dns_hosted_zone_id" { 4 | description = "DNS hosted zone Id" 5 | } 6 | 7 | variable "vpc_name" { 8 | description = "The VPC Name tag" 9 | } 10 | 11 | variable "data-node-count" { 12 | description = "number of data nodes" 13 | default = 3 14 | } 15 | 16 | variable "re-data-node-eips" { 17 | type = list(any) 18 | description = "List of Elastic IP address to add as A records" 19 | default = [] 20 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/ansible/config/ssh.tpl: -------------------------------------------------------------------------------- 1 | Host * 2 | User ubuntu 3 | IdentityFile ~/.ssh/${vpc_name}.pem 4 | ForwardAgent yes 5 | GSSAPIAuthentication no 6 | VerifyHostKeyDNS no 7 | HashKnownHosts no 8 | TCPKeepAlive yes 9 | ServerAliveInterval 300 10 | StrictHostKeyChecking no 11 | UserKnownHostsFile=/dev/null 12 | IdentitiesOnly yes -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/ansible/inventories/inventory_re.tpl: -------------------------------------------------------------------------------- 1 | [all] 2 | ${host_ip} ansible_ssh_common_args='-o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -F /tmp/${vpc_name}_node.cfg' -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/ansible/playbooks/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | enable_volumes: "{{ ENABLE_VOLUMES|default(true)|bool }}" 3 | 4 | deb_packages: 5 | - jq 6 | - tree 7 | - sysstat 8 | - iputils-ping 9 | - libcap2-bin 10 | - build-essential 11 | - autoconf 12 | - automake 13 | - libbsd-dev 14 | - libltdl-dev 15 | - libltdl7 16 | - libtool 17 | - libevent-openssl-2.1-6 18 | - libpcre3-dev 19 | - libevent-dev 20 | - pkg-config 21 | - zlib1g-dev 22 | - libssl-dev 23 | - ntp -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/ansible/playbooks/playbook_re_node.yaml.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: all 4 | become: yes 5 | become_user: root 6 | become_method: sudo 7 | gather_facts: yes 8 | 9 | 10 | pre_tasks: 11 | - name: Load vars 12 | include_vars: "{{ item }}" 13 | with_first_found: 14 | - "{{ ansible_hostname }}.yaml" 15 | - "default.yaml" 16 | 17 | - name: Update Apt Cache 18 | apt: 19 | update_cache: yes 20 | - name: Ubuntu Packages 21 | package: 22 | name: "{{ deb_packages }}" 23 | - name: Stop systemd Resolver 24 | systemd: 25 | name: systemd-resolved 26 | state: stopped 27 | enabled: no 28 | - name: Removing bad resolver 29 | lineinfile: 30 | path: /etc/resolv.conf 31 | regexp: 'nameserver\s+127\.0\.0\.53' 32 | state: absent 33 | - name: Adding known good resolver 34 | lineinfile: 35 | path: /etc/resolv.conf 36 | regexp: '^nameserver\s+1.1.1.1' 37 | line: 'nameserver 1.1.1.1' 38 | state: present 39 | 40 | - name: create re home dir 41 | file: 42 | state: directory 43 | path: "/redis" 44 | 45 | 46 | tasks: 47 | - name: create download directory 48 | file: 49 | state: directory 50 | path: "/var/tmp/re-download" 51 | - name: Unarchive software 52 | unarchive: 53 | src: ${re_download_url} 54 | 55 | dest: /var/tmp/re-download 56 | remote_src: yes 57 | - name: Install the software 58 | command: "./install.sh -y" 59 | args: 60 | chdir: /var/tmp/re-download 61 | creates: /var/opt/redislabs/log/rlcheck.log -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/aws_ebs_volume.tf: -------------------------------------------------------------------------------- 1 | #### Create & attach EBS volumes for RE nodes 2 | 3 | # Attach Ephemeral Volumes 4 | # Instance 1 5 | resource "aws_ebs_volume" "ephemeral_re_cluster_instance" { 6 | count = var.data-node-count 7 | availability_zone = element(var.subnet_azs, count.index) 8 | size = var.re-volume-size 9 | 10 | tags = { 11 | Name = format("%s-ec2-%s-ephemeral", var.vpc_name, count.index+1), 12 | Owner = var.owner 13 | } 14 | } 15 | 16 | resource "aws_volume_attachment" "ephemeral_re_cluster_instance" { 17 | count = var.data-node-count 18 | device_name = "/dev/sdh" 19 | volume_id = element(aws_ebs_volume.ephemeral_re_cluster_instance.*.id, count.index) 20 | instance_id = element(aws_instance.re_cluster_instance.*.id, count.index) 21 | } 22 | 23 | # Attach Persistent Volumes 24 | # Instance 1 25 | resource "aws_ebs_volume" "persistent_re_cluster_instance" { 26 | count = var.data-node-count 27 | availability_zone = element(var.subnet_azs, count.index) 28 | size = var.re-volume-size 29 | 30 | tags = { 31 | Name = format("%s-ec2-%s-persistent", var.vpc_name, count.index+1), 32 | Owner = var.owner 33 | } 34 | } 35 | 36 | resource "aws_volume_attachment" "persistent_re_cluster_instance" { 37 | count = var.data-node-count 38 | device_name = "/dev/sdj" 39 | volume_id = element(aws_ebs_volume.persistent_re_cluster_instance.*.id, count.index) 40 | instance_id = element(aws_instance.re_cluster_instance.*.id, count.index) 41 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/aws_eip.tf: -------------------------------------------------------------------------------- 1 | #### Create & associate EIP with RE 2 | 3 | #### RE Nodes EIP 4 | resource "aws_eip" "re_cluster_instance_eip" { 5 | count = var.data-node-count 6 | network_border_group = var.region 7 | vpc = true 8 | 9 | tags = { 10 | Name = format("%s-eip-%s", var.vpc_name, count.index+1), 11 | Owner = var.owner 12 | } 13 | 14 | } 15 | 16 | #### RE Node Elastic IP association 17 | resource "aws_eip_association" "re-eip-assoc" { 18 | count = var.data-node-count 19 | instance_id = element(aws_instance.re_cluster_instance.*.id, count.index) 20 | allocation_id = element(aws_eip.re_cluster_instance_eip.*.id, count.index) 21 | depends_on = [aws_instance.re_cluster_instance, aws_eip.re_cluster_instance_eip] 22 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/aws_instances.tf: -------------------------------------------------------------------------------- 1 | #### Create EC2 Nodes for RE 2 | 3 | # create nodes for your Redis Enterprise cluster 4 | resource "aws_instance" "re_cluster_instance" { 5 | count = var.data-node-count 6 | ami = data.aws_ami.re-ami.id 7 | associate_public_ip_address = true 8 | availability_zone = element(var.subnet_azs, count.index) 9 | subnet_id = element(var.vpc_subnets_ids, count.index) 10 | instance_type = var.re_instance_type 11 | key_name = var.ssh_key_name 12 | vpc_security_group_ids = [ aws_security_group.re_sg.id ] 13 | root_block_device { volume_size = var.node-root-size } 14 | 15 | tags = { 16 | Name = format("%s-node-%s", var.vpc_name,count.index+1), 17 | Owner = var.owner 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/inputs.tf: -------------------------------------------------------------------------------- 1 | #### AMI for Nodes 2 | #### find the lastest ami for ubuntu 18.04 x86 server 3 | 4 | data "aws_ami" "re-ami" { 5 | most_recent = true 6 | name_regex = "ubuntu\\/images\\/hvm-ssd\\/ubuntu-bionic-18.04-amd64-server" 7 | # This is Canonical's ID (find here: https://ubuntu.com/server/docs/cloud-images/amazon-ec2) 8 | owners = ["099720109477"] 9 | 10 | filter { 11 | name = "root-device-type" 12 | values = ["ebs"] 13 | } 14 | 15 | filter { 16 | name = "virtualization-type" 17 | values = ["hvm"] 18 | } 19 | 20 | filter { 21 | name = "ena-support" 22 | values = [var.ena-support] 23 | } 24 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/outputs.tf: -------------------------------------------------------------------------------- 1 | #### Outputs 2 | 3 | output "re-data-node-eips" { 4 | value = aws_eip.re_cluster_instance_eip[*].public_ip 5 | } 6 | 7 | output "re-data-node-internal-ips" { 8 | value = aws_instance.re_cluster_instance[*].private_ip 9 | } 10 | 11 | output "re-data-node-eip-public-dns" { 12 | value = aws_eip.re_cluster_instance_eip[*].public_dns #ec2 public dns after EIP association 13 | } 14 | 15 | #### Output variables 16 | 17 | #vpc_security_group_ids 18 | output "vpc_security_group_ids" { 19 | value = [ aws_security_group.re_sg.id ] 20 | } 21 | 22 | #re_ami 23 | output "re_ami" { 24 | value = data.aws_ami.re-ami.id 25 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/provisioning_re_node.tf: -------------------------------------------------------------------------------- 1 | #### Generating Ansible config, inventory, playbook 2 | #### and configuring RE nodes and installing RE software 3 | #### (RE nodes need special configuration to work with Ubuntu 18) 4 | 5 | #### Sleeper, after instance, eip assoc, local file inventories & cfg created 6 | #### otherwise it can run to fast, not find the inventory file and fail or hang 7 | resource "time_sleep" "wait_30_seconds_re" { 8 | create_duration = "30s" 9 | depends_on = [aws_instance.re_cluster_instance, 10 | aws_eip_association.re-eip-assoc, 11 | local_file.inventory-setup, 12 | local_file.ssh-setup] 13 | } 14 | 15 | # remote-config waits till the node is accessible 16 | resource "null_resource" "remote-config" { 17 | count = var.data-node-count 18 | provisioner "remote-exec" { 19 | inline = ["sudo apt update > /dev/null"] 20 | 21 | connection { 22 | type = "ssh" 23 | user = "ubuntu" 24 | private_key = file(var.ssh_key_path) 25 | host = element(aws_eip.re_cluster_instance_eip.*.public_ip, count.index) 26 | } 27 | } 28 | depends_on = [aws_instance.re_cluster_instance, 29 | aws_eip_association.re-eip-assoc, 30 | local_file.inventory-setup, 31 | local_file.ssh-setup, 32 | local_file.playbook_setup, 33 | time_sleep.wait_30_seconds_re] 34 | } 35 | 36 | #### Generate Ansible Playbook 37 | resource "local_file" "playbook_setup" { 38 | count = var.data-node-count 39 | content = templatefile("${path.module}/ansible/playbooks/playbook_re_node.yaml.tpl", { 40 | re_download_url = var.re_download_url 41 | }) 42 | filename = "${path.module}/ansible/playbooks/playbook_re_node.yaml" 43 | depends_on = [aws_instance.re_cluster_instance, aws_eip_association.re-eip-assoc, aws_volume_attachment.ephemeral_re_cluster_instance] 44 | } 45 | 46 | #### Generate Ansible Inventory for each node 47 | resource "local_file" "inventory-setup" { 48 | count = var.data-node-count 49 | content = templatefile("${path.module}/ansible/inventories/inventory_re.tpl", { 50 | host_ip = element(aws_eip.re_cluster_instance_eip.*.public_ip, count.index) 51 | vpc_name = var.vpc_name 52 | }) 53 | filename = "/tmp/${var.vpc_name}_node_${count.index}.ini" 54 | depends_on = [aws_instance.re_cluster_instance, aws_eip_association.re-eip-assoc, aws_volume_attachment.ephemeral_re_cluster_instance] 55 | } 56 | 57 | #### Generate ansible.cfg file 58 | resource "local_file" "ssh-setup" { 59 | content = templatefile("${path.module}/ansible/config/ssh.tpl", { 60 | vpc_name = var.vpc_name 61 | }) 62 | filename = "/tmp/${var.vpc_name}_node.cfg" 63 | depends_on = [aws_instance.re_cluster_instance, aws_eip_association.re-eip-assoc, aws_volume_attachment.ephemeral_re_cluster_instance] 64 | } 65 | 66 | ###################### 67 | # Run ansible playbook to install RE software and configure node 68 | resource "null_resource" "ansible-run" { 69 | count = var.data-node-count 70 | provisioner "local-exec" { 71 | command = "ansible-playbook ${path.module}/ansible/playbooks/playbook_re_node.yaml --private-key ${var.ssh_key_path} -i /tmp/${var.vpc_name}_node_${count.index}.ini" 72 | } 73 | depends_on = [null_resource.remote-config,time_sleep.wait_30_seconds_re] 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /cloud-deployment/modules/nodes/security.tf: -------------------------------------------------------------------------------- 1 | #### Create Security Group 2 | 3 | resource "aws_security_group" "re_sg" { 4 | name = format("%s-re-sg", var.vpc_name) 5 | description = "Redis Enterprise Security Group" 6 | vpc_id = var.vpc_id 7 | 8 | tags = { 9 | Name = format("%s-re-sg", var.vpc_name), 10 | Owner = var.owner 11 | } 12 | } 13 | 14 | resource "aws_security_group_rule" "internal_rules" { 15 | count = length(var.internal-rules) 16 | type = lookup(var.internal-rules[count.index], "type") 17 | from_port = lookup(var.internal-rules[count.index], "from_port") 18 | to_port = lookup(var.internal-rules[count.index], "to_port") 19 | protocol = lookup(var.internal-rules[count.index], "protocol") 20 | cidr_blocks = [var.vpc_cidr] 21 | security_group_id = aws_security_group.re_sg.id 22 | } 23 | 24 | resource "aws_security_group_rule" "external_rules" { 25 | count = length(var.external-rules) 26 | type = lookup(var.external-rules[count.index], "type") 27 | from_port = lookup(var.external-rules[count.index], "from_port") 28 | to_port = lookup(var.external-rules[count.index], "to_port") 29 | protocol = lookup(var.external-rules[count.index], "protocol") 30 | cidr_blocks = lookup(var.external-rules[count.index], "cidr") 31 | security_group_id = aws_security_group.re_sg.id 32 | } 33 | 34 | resource "aws_security_group_rule" "open_nets" { 35 | type = "ingress" 36 | from_port = "0" 37 | to_port = "65535" 38 | protocol = "all" 39 | cidr_blocks = var.open-nets 40 | security_group_id = aws_security_group.re_sg.id 41 | } 42 | 43 | resource "aws_security_group_rule" "allow_public_ssh" { 44 | count = var.allow-public-ssh 45 | type = "ingress" 46 | from_port = "22" 47 | to_port = "22" 48 | protocol = "all" 49 | cidr_blocks = ["0.0.0.0/0"] 50 | security_group_id = aws_security_group.re_sg.id 51 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/config/ssh.tpl: -------------------------------------------------------------------------------- 1 | Host * 2 | User ubuntu 3 | IdentityFile ~/.ssh/${vpc_name}.pem 4 | ForwardAgent yes 5 | GSSAPIAuthentication no 6 | VerifyHostKeyDNS no 7 | HashKnownHosts no 8 | TCPKeepAlive yes 9 | ServerAliveInterval 300 10 | StrictHostKeyChecking no 11 | UserKnownHostsFile=/dev/null 12 | IdentitiesOnly yes -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/grafana-datasource.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add data source to grafana 3 | hosts: localhost 4 | roles: 5 | - grafana-datasource 6 | -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/inventories/inventory_prometheus.tpl: -------------------------------------------------------------------------------- 1 | [all] 2 | ${host_ip} ansible_ssh_common_args='-o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -F /tmp/${vpc_name}_triton_node.cfg' -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/playbooks/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | deb_packages: 4 | - jq 5 | - tree 6 | - sysstat 7 | - iputils-ping 8 | - libcap2-bin 9 | - build-essential 10 | - autoconf 11 | - automake 12 | - libbsd-dev 13 | - libltdl-dev 14 | - libltdl7 15 | - libtool 16 | - libevent-openssl-2.1-6 17 | - libpcre3-dev 18 | - libevent-dev 19 | - pkg-config 20 | - zlib1g-dev 21 | - libssl-dev 22 | - ntp -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/playbooks/playbook_grafana_dashboard.yaml.tpl: -------------------------------------------------------------------------------- 1 | - hosts: localhost 2 | connection: local 3 | tasks: 4 | - name: Import Grafana dashboard RE Cluster 5 | grafana_dashboard: 6 | grafana_url: "${grafana_url}" 7 | url_username: admin 8 | url_password: "secret" 9 | state: present 10 | overwrite: yes 11 | path: /tmp/cluster.json 12 | 13 | - name: Import Grafana dashboard RE Node 14 | grafana_dashboard: 15 | grafana_url: "${grafana_url}" 16 | url_username: admin 17 | url_password: "secret" 18 | state: present 19 | overwrite: yes 20 | path: /tmp/node.json 21 | 22 | - name: Import Grafana dashboard RE Database 23 | grafana_dashboard: 24 | grafana_url: "${grafana_url}" 25 | url_username: admin 26 | url_password: "secret" 27 | state: present 28 | overwrite: yes 29 | path: /tmp/database.json -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/playbooks/playbook_prometheus_node.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: all 4 | become: yes 5 | become_user: root 6 | become_method: sudo 7 | gather_facts: yes 8 | 9 | vars: 10 | deb_packages: 11 | - build-essential 12 | - autoconf 13 | - automake 14 | - libbsd-dev 15 | - libltdl-dev 16 | - libltdl7 17 | - libtool 18 | - libevent-openssl-2.1-6 19 | - libpcre3-dev 20 | - libevent-dev 21 | - pkg-config 22 | - zlib1g-dev 23 | - libssl-dev 24 | - nginx 25 | - docker-compose 26 | 27 | pre_tasks: 28 | - name: Update Apt Cache 29 | apt: update_cache=yes 30 | when: ansible_os_family == "Debian" 31 | - name: Ubuntu Packages 32 | package: 33 | name: "{{ deb_packages }}" 34 | - name: Set to Realistic Hostname 35 | hostname: 36 | name: "prometheus" 37 | - name: Setup Hostsfile 38 | lineinfile: 39 | dest: /etc/hosts 40 | regexp: '^127\.0\.0\.1[ \t]+localhost' 41 | line: "127.0.0.1 localhost prometheus" 42 | state: present 43 | 44 | 45 | tasks: 46 | - name: load vars 47 | include_vars: default.yaml 48 | 49 | 50 | # Prepare Node for Prometheus & Grafana 51 | - name: make prometheus directory 52 | file: 53 | path: /home/ubuntu/prometheus 54 | state: directory 55 | 56 | - name: copy prometheus yml 57 | copy: 58 | src: /tmp/prometheus.yml 59 | dest: /home/ubuntu/prometheus/prometheus.yml 60 | 61 | - name: copy docker compose yml 62 | copy: 63 | src: /tmp/docker-compose.yml 64 | dest: /home/ubuntu/docker-compose.yml 65 | 66 | 67 | - name: docker-compose up 68 | command: docker-compose up -d 69 | args: 70 | chdir: /home/ubuntu/ 71 | 72 | -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/prometheus/docker-compose.yml.tpl: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | prometheus-server: 4 | image: prom/prometheus 5 | ports: 6 | - 9090:9090 7 | volumes: 8 | - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml 9 | 10 | grafana-ui: 11 | image: grafana/grafana 12 | ports: 13 | - 3000:3000 14 | environment: 15 | - GF_SECURITY_ADMIN_PASSWORD=secret 16 | links: 17 | - prometheus-server:prometheus -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/prometheus/prometheus.yml.tpl: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | evaluation_interval: 15s 4 | 5 | # Attach these labels to any time series or alerts when communicating with 6 | # external systems (federation, remote storage, Alertmanager). 7 | external_labels: 8 | monitor: "prometheus-stack-monitor" 9 | 10 | # Load and evaluate rules in this file every 'evaluation_interval' seconds. 11 | #rule_files: 12 | # - "first.rules" 13 | # - "second.rules" 14 | 15 | scrape_configs: 16 | # scrape Prometheus itself 17 | - job_name: prometheus 18 | scrape_interval: 10s 19 | scrape_timeout: 5s 20 | static_configs: 21 | - targets: ["localhost:9090"] 22 | 23 | # scrape Redis Enterprise 24 | - job_name: redis-enterprise 25 | scrape_interval: 30s 26 | scrape_timeout: 30s 27 | metrics_path: / 28 | scheme: https 29 | tls_config: 30 | insecure_skip_verify: true 31 | static_configs: 32 | - targets: ["${cluster_fqdn}:8070"] -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/roles/grafana-datasource/defaults/main.yml.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | grafana_url: "${grafana_url}" 3 | grafana_user: admin 4 | grafana_password: "secret" 5 | org_id: "1" 6 | 7 | data_source: 8 | - name: DS_PROMETHEUS1 9 | ds_type: "prometheus" 10 | ds_url: "${prometheus_url}" 11 | tls_skip_verify: true -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/ansible/roles/grafana-datasource/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create prometheus datasource 3 | grafana_datasource: 4 | name: "{{ item.name }}" 5 | grafana_url: "{{ grafana_url }}" 6 | grafana_user: "{{ grafana_user }}" 7 | grafana_password: "{{ grafana_password }}" 8 | ds_type: "{{ item.ds_type }}" 9 | ds_url: "{{ item.ds_url }}" 10 | database: "{{ item.name }}" 11 | tls_skip_verify: "{{item.tls_skip_verify}}" 12 | state: present 13 | with_items: "{{ data_source }}" -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/aws_eip.tf: -------------------------------------------------------------------------------- 1 | #### Create & associate EIP with Prometheus Node 2 | 3 | ##################### 4 | #### Prometheus Nodes EIP 5 | resource "aws_eip" "prometheus_node_eip" { 6 | count = 1 7 | network_border_group = var.region 8 | vpc = true 9 | 10 | tags = { 11 | Name = format("%s-prometheus-eip-%s", var.vpc_name, count.index+1), 12 | Owner = var.owner 13 | } 14 | 15 | } 16 | 17 | #### Prometheus Node Elastic IP association 18 | resource "aws_eip_association" "prometheus_eip_assoc" { 19 | count = 1 20 | instance_id = element(aws_instance.prometheus_node.*.id, count.index) 21 | allocation_id = element(aws_eip.prometheus_node_eip.*.id, count.index) 22 | depends_on = [aws_instance.prometheus_node, aws_eip.prometheus_node_eip] 23 | } 24 | -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/aws_instances.tf: -------------------------------------------------------------------------------- 1 | #### Create EC2 Nodes for Prometheus Node 2 | 3 | # create Prometheus node 4 | resource "aws_instance" "prometheus_node" { 5 | count = 1 6 | ami = var.re_ami 7 | associate_public_ip_address = true 8 | availability_zone = element(var.subnet_azs, count.index) 9 | subnet_id = element(var.vpc_subnets_ids, count.index) 10 | instance_type = var.prometheus_instance_type 11 | key_name = var.ssh_key_name 12 | vpc_security_group_ids = var.vpc_security_group_ids 13 | source_dest_check = false 14 | 15 | tags = { 16 | Name = format("%s-prometheus-node-%s", var.vpc_name,count.index+1), 17 | Owner = var.owner 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/outputs.tf: -------------------------------------------------------------------------------- 1 | #### Outputs 2 | 3 | output "grafana_url" { 4 | value = format("http://%s:3000", aws_eip.prometheus_node_eip[0].public_ip) 5 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/prometheus-node/variables.tf: -------------------------------------------------------------------------------- 1 | #### Required Variables 2 | 3 | variable "region" { 4 | description = "AWS region" 5 | } 6 | 7 | variable "ssh_key_name" { 8 | description = "name of ssh key to be added to instance" 9 | } 10 | 11 | variable "ssh_key_path" { 12 | description = "name of ssh key to be added to instance" 13 | } 14 | 15 | variable "owner" { 16 | description = "owner tag name" 17 | } 18 | 19 | #### VPC 20 | variable "vpc_cidr" { 21 | description = "vpc-cidr" 22 | } 23 | 24 | variable "vpc_id" { 25 | description = "The ID of the VPC" 26 | } 27 | 28 | variable "vpc_name" { 29 | description = "The VPC Project Name tag" 30 | } 31 | 32 | variable "vpc_subnets_ids" { 33 | type = list(any) 34 | description = "The list of subnets available to the VPC" 35 | } 36 | 37 | variable "subnet_azs" { 38 | type = list(any) 39 | description = "subnet availability zone" 40 | default = [""] 41 | } 42 | 43 | #### prometheus Instance Variables 44 | 45 | variable "prometheus_instance_type" { 46 | description = "instance type to use. Default: t3.micro" 47 | default = "t3.micro" 48 | } 49 | 50 | ####### Node Output Variables 51 | #### pulled from node module 52 | 53 | variable "vpc_security_group_ids" { 54 | type = list 55 | description = "." 56 | default = [] 57 | } 58 | 59 | variable "re_ami" { 60 | description = "." 61 | default = "" 62 | } 63 | 64 | 65 | variable "dns_fqdn" { 66 | description = "dns_fqdn" 67 | default = "" 68 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/create_cluster.tf: -------------------------------------------------------------------------------- 1 | #### Generate ansible inventory file & extra_vars file, run ansible playbook to create cluster 2 | 3 | #### Sleeper, just to make sure nodes module is complete and everything is installed 4 | resource "time_sleep" "wait_30_seconds" { 5 | create_duration = "30s" 6 | } 7 | 8 | ##### Generate ansible inventory.ini for any number of nodes 9 | resource "local_file" "dynamic_inventory_ini" { 10 | content = templatefile("${path.module}/inventories/inventory.tpl", { 11 | re-data-node-eip-public-dns = var.re-data-node-eip-public-dns 12 | re-node-internal-ips = var.re-node-internal-ips 13 | re-node-eip-ips = var.re-node-eip-ips 14 | }) 15 | filename = "${path.module}/inventories/${var.vpc_name}_inventory.ini" 16 | } 17 | 18 | ##### Generate extra_vars.yaml file 19 | resource "local_file" "extra_vars" { 20 | content = templatefile("${path.module}/extra_vars/inventory.yaml.tpl", { 21 | ansible_user = "ubuntu" 22 | dns_fqdn = var.dns_fqdn 23 | re_cluster_username = var.re_cluster_username 24 | re_cluster_password = var.re_cluster_password 25 | re_email_from = "admin@domain.tld" 26 | re_smtp_host = "smtp.domain.tld" 27 | }) 28 | filename = "${path.module}/extra_vars/${var.vpc_name}_inventory.yaml" 29 | } 30 | 31 | ###################### 32 | # Run ansible-playbook to create cluster 33 | resource "null_resource" "ansible-run" { 34 | provisioner "local-exec" { 35 | command = "ANSIBLE_HOST_KEY_CHECKING=\"False\" ansible-playbook ${path.module}/redislabs-create-cluster.yaml --private-key ${var.ssh_key_path} -i ${path.module}/inventories/${var.vpc_name}_inventory.ini -e @${path.module}/extra_vars/${var.vpc_name}_inventory.yaml -e @${path.module}/group_vars/all/main.yaml" 36 | } 37 | depends_on = [local_file.dynamic_inventory_ini, 38 | time_sleep.wait_30_seconds, 39 | local_file.extra_vars] 40 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/extra_vars/inventory.yaml.tpl: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_user: ${ansible_user} 3 | cluster_name: ${dns_fqdn} 4 | username: ${re_cluster_username} 5 | password: ${re_cluster_password} 6 | email_from: ${re_email_from} 7 | smtp_host: ${re_smtp_host} -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/group_vars/all/main.yaml: -------------------------------------------------------------------------------- 1 | rl_ansible_vers: a.1.3 2 | redislabs_path: /opt/redislabs/bin 3 | install_dir: /tmp/re_install 4 | persistent_path: /var/opt/redislabs/persist 5 | ephemeral_path: /var/opt/redislabs/tmp 6 | pause_seconds: 15 7 | flash_path: /var/opt/redislabs/flash 8 | flash_enabled: false -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/inventories/inventory.tpl: -------------------------------------------------------------------------------- 1 | %{ for i, name in re-data-node-eip-public-dns ~} 2 | ${name} internal_ip=${re-node-internal-ips[i]} external_ip=${re-node-eip-ips[i]} 3 | %{ endfor ~} -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/outputs.tf: -------------------------------------------------------------------------------- 1 | #### Outputs 2 | 3 | output "re-cluster-url" { 4 | value = format("https://%s:8443", var.dns_fqdn) 5 | } 6 | 7 | output "re-cluster-username" { 8 | value = var.re_cluster_username 9 | } 10 | 11 | output "re-cluster-password" { 12 | value = var.re_cluster_password 13 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/redislabs-create-cluster.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | become: yes 4 | become_user: root 5 | become_method: sudo 6 | gather_facts: yes 7 | serial: 1 8 | roles: 9 | - create_cluster 10 | -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/roles/create_cluster/tasks/main.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # This task is to setup/config Redis Enterprise via the REST API. 3 | # 4 | 5 | - name: setting master host as the first host in the inventory 6 | set_fact: 7 | master_host: "{{ groups['all'][0] }}" 8 | rack_awareness: "{{ hostvars[inventory_hostname].rack_id is defined }}" 9 | 10 | - name: setting the rack id 11 | set_fact: 12 | rack_id: "{{ hostvars[inventory_hostname].rack_id }}" 13 | when: hostvars[inventory_hostname].rack_id is defined 14 | 15 | - name: setting internal IP to {{ inventory_hostname }} 16 | set_fact: 17 | internal_ip: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}" 18 | when: internal_ip is not defined 19 | 20 | - name: setting external IP to {{ internal_ip }} 21 | set_fact: 22 | external_ip: "{{ internal_ip }}" 23 | when: external_ip is not defined 24 | 25 | - name: setting master internal IP to specified internal IP 26 | set_fact: 27 | master_ip: "{{ hostvars[master_host].internal_ip }}" 28 | when: hostvars[master_host].internal_ip is defined 29 | 30 | - name: setting master internal IP to {{ inventory_hostname }} 31 | set_fact: 32 | master_ip: "{{ master_host }}" 33 | when: hostvars[master_host].internal_ip is not defined 34 | 35 | - name: setting facts 36 | set_fact: 37 | bootstrap_url: "https://{{ internal_ip }}:9443/v1/bootstrap" 38 | create_url: "https://{{ internal_ip }}:9443/v1/bootstrap/create_cluster" 39 | join_url: "https://{{ internal_ip }}:9443/v1/bootstrap/join_cluster" 40 | 41 | - name: setting the am I the master flag 42 | set_fact: 43 | i_am_the_master: "{{ master_ip == internal_ip }}" 44 | 45 | # Check bootstrapped 46 | - name: check to see if this node is in the cluster 47 | uri: 48 | url: "{{ bootstrap_url }}" 49 | method: GET 50 | user: "{{ username }}" 51 | password: "{{ password }}" 52 | force_basic_auth: yes 53 | return_content: yes 54 | validate_certs: no 55 | follow_redirects: all 56 | register: bootstap_request 57 | 58 | - name: is this node cluster joined? 59 | set_fact: 60 | cluster_joined: "{{ bootstap_request is defined and bootstap_request.json.bootstrap_status.state == 'completed' }}" 61 | 62 | - name: is the cluster rack aware 63 | debug: 64 | msg: 'Creating a cluster with rack awareness...' 65 | when: 66 | - rack_awareness 67 | 68 | - name: Creating Cluster for RedisLabs via REST API 69 | uri: 70 | url: "{{ create_url }}" 71 | method: POST 72 | user: "{{ username }}" 73 | password: "{{ password }}" 74 | force_basic_auth: yes 75 | return_content: yes 76 | validate_certs: no 77 | follow_redirects: all 78 | body: "{{ lookup('template','create_cluster.json.j2') }}" 79 | body_format: json 80 | when: 81 | - i_am_the_master 82 | - not cluster_joined 83 | 84 | 85 | - name: Join Cluster for RedisLabs via REST API 86 | uri: 87 | url: "{{ join_url }}" 88 | method: POST 89 | user: "{{ username }}" 90 | password: "{{ password }}" 91 | force_basic_auth: yes 92 | return_content: yes 93 | validate_certs: no 94 | follow_redirects: all 95 | body: "{{ lookup('template','join_cluster.json.j2') }}" 96 | body_format: json 97 | when: 98 | - not i_am_the_master 99 | - not cluster_joined 100 | 101 | - name: checking to see if we've completed bootstrapping 102 | uri: 103 | url: "{{ bootstrap_url }}" 104 | method: GET 105 | user: "{{ username }}" 106 | password: "{{ password }}" 107 | force_basic_auth: yes 108 | return_content: yes 109 | validate_certs: no 110 | follow_redirects: all 111 | register: bootstrap_status 112 | until: bootstrap_status.json.bootstrap_status.state == 'completed' 113 | delay: 10 114 | retries: 10 115 | when: 116 | - not cluster_joined -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/roles/create_cluster/templates/create_cluster.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "action": "create_cluster", 3 | "cluster": { 4 | "nodes": [], 5 | "name": "{{ cluster_name }}", 6 | "clobber": true 7 | }, 8 | "node": { 9 | {% if flash_enabled %} 10 | "bigstore_driver": "rocksdb", 11 | {% endif %} 12 | "paths": { 13 | {% if flash_enabled %} 14 | "bigstore_path": "{{ flash_path }}", 15 | {% endif %} 16 | "persistent_path": "{{ persistent_path }}", 17 | "ephemeral_path": "{{ ephemeral_path }}" 18 | }, 19 | "identity": { 20 | {% if rack_awareness %} 21 | "rack_id" : "{{ rack_id }}", 22 | {% endif %} 23 | "addr": "{{ internal_ip }}", 24 | "external_addr": [ 25 | "{{ external_ip }}" 26 | ] 27 | } 28 | }, 29 | "credentials": { 30 | "username": "{{ username }}", 31 | "password": "{{ password }}" 32 | } 33 | {% if rack_awareness %} 34 | ,"policy": { 35 | "rack_aware": true 36 | } 37 | {% endif %} 38 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/roles/create_cluster/templates/join_cluster.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "action": "join_cluster", 3 | "cluster": { 4 | "nodes": [ 5 | "{{ master_ip }}" 6 | ], 7 | "name": "{{ cluster_name }}", 8 | "clobber": true 9 | }, 10 | "node": { 11 | {% if flash_enabled %} 12 | "bigstore_driver": "rocksdb", 13 | {% endif %} 14 | "paths": { 15 | {% if flash_enabled %} 16 | "bigstore_path": "{{ flash_path }}", 17 | {% endif %} 18 | "persistent_path": "{{ persistent_path }}", 19 | "ephemeral_path": "{{ ephemeral_path }}" 20 | }, 21 | "identity": { 22 | {% if rack_awareness %} 23 | "rack_id" : "{{ rack_id }}", 24 | {% endif %} 25 | "addr": "{{ internal_ip }}", 26 | "external_addr": [ 27 | "{{ external_ip }}" 28 | ] 29 | } 30 | }, 31 | "credentials": { 32 | "username": "{{ username }}", 33 | "password": "{{ password }}" 34 | } 35 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/re-cluster/variables.tf: -------------------------------------------------------------------------------- 1 | #### Required Variables 2 | variable "region" { 3 | description = "AWS region" 4 | } 5 | 6 | variable "ssh_key_path" { 7 | description = "name of ssh key to be added to instance" 8 | } 9 | 10 | variable "vpc_name" { 11 | description = "The VPC Project Name tag" 12 | } 13 | 14 | ####### Create Cluster Variables 15 | ####### Node and DNS outputs used to Create Cluster 16 | #### created during node module and used as outputs (no input required) 17 | variable "dns_fqdn" { 18 | description = "." 19 | default = "" 20 | } 21 | 22 | variable "re-node-internal-ips" { 23 | type = list 24 | description = "." 25 | default = [] 26 | } 27 | 28 | variable "re-node-eip-ips" { 29 | type = list 30 | description = "." 31 | default = [] 32 | } 33 | 34 | variable "re-data-node-eip-public-dns" { 35 | type = list 36 | description = "." 37 | default = [] 38 | } 39 | 40 | ############# Create RE Cluster Variables 41 | 42 | #### Cluster Inputs 43 | #### RE Cluster Username 44 | variable "re_cluster_username" { 45 | description = "redis enterprise cluster username" 46 | default = "admin@admin.com" 47 | } 48 | 49 | #### RE Cluster Password 50 | variable "re_cluster_password" { 51 | description = "redis enterprise cluster password" 52 | default = "admin" 53 | } 54 | 55 | -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/ansible/config/ssh.tpl: -------------------------------------------------------------------------------- 1 | Host * 2 | User ubuntu 3 | IdentityFile ~/.ssh/${vpc_name}.pem 4 | ForwardAgent yes 5 | GSSAPIAuthentication no 6 | VerifyHostKeyDNS no 7 | HashKnownHosts no 8 | TCPKeepAlive yes 9 | ServerAliveInterval 300 10 | StrictHostKeyChecking no 11 | UserKnownHostsFile=/dev/null 12 | IdentitiesOnly yes -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/ansible/inventories/inventory_test.tpl: -------------------------------------------------------------------------------- 1 | [all] 2 | ${host_ip} ansible_ssh_common_args='-o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -F /tmp/${vpc_name}_triton_node.cfg' -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/ansible/playbooks/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | deb_packages: 4 | - jq 5 | - tree 6 | - sysstat 7 | - iputils-ping 8 | - libcap2-bin 9 | - build-essential 10 | - autoconf 11 | - automake 12 | - libbsd-dev 13 | - libltdl-dev 14 | - libltdl7 15 | - libtool 16 | - libevent-openssl-2.1-6 17 | - libpcre3-dev 18 | - libevent-dev 19 | - pkg-config 20 | - zlib1g-dev 21 | - libssl-dev 22 | - ntp -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/ansible/playbooks/playbook_test_node.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: all 4 | become: yes 5 | become_user: root 6 | become_method: sudo 7 | gather_facts: yes 8 | 9 | vars: 10 | deb_packages: 11 | - build-essential 12 | - autoconf 13 | - automake 14 | - libbsd-dev 15 | - libltdl-dev 16 | - libltdl7 17 | - libtool 18 | - libpcre3-dev 19 | - libevent-dev 20 | - pkg-config 21 | - zlib1g-dev 22 | - libssl-dev 23 | - nginx 24 | 25 | handlers: 26 | - name: restart_nginx 27 | service: 28 | name: nginx 29 | state: restarted 30 | 31 | 32 | pre_tasks: 33 | - name: Update Apt Cache 34 | apt: update_cache=yes 35 | when: ansible_os_family == "Debian" 36 | - name: Ubuntu Packages 37 | package: 38 | name: "{{ deb_packages }}" 39 | - name: Set to Realistic Hostname 40 | hostname: 41 | name: "tester" 42 | - name: Setup Hostsfile 43 | lineinfile: 44 | dest: /etc/hosts 45 | regexp: '^127\.0\.0\.1[ \t]+localhost' 46 | line: "127.0.0.1 localhost tester" 47 | state: present 48 | 49 | tasks: 50 | - name: load vars 51 | include_vars: default.yaml 52 | - name: create redis user 53 | user: 54 | name: redis 55 | uid: 4001 56 | shell: /bin/bash 57 | state: present 58 | 59 | - name: checkout redis git repo 60 | git: 61 | repo: 'https://github.com/antirez/redis' 62 | dest: /home/redis/redis 63 | 64 | - name: redis - Build the default target 65 | make: 66 | chdir: /home/redis/redis 67 | 68 | - name: redis - Run 'install' target as root 69 | make: 70 | chdir: /home/redis/redis 71 | target: install 72 | become: yes 73 | 74 | - name: checkout memtier_benchmark git repo 75 | git: 76 | repo: 'https://github.com/RedisLabs/memtier_benchmark' 77 | dest: /home/redis/memtier_benchmark 78 | 79 | - name: memtier - Run the autoconf 80 | command: autoreconf -ivf 81 | args: 82 | chdir: /home/redis/memtier_benchmark 83 | creates: /home/redis/memtier_benchmark/configure 84 | 85 | - name: memtier - Run the configure script first 86 | command: ./configure 87 | args: 88 | chdir: /home/redis/memtier_benchmark 89 | creates: /home/redis/memtier_benchmark/Makefile 90 | 91 | - name: memtier - Build the default target 92 | make: 93 | chdir: /home/redis/memtier_benchmark 94 | 95 | - name: Run 'memtier_benchmark install' target as root 96 | make: 97 | chdir: /home/redis/memtier_benchmark 98 | target: install 99 | become: yes 100 | 101 | post_tasks: 102 | - name: create the redis dir 103 | file: 104 | path: /redis 105 | owner: redis 106 | group: redis 107 | mode: '0755' 108 | state: directory -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/aws_eip.tf: -------------------------------------------------------------------------------- 1 | #### Create & associate EIP with Triton Nodes 2 | 3 | ##################### 4 | #### Triton Nodes EIP 5 | resource "aws_eip" "triton_node_eip" { 6 | count = var.triton-node-count 7 | network_border_group = var.region 8 | vpc = true 9 | 10 | tags = { 11 | Name = format("%s-triton-eip-%s", var.vpc_name, count.index+1), 12 | Owner = var.owner 13 | } 14 | 15 | } 16 | 17 | #### Test Node Elastic IP association 18 | resource "aws_eip_association" "triton_eip_assoc" { 19 | count = var.triton-node-count 20 | instance_id = element(aws_instance.triton_node.*.id, count.index) 21 | allocation_id = element(aws_eip.triton_node_eip.*.id, count.index) 22 | depends_on = [aws_instance.triton_node, aws_eip.triton_node_eip] 23 | } 24 | -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/aws_instances.tf: -------------------------------------------------------------------------------- 1 | #### Create EC2 Nodes for Triton 2 | 3 | resource "aws_instance" "triton_node" { 4 | count = var.triton-node-count 5 | ami = var.triton_ami 6 | associate_public_ip_address = true 7 | availability_zone = element(var.subnet_azs, count.index) 8 | subnet_id = element(var.vpc_subnets_ids, count.index) 9 | instance_type = var.triton_instance_type 10 | key_name = var.ssh_key_name 11 | vpc_security_group_ids = var.vpc_security_group_ids 12 | source_dest_check = false 13 | 14 | tags = { 15 | Name = format("%s-triton-node-%s", var.vpc_name,count.index+1), 16 | Owner = var.owner 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/outputs.tf: -------------------------------------------------------------------------------- 1 | #### Outputs 2 | 3 | ### triton node 4 | output "triton-node-eips" { 5 | value = aws_eip.triton_node_eip[*].public_ip 6 | } 7 | -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/provisioning_test_node.tf: -------------------------------------------------------------------------------- 1 | #### Generating Ansible config, inventory, playbook 2 | #### and configuring test nodes and installing Redis and Memtier 3 | 4 | #### Sleeper, after instance, eip assoc, local file inventories & cfg created 5 | #### otherwise it can run to fast, not find the inventory file and fail or hang 6 | resource "time_sleep" "wait_30_seconds_test" { 7 | create_duration = "30s" 8 | depends_on = [aws_instance.triton_node, 9 | aws_eip_association.triton_eip_assoc, 10 | local_file.inventory_setup_test, 11 | local_file.ssh-setup-test] 12 | } 13 | 14 | # remote-config waits till the node is accessible 15 | resource "null_resource" "remote_config_test" { 16 | count = var.triton-node-count 17 | provisioner "remote-exec" { 18 | inline = ["sudo apt update > /dev/null"] 19 | 20 | connection { 21 | type = "ssh" 22 | user = "ubuntu" 23 | private_key = file(var.ssh_key_path) 24 | host = element(aws_eip.triton_node_eip.*.public_ip, count.index) 25 | } 26 | } 27 | 28 | depends_on = [aws_instance.triton_node, 29 | aws_eip_association.triton_eip_assoc, 30 | local_file.inventory_setup_test, 31 | local_file.ssh-setup-test, 32 | time_sleep.wait_30_seconds_test] 33 | } 34 | 35 | #### Generate Ansible Inventory for each node 36 | resource "local_file" "inventory_setup_test" { 37 | count = var.triton-node-count 38 | content = templatefile("${path.module}/ansible/inventories/inventory_test.tpl", { 39 | host_ip = element(aws_eip.triton_node_eip.*.public_ip, count.index) 40 | vpc_name = var.vpc_name 41 | }) 42 | filename = "/tmp/${var.vpc_name}_triton_node_${count.index}.ini" 43 | depends_on = [aws_instance.triton_node, aws_eip_association.triton_eip_assoc] 44 | } 45 | 46 | #### Generate ansible.cfg file 47 | resource "local_file" "ssh-setup-test" { 48 | content = templatefile("${path.module}/ansible/config/ssh.tpl", { 49 | vpc_name = var.vpc_name 50 | }) 51 | filename = "/tmp/${var.vpc_name}_triton_node.cfg" 52 | depends_on = [aws_instance.triton_node, aws_eip_association.triton_eip_assoc] 53 | } 54 | 55 | ###################### 56 | # Run ansible playbook to install redis and memtier 57 | resource "null_resource" "ansible_test_run" { 58 | count = var.triton-node-count 59 | provisioner "local-exec" { 60 | command = "ansible-playbook ${path.module}/ansible/playbooks/playbook_test_node.yaml --private-key ${var.ssh_key_path} -i /tmp/${var.vpc_name}_triton_node_${count.index}.ini" 61 | } 62 | depends_on = [null_resource.remote_config_test, time_sleep.wait_30_seconds_test] 63 | } 64 | -------------------------------------------------------------------------------- /cloud-deployment/modules/triton-nodes/variables.tf: -------------------------------------------------------------------------------- 1 | #### Required Variables 2 | 3 | variable "region" { 4 | description = "AWS region" 5 | } 6 | 7 | variable "ssh_key_name" { 8 | description = "name of ssh key to be added to instance" 9 | } 10 | 11 | variable "ssh_key_path" { 12 | description = "name of ssh key to be added to instance" 13 | } 14 | 15 | variable "owner" { 16 | description = "owner tag name" 17 | } 18 | 19 | #### VPC 20 | variable "vpc_cidr" { 21 | description = "vpc-cidr" 22 | } 23 | 24 | variable "vpc_id" { 25 | description = "The ID of the VPC" 26 | } 27 | 28 | variable "vpc_name" { 29 | description = "The VPC Project Name tag" 30 | } 31 | 32 | variable "vpc_subnets_ids" { 33 | type = list(any) 34 | description = "The list of subnets available to the VPC" 35 | } 36 | 37 | variable "subnet_azs" { 38 | type = list(any) 39 | description = "subnet availability zone" 40 | default = [""] 41 | } 42 | 43 | #### Triton Instance Variables 44 | 45 | variable "triton-node-count" { 46 | description = "number of triton nodes" 47 | default = 1 48 | } 49 | 50 | variable "triton_instance_type" { 51 | description = "instance type to use. Default: t3.micro" 52 | default = "g4dn.xlarge" 53 | } 54 | 55 | ####### Node Output Variables 56 | #### used in additional modules 57 | 58 | variable "vpc_security_group_ids" { 59 | type = list 60 | description = "." 61 | default = [] 62 | } 63 | 64 | variable "triton_ami" { 65 | description = "Amazon machine image for triton" 66 | default = "ami-0b61d2979f583d63d" 67 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/vpc/aws_vpc.tf: -------------------------------------------------------------------------------- 1 | #### Resource: aws_vpc (provides a VPC resource) 2 | #### and associated resources for vpc. 3 | 4 | #### Create a VPC 5 | resource "aws_vpc" "redis_cluster_vpc" { 6 | cidr_block = var.vpc_cidr 7 | enable_dns_support = true 8 | enable_dns_hostnames = true 9 | 10 | tags = { 11 | Name = format("%s-%s-cluster-vpc", var.base_name, var.region), 12 | Project = format("%s-%s-cluster", var.base_name, var.region), 13 | Owner = var.owner 14 | } 15 | } 16 | 17 | data "aws_vpc" "re-vpc-data" { 18 | id = aws_vpc.redis_cluster_vpc.id 19 | } 20 | 21 | 22 | #### Create private subnets 23 | resource "aws_subnet" "re_subnet1" { 24 | vpc_id = aws_vpc.redis_cluster_vpc.id 25 | cidr_block = var.subnet_cidr_blocks[0] 26 | availability_zone = var.subnet_azs[0] 27 | 28 | tags = { 29 | Name = format("%s-subnet1", var.base_name), 30 | Project = format("%s-%s-cluster", var.base_name, var.region), 31 | Owner = var.owner 32 | } 33 | } 34 | 35 | resource "aws_subnet" "re_subnet2" { 36 | vpc_id = aws_vpc.redis_cluster_vpc.id 37 | cidr_block = var.subnet_cidr_blocks[1] 38 | availability_zone = var.subnet_azs[1] 39 | 40 | tags = { 41 | Name = format("%s-subnet2", var.base_name), 42 | Project = format("%s-%s-cluster", var.base_name, var.region), 43 | Owner = var.owner 44 | } 45 | } 46 | 47 | resource "aws_subnet" "re_subnet3" { 48 | vpc_id = aws_vpc.redis_cluster_vpc.id 49 | cidr_block = var.subnet_cidr_blocks[2] 50 | availability_zone = var.subnet_azs[2] 51 | 52 | tags = { 53 | Name = format("%s-subnet3", var.base_name), 54 | Project = format("%s-%s-cluster", var.base_name, var.region), 55 | Owner = var.owner 56 | } 57 | } 58 | 59 | #### network 60 | #### Create Internet Gateway 61 | resource "aws_internet_gateway" "igw" { 62 | vpc_id = aws_vpc.redis_cluster_vpc.id 63 | tags = { 64 | Name = format("%s-igw", var.base_name), 65 | Project = format("%s-%s-cluster", var.base_name, var.region), 66 | Owner = var.owner 67 | } 68 | } 69 | 70 | #### Create a custom route table 71 | #### (custom route table for the subnet) 72 | resource "aws_default_route_table" "route_table" { 73 | default_route_table_id = aws_vpc.redis_cluster_vpc.default_route_table_id 74 | route { 75 | cidr_block = "0.0.0.0/0" 76 | gateway_id = aws_internet_gateway.igw.id 77 | } 78 | tags = { 79 | Name = format("%s-rt", var.base_name), 80 | Project = format("%s-%s-cluster", var.base_name, var.region), 81 | Owner = var.owner 82 | } 83 | } 84 | 85 | #### associate the route table to the subnet. 86 | resource "aws_route_table_association" "subnet_association1" { 87 | subnet_id = aws_subnet.re_subnet1.id 88 | route_table_id = aws_default_route_table.route_table.id 89 | } 90 | resource "aws_route_table_association" "subnet_association2" { 91 | subnet_id = aws_subnet.re_subnet2.id 92 | route_table_id = aws_default_route_table.route_table.id 93 | } 94 | resource "aws_route_table_association" "subnet_association3" { 95 | subnet_id = aws_subnet.re_subnet3.id 96 | route_table_id = aws_default_route_table.route_table.id 97 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | 2 | output "base_name" { 3 | value = var.base_name 4 | } 5 | 6 | output "region" { 7 | value = var.region 8 | } 9 | 10 | output "subnet-azs" { 11 | value = var.subnet_azs 12 | } 13 | 14 | 15 | output "subnet-ids" { 16 | value = [aws_subnet.re_subnet1.id, 17 | aws_subnet.re_subnet2.id, 18 | aws_subnet.re_subnet3.id] 19 | } 20 | 21 | output "vpc-id" { 22 | value = aws_vpc.redis_cluster_vpc.id 23 | } 24 | 25 | output "vpc-name" { 26 | description = "get all tags, get the Project Name tag for the VPC" 27 | value = aws_vpc.redis_cluster_vpc.tags_all.Project 28 | } -------------------------------------------------------------------------------- /cloud-deployment/modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "region" { 2 | description = "AWS region" 3 | } 4 | 5 | variable "aws_creds" { 6 | description = "Access key and Secret key for AWS [Access Keys, Secret Key]" 7 | } 8 | 9 | variable "owner" { 10 | description = "owner tag name" 11 | } 12 | 13 | #### VPC 14 | variable "vpc_cidr" { 15 | description = "vpc-cidr" 16 | default = "10.0.0.0/16" 17 | } 18 | 19 | variable "base_name" { 20 | description = "base name for resources" 21 | default = "redisuser1-tf" 22 | } 23 | 24 | variable "subnet_cidr_blocks" { 25 | type = list(any) 26 | description = "subnet_cidr_block" 27 | default = ["10.0.0.0/24","10.0.16.0/24","10.0.32.0/24"] 28 | } 29 | 30 | variable "subnet_azs" { 31 | type = list(any) 32 | description = "subnet availability zone (3 required)" 33 | default = [""] 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /cloud-deployment/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws" 5 | version = "~> 3.0" 6 | } 7 | } 8 | } 9 | 10 | #### AWS region and AWS key pair 11 | provider "aws" { 12 | region = var.region 13 | access_key = var.aws_creds[0] 14 | secret_key = var.aws_creds[1] 15 | } -------------------------------------------------------------------------------- /cloud-deployment/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | #### User Input Required 2 | #### Enter your user variables here 3 | #### Some variables have default values and if you do not specify 4 | 5 | #### User Input Required 6 | #### Access key and Secret key for aws account [AWS_ACCESS_KEY, AWS_SECRET_KEY] 7 | #### (fake example: aws_creds = ["myAccesssKeyxjdklfdakf","MySecretKeyxkldkfhadjkfh"]) 8 | aws_creds = 9 | 10 | #### User Input Required 11 | #### name of ssh key (.pem file) to be added to instance (AWS key pair name) 12 | #### ie. if your AWS ssh keys name is "my-ssh-key.pem", enter in "my-ssh-key" 13 | #### the SSH key must have already had "chmod 400 my-ssh-key.pem" run on it before using 14 | #### (fake example: ssh_key_name = "my-ssh-key") 15 | ssh_key_name = 16 | 17 | #### User Input Required 18 | #### path to your the SSH key .pem file. 19 | #### (fake example: ssh_key_path = "~/keys/my-ssh-key.pem") 20 | ssh_key_path = 21 | 22 | #### User Input Required 23 | #### DNS hosted zone id (find value in R53 hosted zones) 24 | ### navigate to Route 53 in the AWS console, click hosted zones, 25 | ### find hosted zone "domain name" of interest, use its "hosted zone ID" (fake example: dns_hosted_zone_id="Z903232kljadfdk") 26 | dns_hosted_zone_id = 27 | 28 | #### Owner tag name of resources 29 | #### example: owner = "redisuser" 30 | owner = "redisuser" 31 | 32 | #### AWS Region 33 | #### example: region = "us-west-2" 34 | region = "us-west-2" 35 | 36 | #### Base Name of Resources 37 | #### (Resource prefix for all generated resources) 38 | #### default = "redisuser1-tf" 39 | base_name = "redis-tf" 40 | 41 | ################### VPC Variables 42 | 43 | #### example: vpc_cidr = ""10.0.0.0/16"" 44 | vpc_cidr = "10.0.0.0/16" 45 | 46 | #### Subnet CIDR Block 47 | #### example: subnet_cidr_blocks = ["10.0.0.0/24","10.0.16.0/24","10.0.32.0/24"] 48 | subnet_cidr_blocks = ["10.0.0.0/24","10.0.16.0/24","10.0.32.0/24"] 49 | 50 | #### Subnet AZ 51 | #### example: subnet_azs = ["us-west-2a","us-west-2b","us-west-2c"] 52 | subnet_azs = ["us-west-2a","us-west-2b","us-west-2c"] 53 | 54 | ############### Triton Instance Variables 55 | 56 | #### instance type to use for GPU triton node 57 | #### example: triton_instance_type = "g4dn.xlarge" 58 | triton_instance_type = "g4dn.xlarge" 59 | 60 | #### example: triton-node-count = 1 61 | triton-node-count = 1 62 | 63 | #### AMI for triton 64 | triton_ami = "ami-0b61d2979f583d63d" 65 | 66 | 67 | ############## Redis Enterprise Nodes Variables 68 | 69 | #### User Input Required 70 | #### RE Software download url (MUST BE ubuntu 18.04) 71 | #### (FAKE example (update the x.x.xx with the Redis Software version!): re_download_url = "https://s3.amazonaws.com/redis-enterprise-software-downloads/x.x.xx/redislabs-x.x.xx-68-bionic-amd64.tar") 72 | re_download_url = 73 | 74 | #### how many data nodes, 3 minimum, (odd numbers required) 75 | #### example: data-node-count = 3 76 | data-node-count = 3 77 | 78 | ##### redis enterprise software instance type 79 | ##### example: re_instance_type = "t2.xlarge" 80 | re_instance_type = "t2.xlarge" 81 | 82 | ##### EBS volume for persistent and ephemeral storage 83 | #### example: re-volume-size = 150 84 | re-volume-size = "50" 85 | 86 | ##### SECURITY 87 | ##### example: allow-public-ssh = 1 88 | allow-public-ssh = 1 89 | 90 | ##### explanation... 91 | ##### example: open-nets = ["10.0.0.0/16"] 92 | open-nets = ["10.0.0.0/16"] 93 | 94 | 95 | ############# Create RE Cluster Variables 96 | 97 | #### Cluster Inputs 98 | #### RE Cluster Username 99 | #### example: re_cluster_username = "admin@admin.com" 100 | re_cluster_username = "admin@admin.com" 101 | 102 | #### RE Cluster Password 103 | #### example: re_cluster_password = "admin" 104 | re_cluster_password = "admin" 105 | 106 | -------------------------------------------------------------------------------- /large-scale-recsys/docker-compose-cloud.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | merlin: 4 | image: "nvcr.io/nvidia/merlin/merlin-hugectr:22.10" 5 | privileged: true 6 | command: ["jupyter", "lab", "--no-browser", "--allow-root", "--ip", "0.0.0.0", "--port", "8888"] 7 | ports: 8 | - 8888:8888 9 | - 8000:8000 10 | - 8001:8001 11 | - 8002:8002 12 | environment: 13 | NVIDIA_VISIBLE_DEVICES: all 14 | volumes: 15 | - '../data:/model-data/' 16 | - './:/workdir' 17 | working_dir: /workdir 18 | deploy: 19 | resources: 20 | reservations: 21 | devices: 22 | - driver: "nvidia" 23 | capabilities: ["gpu"] 24 | count: 1 25 | -------------------------------------------------------------------------------- /large-scale-recsys/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | merlin: 4 | image: "nvcr.io/nvidia/merlin/merlin-hugectr:22.12" 5 | privileged: true 6 | command: ["jupyter", "lab", "--no-browser", "--allow-root", "--ip", "0.0.0.0", "--port", "8888"] 7 | ports: 8 | - 8888:8888 9 | - 8000:8000 10 | - 8001:8001 11 | - 8002:8002 12 | environment: 13 | NVIDIA_VISIBLE_DEVICES: all 14 | REDIS_NODE_1: redis-node-1:6373 15 | REDIS_NODE_2: redis-node-2:6374 16 | REDIS_NODE_3: redis-node-3:6375 17 | volumes: 18 | - '../data:/model-data/' 19 | - './:/workdir' 20 | working_dir: /workdir 21 | networks: 22 | app_subnet: 23 | ipv4_address: 172.20.0.10 24 | depends_on: 25 | - redis-cluster 26 | deploy: 27 | resources: 28 | reservations: 29 | devices: 30 | - driver: "nvidia" 31 | capabilities: ["gpu"] 32 | count: 1 33 | 34 | redis-cluster: 35 | image: 'redis:7.0-alpine' 36 | command: redis-cli --cluster create 172.20.0.31:6373 172.20.0.32:6374 172.20.0.33:6375 --cluster-yes 37 | networks: 38 | app_subnet: 39 | ipv4_address: 172.20.0.30 40 | depends_on: 41 | - redis-node-1 42 | - redis-node-2 43 | - redis-node-3 44 | redis-node-1: 45 | image: 'redis:7.0-alpine' 46 | command: redis-server /usr/local/etc/redis/redis.conf 47 | ports: 48 | - '6373:6373' 49 | volumes: 50 | - redis-node-1-data:/var/lib/redis 51 | - ./redis-confs/redis-1.conf:/usr/local/etc/redis/redis.conf 52 | networks: 53 | app_subnet: 54 | ipv4_address: 172.20.0.31 55 | redis-node-2: 56 | image: 'redis:7.0-alpine' 57 | command: redis-server /usr/local/etc/redis/redis.conf 58 | ports: 59 | - '6374:6374' 60 | volumes: 61 | - redis-node-2-data:/var/lib/redis 62 | - ./redis-confs/redis-2.conf:/usr/local/etc/redis/redis.conf 63 | networks: 64 | app_subnet: 65 | ipv4_address: 172.20.0.32 66 | redis-node-3: 67 | image: 'redis:7.0-alpine' 68 | command: redis-server /usr/local/etc/redis/redis.conf 69 | ports: 70 | - '6375:6375' 71 | volumes: 72 | - redis-node-3-data:/var/lib/redis 73 | - ./redis-confs/redis-3.conf:/usr/local/etc/redis/redis.conf 74 | networks: 75 | app_subnet: 76 | ipv4_address: 172.20.0.33 77 | 78 | volumes: 79 | merlin: 80 | redis-node-1-data: 81 | redis-node-2-data: 82 | redis-node-3-data: 83 | networks: 84 | app_subnet: 85 | driver: bridge 86 | ipam: 87 | config: 88 | - subnet: 172.20.0.0/24 -------------------------------------------------------------------------------- /large-scale-recsys/redis-confs/redis-1.conf: -------------------------------------------------------------------------------- 1 | port 6373 2 | cluster-enabled yes 3 | cluster-config-file nodes.conf 4 | cluster-node-timeout 5000 5 | appendonly no 6 | save "" 7 | protected-mode no -------------------------------------------------------------------------------- /large-scale-recsys/redis-confs/redis-2.conf: -------------------------------------------------------------------------------- 1 | port 6374 2 | cluster-enabled yes 3 | cluster-config-file nodes-1.conf 4 | cluster-node-timeout 5000 5 | appendonly no 6 | save "" 7 | protected-mode no 8 | -------------------------------------------------------------------------------- /large-scale-recsys/redis-confs/redis-3.conf: -------------------------------------------------------------------------------- 1 | port 6375 2 | cluster-enabled yes 3 | cluster-config-file nodes-2.conf 4 | cluster-node-timeout 5000 5 | appendonly no 6 | save "" 7 | protected-mode no 8 | -------------------------------------------------------------------------------- /offline-batch-recsys/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvcr.io/nvidia/merlin/merlin-tensorflow:22.11 2 | 3 | WORKDIR /workdir 4 | 5 | COPY ./requirements.txt . 6 | RUN python3 -m pip install -r requirements.txt 7 | 8 | # Install Curl 9 | RUN apt-get update \ 10 | && echo "Installing curl" \ 11 | && apt-get install -y curl \ 12 | && echo "Installing Redis" \ 13 | && apt-get install -y redis \ 14 | && apt install -y redis-tools \ 15 | && apt install -y tree 16 | 17 | # Define this parameter to install jupyter lab 18 | ENV JUPYTER_ENABLE_LAB=yes 19 | 20 | CMD jupyter lab --no-browser --ip=0.0.0.0 --allow-root --port=8888 21 | -------------------------------------------------------------------------------- /offline-batch-recsys/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | offline-recsys-training: 5 | container_name: offline-recsys-training 6 | build: 7 | context: ./ 8 | dockerfile: ./Dockerfile 9 | privileged: true 10 | environment: 11 | NVIDIA_VISIBLE_DEVICES: all 12 | ports: 13 | - 8888:8888 14 | volumes: 15 | - '../data:/model-data/' 16 | - ./:/workdir 17 | depends_on: 18 | - redis-inference-store 19 | deploy: 20 | resources: 21 | reservations: 22 | devices: 23 | - driver: "nvidia" 24 | capabilities: ["gpu"] 25 | count: 1 26 | redis-inference-store: 27 | image: redis/redis-stack:latest 28 | ports: 29 | - '6379:6379' 30 | 31 | -------------------------------------------------------------------------------- /offline-batch-recsys/requirements.txt: -------------------------------------------------------------------------------- 1 | jupyterlab==3.5.2 2 | redis==4.4.2 -------------------------------------------------------------------------------- /online-multi-stage-recsys/client.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import numpy as np 3 | import json 4 | import tritonclient.grpc as triton_grpc 5 | import argparse 6 | import time 7 | 8 | 9 | if __name__ == "__main__": 10 | parser = argparse.ArgumentParser() 11 | parser.add_argument("--model_name", 12 | type=str, 13 | required=False, 14 | default="ensemble-model", 15 | help="Model name") 16 | parser.add_argument("--url", 17 | type=str, 18 | required=False, 19 | default="localhost:8001", 20 | help="Inference server URL. Default is localhost:8001.") 21 | parser.add_argument("--user", 22 | type=int, 23 | required=False, 24 | default=7, 25 | help="User ID to find Recommendations") 26 | parser.add_argument('-v', 27 | "--verbose", 28 | action="store_true", 29 | required=False, 30 | default=False, 31 | help='Enable verbose output') 32 | args = parser.parse_args() 33 | 34 | try: 35 | triton_client = triton_grpc.InferenceServerClient(url=args.url, verbose=args.verbose) 36 | except Exception as e: 37 | print("channel creation failed: " + str(e)) 38 | sys.exit(1) 39 | 40 | 41 | inputs = [] 42 | outputs = [] 43 | input_name = "user_id_raw" 44 | output_name = "ordered_ids" 45 | inp = np.array([int(args.user)], dtype=np.int32).reshape(-1, 1) 46 | print("Finding recommendations for User", inp[0][0], flush=True) 47 | 48 | t = time.time() 49 | 50 | inputs.append(triton_grpc.InferInput(input_name, inp.shape, "INT32")) 51 | outputs.append(triton_grpc.InferRequestedOutput(output_name)) 52 | inputs[0].set_data_from_numpy(inp) 53 | results = triton_client.infer(model_name=args.model_name, 54 | inputs=inputs, 55 | outputs=outputs) 56 | 57 | output0_data = results.as_numpy(output_name) 58 | print("Recommended Product Ids in", time.time()-t, "seconds", flush=True) 59 | print(output0_data, flush=True) -------------------------------------------------------------------------------- /online-multi-stage-recsys/docker-compose-cloud.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | online-recsys: 4 | image: "nvcr.io/nvidia/merlin/merlin-tensorflow:22.11" 5 | privileged: true 6 | command: ["pip install -r requirements.txt && jupyter lab --no-browser --ip=0.0.0.0 --allow-root --port=8888"] 7 | entrypoint: ["/bin/sh", "-c"] 8 | ports: 9 | - 8888:8888 10 | - 8000:8000 11 | - 8001:8001 12 | - 8002:8002 13 | shm_size: '5G' 14 | environment: 15 | NVIDIA_VISIBLE_DEVICES: all 16 | FEATURE_STORE_ADDRESS: "50.18.102.210:10657" 17 | volumes: 18 | - '../data:/model-data/' 19 | - './:/workdir' 20 | working_dir: /workdir 21 | deploy: 22 | resources: 23 | reservations: 24 | devices: 25 | - driver: "nvidia" 26 | capabilities: ["gpu"] 27 | count: 1 28 | 29 | 30 | volumes: 31 | online-recsys: 32 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | online-recsys: 4 | image: "nvcr.io/nvidia/merlin/merlin-tensorflow:22.11" 5 | privileged: true 6 | command: ["pip install -r requirements.txt && jupyter lab --no-browser --ip=0.0.0.0 --allow-root --port=8888"] 7 | entrypoint: ["/bin/sh", "-c"] 8 | ports: 9 | - 8888:8888 10 | - 8000:8000 11 | - 8001:8001 12 | - 8002:8002 13 | shm_size: '512mb' 14 | environment: 15 | NVIDIA_VISIBLE_DEVICES: all 16 | FEATURE_STORE_ADDRESS: "172.20.0.20:6379" 17 | volumes: 18 | - '../data:/model-data/' 19 | - './:/workdir' 20 | working_dir: /workdir 21 | depends_on: 22 | - redis-feature-store 23 | networks: 24 | app_subnet: 25 | ipv4_address: 172.20.0.10 26 | deploy: 27 | resources: 28 | reservations: 29 | devices: 30 | - driver: "nvidia" 31 | capabilities: ["gpu"] 32 | count: 1 33 | 34 | redis-feature-store: 35 | image: redis/redis-stack:latest 36 | ports: 37 | - 6379:6379 38 | volumes: 39 | - redis-feature-store:/var/lib/redis 40 | networks: 41 | app_subnet: 42 | ipv4_address: 172.20.0.20 43 | 44 | volumes: 45 | online-recsys: 46 | redis-feature-store: 47 | networks: 48 | app_subnet: 49 | driver: bridge 50 | ipam: 51 | config: 52 | - subnet: 172.20.0.0/24 -------------------------------------------------------------------------------- /online-multi-stage-recsys/feature_repo/feature_store.yaml: -------------------------------------------------------------------------------- 1 | project: merlin_feature_store 2 | registry: data/registry.db 3 | provider: local 4 | online_store: 5 | type: redis 6 | redis_type: redis 7 | connection_string: "172.20.0.20:6379" 8 | entity_key_serialization_version: 2 -------------------------------------------------------------------------------- /online-multi-stage-recsys/feature_repo/item_features.py: -------------------------------------------------------------------------------- 1 | import os.path as osp 2 | 3 | from datetime import timedelta 4 | from feast.types import Int32 5 | from feast import Entity, Field, FeatureView, ValueType 6 | from feast.infra.offline_stores.file_source import FileSource 7 | 8 | 9 | dir_path = osp.dirname(osp.abspath(__file__)) 10 | data_path = osp.join(dir_path, "data") 11 | 12 | item_features = FileSource( 13 | path=f"{data_path}/item_features.parquet", 14 | timestamp_field="datetime", 15 | created_timestamp_column="created", 16 | ) 17 | 18 | item = Entity(name="item_id", value_type=ValueType.INT32, description="item id") 19 | 20 | item_features_view = FeatureView( 21 | name="item_features", 22 | entities=[item], 23 | ttl=timedelta(days=365), 24 | schema=[ 25 | Field(name="item_category", dtype=Int32), 26 | Field(name="item_shop", dtype=Int32), 27 | Field(name="item_brand", dtype=Int32), 28 | Field(name="item_id_raw", dtype=Int32), 29 | ], 30 | online=True, 31 | source=item_features, 32 | ) -------------------------------------------------------------------------------- /online-multi-stage-recsys/feature_repo/user_features.py: -------------------------------------------------------------------------------- 1 | import os.path as osp 2 | 3 | from datetime import timedelta 4 | from feast.types import Int32 5 | from feast import Entity, Field, FeatureView, ValueType 6 | from feast.infra.offline_stores.file_source import FileSource 7 | 8 | 9 | 10 | dir_path = osp.dirname(osp.abspath(__file__)) 11 | data_path = osp.join(dir_path, "data") 12 | 13 | user_features = FileSource( 14 | path=f"{data_path}/user_features.parquet", 15 | timestamp_field="datetime", 16 | created_timestamp_column="created", 17 | ) 18 | 19 | user_raw = Entity(name="user_id_raw", value_type=ValueType.INT32, description="user id raw") 20 | 21 | user_features_view = FeatureView( 22 | name="user_features", 23 | entities=[user_raw], 24 | ttl=timedelta(days=365), 25 | schema=[ 26 | Field(name="user_shops", dtype=Int32), 27 | Field(name="user_profile", dtype=Int32), 28 | Field(name="user_group", dtype=Int32), 29 | Field(name="user_gender", dtype=Int32), 30 | Field(name="user_age", dtype=Int32), 31 | Field(name="user_consumption_2", dtype=Int32), 32 | Field(name="user_is_occupied", dtype=Int32), 33 | Field(name="user_geography", dtype=Int32), 34 | Field(name="user_intentions", dtype=Int32), 35 | Field(name="user_brands", dtype=Int32), 36 | Field(name="user_categories", dtype=Int32), 37 | Field(name="user_id", dtype=Int32), 38 | ], 39 | online=True, 40 | source=user_features 41 | ) -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | input { 4 | name: "user_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | output { 10 | name: "user_shops" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "user_profile" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | output { 22 | name: "user_group" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | output { 28 | name: "user_gender" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | output { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | output { 40 | name: "user_consumption_2" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | output { 46 | name: "user_is_occupied" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | output { 52 | name: "user_geography" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | output { 58 | name: "user_intentions" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | output { 64 | name: "user_brands" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | output { 70 | name: "user_categories" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | output { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | input { 5 | name: "user_age" 6 | data_type: TYPE_INT32 7 | dims: -1 8 | dims: 1 9 | } 10 | input { 11 | name: "user_brands" 12 | data_type: TYPE_INT32 13 | dims: -1 14 | dims: 1 15 | } 16 | input { 17 | name: "user_categories" 18 | data_type: TYPE_INT32 19 | dims: -1 20 | dims: 1 21 | } 22 | input { 23 | name: "user_consumption_2" 24 | data_type: TYPE_INT32 25 | dims: -1 26 | dims: 1 27 | } 28 | input { 29 | name: "user_gender" 30 | data_type: TYPE_INT32 31 | dims: -1 32 | dims: 1 33 | } 34 | input { 35 | name: "user_geography" 36 | data_type: TYPE_INT32 37 | dims: -1 38 | dims: 1 39 | } 40 | input { 41 | name: "user_group" 42 | data_type: TYPE_INT32 43 | dims: -1 44 | dims: 1 45 | } 46 | input { 47 | name: "user_id" 48 | data_type: TYPE_INT32 49 | dims: -1 50 | dims: 1 51 | } 52 | input { 53 | name: "user_intentions" 54 | data_type: TYPE_INT32 55 | dims: -1 56 | dims: 1 57 | } 58 | input { 59 | name: "user_is_occupied" 60 | data_type: TYPE_INT32 61 | dims: -1 62 | dims: 1 63 | } 64 | input { 65 | name: "user_profile" 66 | data_type: TYPE_INT32 67 | dims: -1 68 | dims: 1 69 | } 70 | input { 71 | name: "user_shops" 72 | data_type: TYPE_INT32 73 | dims: -1 74 | dims: 1 75 | } 76 | output { 77 | name: "output_1" 78 | data_type: TYPE_FP32 79 | dims: -1 80 | dims: 64 81 | } 82 | parameters { 83 | key: "TF_GRAPH_TAG" 84 | value { 85 | string_value: "serve" 86 | } 87 | } 88 | parameters { 89 | key: "TF_SIGNATURE_DEF" 90 | value { 91 | string_value: "serving_default" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | input { 4 | name: "output_1" 5 | data_type: TYPE_FP32 6 | dims: -1 7 | dims: 64 8 | } 9 | output { 10 | name: "candidate_ids" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | parameters { 16 | key: "vector_db_config" 17 | value { 18 | string_value: "{\"index_name\": \"candidate_index\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 19 | } 20 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/3-query-item-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "3-query-item-features" 2 | backend: "python" 3 | input { 4 | name: "candidate_ids" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | output { 10 | name: "item_category" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "item_shop" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | output { 22 | name: "item_brand" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | output { 28 | name: "item_id_raw" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | output { 34 | name: "item_id" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | input { 4 | name: "item_category" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_shop" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_brand" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_id" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_id" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_shops" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_profile" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_group" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_age" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_consumption_2" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_is_occupied" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_geography" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_brands" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_categories" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | output { 106 | name: "item_category" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "item_shop" 113 | data_type: TYPE_INT32 114 | dims: -1 115 | dims: 1 116 | } 117 | output { 118 | name: "item_brand" 119 | data_type: TYPE_INT32 120 | dims: -1 121 | dims: 1 122 | } 123 | output { 124 | name: "item_id_raw" 125 | data_type: TYPE_INT32 126 | dims: -1 127 | dims: 1 128 | } 129 | output { 130 | name: "item_id" 131 | data_type: TYPE_INT32 132 | dims: -1 133 | dims: 1 134 | } 135 | output { 136 | name: "user_id" 137 | data_type: TYPE_INT32 138 | dims: -1 139 | dims: 1 140 | } 141 | output { 142 | name: "user_shops" 143 | data_type: TYPE_INT32 144 | dims: -1 145 | dims: 1 146 | } 147 | output { 148 | name: "user_profile" 149 | data_type: TYPE_INT32 150 | dims: -1 151 | dims: 1 152 | } 153 | output { 154 | name: "user_group" 155 | data_type: TYPE_INT32 156 | dims: -1 157 | dims: 1 158 | } 159 | output { 160 | name: "user_gender" 161 | data_type: TYPE_INT32 162 | dims: -1 163 | dims: 1 164 | } 165 | output { 166 | name: "user_age" 167 | data_type: TYPE_INT32 168 | dims: -1 169 | dims: 1 170 | } 171 | output { 172 | name: "user_consumption_2" 173 | data_type: TYPE_INT32 174 | dims: -1 175 | dims: 1 176 | } 177 | output { 178 | name: "user_is_occupied" 179 | data_type: TYPE_INT32 180 | dims: -1 181 | dims: 1 182 | } 183 | output { 184 | name: "user_geography" 185 | data_type: TYPE_INT32 186 | dims: -1 187 | dims: 1 188 | } 189 | output { 190 | name: "user_intentions" 191 | data_type: TYPE_INT32 192 | dims: -1 193 | dims: 1 194 | } 195 | output { 196 | name: "user_brands" 197 | data_type: TYPE_INT32 198 | dims: -1 199 | dims: 1 200 | } 201 | output { 202 | name: "user_categories" 203 | data_type: TYPE_INT32 204 | dims: -1 205 | dims: 1 206 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | input { 4 | name: "item_brand" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_category" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_id" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_shop" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_brands" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_categories" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_consumption_2" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_geography" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_group" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_id_raw" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_is_occupied" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_profile" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | input { 106 | name: "user_shops" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "click/binary_classification_task" 113 | data_type: TYPE_FP32 114 | dims: -1 115 | dims: 1 116 | } 117 | parameters { 118 | key: "TF_GRAPH_TAG" 119 | value { 120 | string_value: "serve" 121 | } 122 | } 123 | parameters { 124 | key: "TF_SIGNATURE_DEF" 125 | value { 126 | string_value: "serving_default" 127 | } 128 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | input { 4 | name: "item_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "click/binary_classification_task" 11 | data_type: TYPE_FP32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "ordered_ids" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: -1 20 | } 21 | parameters { 22 | key: "softmax_config" 23 | value { 24 | string_value: "{\"topk\": \"16\"}" 25 | } 26 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/models/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/models/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/requirements.txt: -------------------------------------------------------------------------------- 1 | feast[redis]==0.28.0 2 | seedir==0.4.0 3 | jupyterlab==3.5.2 4 | redis==4.4.2 5 | protobuf==3.20 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | input { 4 | name: "user_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | output { 10 | name: "user_shops" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "user_profile" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | output { 22 | name: "user_group" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | output { 28 | name: "user_gender" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | output { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | output { 40 | name: "user_consumption_2" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | output { 46 | name: "user_is_occupied" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | output { 52 | name: "user_geography" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | output { 58 | name: "user_intentions" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | output { 64 | name: "user_brands" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | output { 70 | name: "user_categories" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | output { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | input { 5 | name: "user_age" 6 | data_type: TYPE_INT32 7 | dims: -1 8 | dims: 1 9 | } 10 | input { 11 | name: "user_brands" 12 | data_type: TYPE_INT32 13 | dims: -1 14 | dims: 1 15 | } 16 | input { 17 | name: "user_categories" 18 | data_type: TYPE_INT32 19 | dims: -1 20 | dims: 1 21 | } 22 | input { 23 | name: "user_consumption_2" 24 | data_type: TYPE_INT32 25 | dims: -1 26 | dims: 1 27 | } 28 | input { 29 | name: "user_gender" 30 | data_type: TYPE_INT32 31 | dims: -1 32 | dims: 1 33 | } 34 | input { 35 | name: "user_geography" 36 | data_type: TYPE_INT32 37 | dims: -1 38 | dims: 1 39 | } 40 | input { 41 | name: "user_group" 42 | data_type: TYPE_INT32 43 | dims: -1 44 | dims: 1 45 | } 46 | input { 47 | name: "user_id" 48 | data_type: TYPE_INT32 49 | dims: -1 50 | dims: 1 51 | } 52 | input { 53 | name: "user_intentions" 54 | data_type: TYPE_INT32 55 | dims: -1 56 | dims: 1 57 | } 58 | input { 59 | name: "user_is_occupied" 60 | data_type: TYPE_INT32 61 | dims: -1 62 | dims: 1 63 | } 64 | input { 65 | name: "user_profile" 66 | data_type: TYPE_INT32 67 | dims: -1 68 | dims: 1 69 | } 70 | input { 71 | name: "user_shops" 72 | data_type: TYPE_INT32 73 | dims: -1 74 | dims: 1 75 | } 76 | output { 77 | name: "output_1" 78 | data_type: TYPE_FP32 79 | dims: -1 80 | dims: 64 81 | } 82 | parameters { 83 | key: "TF_GRAPH_TAG" 84 | value { 85 | string_value: "serve" 86 | } 87 | } 88 | parameters { 89 | key: "TF_SIGNATURE_DEF" 90 | value { 91 | string_value: "serving_default" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | input { 4 | name: "output_1" 5 | data_type: TYPE_FP32 6 | dims: -1 7 | dims: 64 8 | } 9 | output { 10 | name: "candidate_ids" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | parameters { 16 | key: "vector_db_config" 17 | value { 18 | string_value: "{\"index_name\": \"candidate_index\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 19 | } 20 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/3-query-item-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "3-query-item-features" 2 | backend: "python" 3 | input { 4 | name: "candidate_ids" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | output { 10 | name: "item_category" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "item_shop" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | output { 22 | name: "item_brand" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | output { 28 | name: "item_id_raw" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | output { 34 | name: "item_id" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | input { 4 | name: "item_category" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_shop" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_brand" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_id" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_id" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_shops" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_profile" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_group" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_age" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_consumption_2" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_is_occupied" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_geography" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_brands" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_categories" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | output { 106 | name: "item_category" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "item_shop" 113 | data_type: TYPE_INT32 114 | dims: -1 115 | dims: 1 116 | } 117 | output { 118 | name: "item_brand" 119 | data_type: TYPE_INT32 120 | dims: -1 121 | dims: 1 122 | } 123 | output { 124 | name: "item_id_raw" 125 | data_type: TYPE_INT32 126 | dims: -1 127 | dims: 1 128 | } 129 | output { 130 | name: "item_id" 131 | data_type: TYPE_INT32 132 | dims: -1 133 | dims: 1 134 | } 135 | output { 136 | name: "user_id" 137 | data_type: TYPE_INT32 138 | dims: -1 139 | dims: 1 140 | } 141 | output { 142 | name: "user_shops" 143 | data_type: TYPE_INT32 144 | dims: -1 145 | dims: 1 146 | } 147 | output { 148 | name: "user_profile" 149 | data_type: TYPE_INT32 150 | dims: -1 151 | dims: 1 152 | } 153 | output { 154 | name: "user_group" 155 | data_type: TYPE_INT32 156 | dims: -1 157 | dims: 1 158 | } 159 | output { 160 | name: "user_gender" 161 | data_type: TYPE_INT32 162 | dims: -1 163 | dims: 1 164 | } 165 | output { 166 | name: "user_age" 167 | data_type: TYPE_INT32 168 | dims: -1 169 | dims: 1 170 | } 171 | output { 172 | name: "user_consumption_2" 173 | data_type: TYPE_INT32 174 | dims: -1 175 | dims: 1 176 | } 177 | output { 178 | name: "user_is_occupied" 179 | data_type: TYPE_INT32 180 | dims: -1 181 | dims: 1 182 | } 183 | output { 184 | name: "user_geography" 185 | data_type: TYPE_INT32 186 | dims: -1 187 | dims: 1 188 | } 189 | output { 190 | name: "user_intentions" 191 | data_type: TYPE_INT32 192 | dims: -1 193 | dims: 1 194 | } 195 | output { 196 | name: "user_brands" 197 | data_type: TYPE_INT32 198 | dims: -1 199 | dims: 1 200 | } 201 | output { 202 | name: "user_categories" 203 | data_type: TYPE_INT32 204 | dims: -1 205 | dims: 1 206 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | input { 4 | name: "item_brand" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_category" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_id" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_shop" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_brands" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_categories" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_consumption_2" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_geography" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_group" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_id_raw" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_is_occupied" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_profile" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | input { 106 | name: "user_shops" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "click/binary_classification_task" 113 | data_type: TYPE_FP32 114 | dims: -1 115 | dims: 1 116 | } 117 | parameters { 118 | key: "TF_GRAPH_TAG" 119 | value { 120 | string_value: "serve" 121 | } 122 | } 123 | parameters { 124 | key: "TF_SIGNATURE_DEF" 125 | value { 126 | string_value: "serving_default" 127 | } 128 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | input { 4 | name: "item_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "click/binary_classification_task" 11 | data_type: TYPE_FP32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "ordered_ids" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: -1 20 | } 21 | parameters { 22 | key: "softmax_config" 23 | value { 24 | string_value: "{\"topk\": \"16\"}" 25 | } 26 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/01-baseline/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/01-baseline/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | input { 4 | name: "user_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | output { 10 | name: "user_shops" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "user_profile" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | output { 22 | name: "user_group" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | output { 28 | name: "user_gender" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | output { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | output { 40 | name: "user_consumption_2" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | output { 46 | name: "user_is_occupied" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | output { 52 | name: "user_geography" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | output { 58 | name: "user_intentions" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | output { 64 | name: "user_brands" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | output { 70 | name: "user_categories" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | output { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | input { 5 | name: "user_age" 6 | data_type: TYPE_INT32 7 | dims: -1 8 | dims: 1 9 | } 10 | input { 11 | name: "user_brands" 12 | data_type: TYPE_INT32 13 | dims: -1 14 | dims: 1 15 | } 16 | input { 17 | name: "user_categories" 18 | data_type: TYPE_INT32 19 | dims: -1 20 | dims: 1 21 | } 22 | input { 23 | name: "user_consumption_2" 24 | data_type: TYPE_INT32 25 | dims: -1 26 | dims: 1 27 | } 28 | input { 29 | name: "user_gender" 30 | data_type: TYPE_INT32 31 | dims: -1 32 | dims: 1 33 | } 34 | input { 35 | name: "user_geography" 36 | data_type: TYPE_INT32 37 | dims: -1 38 | dims: 1 39 | } 40 | input { 41 | name: "user_group" 42 | data_type: TYPE_INT32 43 | dims: -1 44 | dims: 1 45 | } 46 | input { 47 | name: "user_id" 48 | data_type: TYPE_INT32 49 | dims: -1 50 | dims: 1 51 | } 52 | input { 53 | name: "user_intentions" 54 | data_type: TYPE_INT32 55 | dims: -1 56 | dims: 1 57 | } 58 | input { 59 | name: "user_is_occupied" 60 | data_type: TYPE_INT32 61 | dims: -1 62 | dims: 1 63 | } 64 | input { 65 | name: "user_profile" 66 | data_type: TYPE_INT32 67 | dims: -1 68 | dims: 1 69 | } 70 | input { 71 | name: "user_shops" 72 | data_type: TYPE_INT32 73 | dims: -1 74 | dims: 1 75 | } 76 | output { 77 | name: "output_1" 78 | data_type: TYPE_FP32 79 | dims: -1 80 | dims: 64 81 | } 82 | parameters { 83 | key: "TF_GRAPH_TAG" 84 | value { 85 | string_value: "serve" 86 | } 87 | } 88 | parameters { 89 | key: "TF_SIGNATURE_DEF" 90 | value { 91 | string_value: "serving_default" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU, count: 4 }] 5 | input { 6 | name: "output_1" 7 | data_type: TYPE_FP32 8 | dims: -1 9 | dims: 64 10 | } 11 | output { 12 | name: "item_category" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "item_shop" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | output { 24 | name: "item_brand" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | output { 30 | name: "item_id_raw" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | output { 36 | name: "item_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | parameters { 42 | key: "vector_db_config" 43 | value { 44 | string_value: "{\"index_name\": \"candidate_index\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 45 | } 46 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | input { 4 | name: "item_category" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_shop" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_brand" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_id" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_id" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_shops" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_profile" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_group" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_age" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_consumption_2" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_is_occupied" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_geography" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_brands" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_categories" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | output { 106 | name: "item_category" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "item_shop" 113 | data_type: TYPE_INT32 114 | dims: -1 115 | dims: 1 116 | } 117 | output { 118 | name: "item_brand" 119 | data_type: TYPE_INT32 120 | dims: -1 121 | dims: 1 122 | } 123 | output { 124 | name: "item_id_raw" 125 | data_type: TYPE_INT32 126 | dims: -1 127 | dims: 1 128 | } 129 | output { 130 | name: "item_id" 131 | data_type: TYPE_INT32 132 | dims: -1 133 | dims: 1 134 | } 135 | output { 136 | name: "user_id" 137 | data_type: TYPE_INT32 138 | dims: -1 139 | dims: 1 140 | } 141 | output { 142 | name: "user_shops" 143 | data_type: TYPE_INT32 144 | dims: -1 145 | dims: 1 146 | } 147 | output { 148 | name: "user_profile" 149 | data_type: TYPE_INT32 150 | dims: -1 151 | dims: 1 152 | } 153 | output { 154 | name: "user_group" 155 | data_type: TYPE_INT32 156 | dims: -1 157 | dims: 1 158 | } 159 | output { 160 | name: "user_gender" 161 | data_type: TYPE_INT32 162 | dims: -1 163 | dims: 1 164 | } 165 | output { 166 | name: "user_age" 167 | data_type: TYPE_INT32 168 | dims: -1 169 | dims: 1 170 | } 171 | output { 172 | name: "user_consumption_2" 173 | data_type: TYPE_INT32 174 | dims: -1 175 | dims: 1 176 | } 177 | output { 178 | name: "user_is_occupied" 179 | data_type: TYPE_INT32 180 | dims: -1 181 | dims: 1 182 | } 183 | output { 184 | name: "user_geography" 185 | data_type: TYPE_INT32 186 | dims: -1 187 | dims: 1 188 | } 189 | output { 190 | name: "user_intentions" 191 | data_type: TYPE_INT32 192 | dims: -1 193 | dims: 1 194 | } 195 | output { 196 | name: "user_brands" 197 | data_type: TYPE_INT32 198 | dims: -1 199 | dims: 1 200 | } 201 | output { 202 | name: "user_categories" 203 | data_type: TYPE_INT32 204 | dims: -1 205 | dims: 1 206 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | input { 4 | name: "item_brand" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "item_category" 11 | data_type: TYPE_INT32 12 | dims: -1 13 | dims: 1 14 | } 15 | input { 16 | name: "item_id" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: 1 20 | } 21 | input { 22 | name: "item_id_raw" 23 | data_type: TYPE_INT32 24 | dims: -1 25 | dims: 1 26 | } 27 | input { 28 | name: "item_shop" 29 | data_type: TYPE_INT32 30 | dims: -1 31 | dims: 1 32 | } 33 | input { 34 | name: "user_age" 35 | data_type: TYPE_INT32 36 | dims: -1 37 | dims: 1 38 | } 39 | input { 40 | name: "user_brands" 41 | data_type: TYPE_INT32 42 | dims: -1 43 | dims: 1 44 | } 45 | input { 46 | name: "user_categories" 47 | data_type: TYPE_INT32 48 | dims: -1 49 | dims: 1 50 | } 51 | input { 52 | name: "user_consumption_2" 53 | data_type: TYPE_INT32 54 | dims: -1 55 | dims: 1 56 | } 57 | input { 58 | name: "user_gender" 59 | data_type: TYPE_INT32 60 | dims: -1 61 | dims: 1 62 | } 63 | input { 64 | name: "user_geography" 65 | data_type: TYPE_INT32 66 | dims: -1 67 | dims: 1 68 | } 69 | input { 70 | name: "user_group" 71 | data_type: TYPE_INT32 72 | dims: -1 73 | dims: 1 74 | } 75 | input { 76 | name: "user_id" 77 | data_type: TYPE_INT32 78 | dims: -1 79 | dims: 1 80 | } 81 | input { 82 | name: "user_id_raw" 83 | data_type: TYPE_INT32 84 | dims: -1 85 | dims: 1 86 | } 87 | input { 88 | name: "user_intentions" 89 | data_type: TYPE_INT32 90 | dims: -1 91 | dims: 1 92 | } 93 | input { 94 | name: "user_is_occupied" 95 | data_type: TYPE_INT32 96 | dims: -1 97 | dims: 1 98 | } 99 | input { 100 | name: "user_profile" 101 | data_type: TYPE_INT32 102 | dims: -1 103 | dims: 1 104 | } 105 | input { 106 | name: "user_shops" 107 | data_type: TYPE_INT32 108 | dims: -1 109 | dims: 1 110 | } 111 | output { 112 | name: "click/binary_classification_task" 113 | data_type: TYPE_FP32 114 | dims: -1 115 | dims: 1 116 | } 117 | parameters { 118 | key: "TF_GRAPH_TAG" 119 | value { 120 | string_value: "serve" 121 | } 122 | } 123 | parameters { 124 | key: "TF_SIGNATURE_DEF" 125 | value { 126 | string_value: "serving_default" 127 | } 128 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | input { 4 | name: "item_id_raw" 5 | data_type: TYPE_INT32 6 | dims: -1 7 | dims: 1 8 | } 9 | input { 10 | name: "click/binary_classification_task" 11 | data_type: TYPE_FP32 12 | dims: -1 13 | dims: 1 14 | } 15 | output { 16 | name: "ordered_ids" 17 | data_type: TYPE_INT32 18 | dims: -1 19 | dims: -1 20 | } 21 | parameters { 22 | key: "softmax_config" 23 | value { 24 | string_value: "{\"topk\": \"16\"}" 25 | } 26 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/02-stage-consolidation/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/02-stage-consolidation/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/0-query-user-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import json 4 | import logging 5 | import sys 6 | 7 | import numpy as np 8 | from redis import client 9 | import triton_python_backend_utils as pb_utils 10 | 11 | from merlin.dag import DictArray 12 | 13 | 14 | class TritonPythonModel: 15 | """Your Python model must use the same class name. Every Python model 16 | that is created must have "TritonPythonModel" as the class name. 17 | """ 18 | 19 | def initialize(self, args): 20 | """`initialize` is called only once when the model is being loaded. 21 | Implementing `initialize` function is optional. This function allows 22 | the model to intialize any state associated with this model. 23 | Parameters 24 | ---------- 25 | args : dict 26 | Both keys and values are strings. The dictionary keys and values are: 27 | * model_config: A JSON string containing the model configuration 28 | * model_instance_kind: A string containing model instance kind 29 | * model_instance_device_id: A string containing model instance device ID 30 | * model_repository: Model repository path 31 | * model_version: Model version 32 | * model_name: Model name 33 | """ 34 | 35 | # Parse model_config. JSON string is not parsed here 36 | logging.info("Parsing model config") 37 | self.model_config = model_config = json.loads(args['model_config']) 38 | self.output_names = [output["name"] for output in self.model_config["output"]] 39 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 40 | self.entity_column = "user_id_raw" 41 | 42 | host, port = os.environ["FEATURE_STORE_ADDRESS"].split(":") 43 | self._client = client.Redis(host=host, port=port, decode_responses=True) 44 | 45 | def execute(self, requests): 46 | """`execute` MUST be implemented in every Python model. `execute` 47 | function receives a list of pb_utils.InferenceRequest as the only 48 | argument. This function is called when an inference request is made 49 | for this model. Depending on the batching configuration (e.g. Dynamic 50 | Batching) used, `requests` may contain multiple requests. Every 51 | Python model, must create one pb_utils.InferenceResponse for every 52 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 53 | set the error argument when creating a pb_utils.InferenceResponse 54 | Parameters 55 | ---------- 56 | requests : list 57 | A list of pb_utils.InferenceRequest 58 | Returns 59 | ------- 60 | list 61 | A list of pb_utils.InferenceResponse. The length of this list must 62 | be the same as `requests` 63 | """ 64 | responses = [] 65 | 66 | # Every Python backend must iterate over everyone of the requests 67 | # and create a pb_utils.InferenceResponse for each of them. 68 | for request in requests: 69 | # Get Inputs 70 | input_tensor = { 71 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 72 | for name in self.input_names 73 | } 74 | input_df = DictArray(input_tensor) 75 | entity_ids = input_df[self.entity_column] 76 | if len(entity_ids) < 1: 77 | raise ValueError( 78 | "must provide entity_id" 79 | ) 80 | 81 | user_features = self._client.hgetall(f"user:{str(entity_ids[0][0])}") 82 | # Loop through features to create separate output tensors 83 | output_tensors = [] 84 | for output_name in self.output_names: 85 | feature_value = int(user_features[output_name]) 86 | feature_array = np.array(feature_value).T.astype(np.int32).reshape(-1, 1) 87 | out_tensor = pb_utils.Tensor(output_name, feature_array) 88 | output_tensors.append(out_tensor) 89 | 90 | # Create InferenceResponse and append to responses 91 | inference_response = pb_utils.InferenceResponse(output_tensors) 92 | responses.append(inference_response) 93 | 94 | # Return a list of pb_utils.InferenceResponse 95 | return responses 96 | 97 | def finalize(self): 98 | """`finalize` is called only once when the model is being unloaded. 99 | Implementing `finalize` function is OPTIONAL. This function allows 100 | the model to perform any necessary clean ups before exit. 101 | """ 102 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU, count: 2 }] 5 | input { 6 | name: "user_id_raw" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | output { 12 | name: "user_shops" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "user_profile" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | output { 24 | name: "user_group" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | output { 30 | name: "user_gender" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | output { 36 | name: "user_age" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | output { 42 | name: "user_consumption_2" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | output { 48 | name: "user_is_occupied" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | output { 54 | name: "user_geography" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | output { 60 | name: "user_intentions" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | output { 66 | name: "user_brands" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | output { 72 | name: "user_categories" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | output { 78 | name: "user_id" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | dynamic_batching {} 5 | input { 6 | name: "user_age" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "user_brands" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "user_categories" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "user_consumption_2" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "user_gender" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_geography" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_group" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_id" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_intentions" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_is_occupied" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_profile" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_shops" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | output { 78 | name: "output_1" 79 | data_type: TYPE_FP32 80 | dims: -1 81 | dims: 64 82 | } 83 | parameters { 84 | key: "TF_GRAPH_TAG" 85 | value { 86 | string_value: "serve" 87 | } 88 | } 89 | parameters { 90 | key: "TF_SIGNATURE_DEF" 91 | value { 92 | string_value: "serving_default" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU, count: 4 }] 5 | input { 6 | name: "output_1" 7 | data_type: TYPE_FP32 8 | dims: -1 9 | dims: 64 10 | } 11 | output { 12 | name: "item_category" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "item_shop" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | output { 24 | name: "item_brand" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | output { 30 | name: "item_id_raw" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | output { 36 | name: "item_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | parameters { 42 | key: "vector_db_config" 43 | value { 44 | string_value: "{\"index_name\": \"candidate_index\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 45 | } 46 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_category" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "item_shop" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "item_brand" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "item_id_raw" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "item_id" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_shops" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_profile" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_group" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_gender" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_age" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_consumption_2" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | input { 78 | name: "user_is_occupied" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } 83 | input { 84 | name: "user_geography" 85 | data_type: TYPE_INT32 86 | dims: -1 87 | dims: 1 88 | } 89 | input { 90 | name: "user_intentions" 91 | data_type: TYPE_INT32 92 | dims: -1 93 | dims: 1 94 | } 95 | input { 96 | name: "user_brands" 97 | data_type: TYPE_INT32 98 | dims: -1 99 | dims: 1 100 | } 101 | input { 102 | name: "user_categories" 103 | data_type: TYPE_INT32 104 | dims: -1 105 | dims: 1 106 | } 107 | output { 108 | name: "item_category" 109 | data_type: TYPE_INT32 110 | dims: -1 111 | dims: 1 112 | } 113 | output { 114 | name: "item_shop" 115 | data_type: TYPE_INT32 116 | dims: -1 117 | dims: 1 118 | } 119 | output { 120 | name: "item_brand" 121 | data_type: TYPE_INT32 122 | dims: -1 123 | dims: 1 124 | } 125 | output { 126 | name: "item_id_raw" 127 | data_type: TYPE_INT32 128 | dims: -1 129 | dims: 1 130 | } 131 | output { 132 | name: "item_id" 133 | data_type: TYPE_INT32 134 | dims: -1 135 | dims: 1 136 | } 137 | output { 138 | name: "user_id" 139 | data_type: TYPE_INT32 140 | dims: -1 141 | dims: 1 142 | } 143 | output { 144 | name: "user_shops" 145 | data_type: TYPE_INT32 146 | dims: -1 147 | dims: 1 148 | } 149 | output { 150 | name: "user_profile" 151 | data_type: TYPE_INT32 152 | dims: -1 153 | dims: 1 154 | } 155 | output { 156 | name: "user_group" 157 | data_type: TYPE_INT32 158 | dims: -1 159 | dims: 1 160 | } 161 | output { 162 | name: "user_gender" 163 | data_type: TYPE_INT32 164 | dims: -1 165 | dims: 1 166 | } 167 | output { 168 | name: "user_age" 169 | data_type: TYPE_INT32 170 | dims: -1 171 | dims: 1 172 | } 173 | output { 174 | name: "user_consumption_2" 175 | data_type: TYPE_INT32 176 | dims: -1 177 | dims: 1 178 | } 179 | output { 180 | name: "user_is_occupied" 181 | data_type: TYPE_INT32 182 | dims: -1 183 | dims: 1 184 | } 185 | output { 186 | name: "user_geography" 187 | data_type: TYPE_INT32 188 | dims: -1 189 | dims: 1 190 | } 191 | output { 192 | name: "user_intentions" 193 | data_type: TYPE_INT32 194 | dims: -1 195 | dims: 1 196 | } 197 | output { 198 | name: "user_brands" 199 | data_type: TYPE_INT32 200 | dims: -1 201 | dims: 1 202 | } 203 | output { 204 | name: "user_categories" 205 | data_type: TYPE_INT32 206 | dims: -1 207 | dims: 1 208 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | dynamic_batching {} 4 | input { 5 | name: "item_brand" 6 | data_type: TYPE_INT32 7 | dims: -1 8 | dims: 1 9 | } 10 | input { 11 | name: "item_category" 12 | data_type: TYPE_INT32 13 | dims: -1 14 | dims: 1 15 | } 16 | input { 17 | name: "item_id" 18 | data_type: TYPE_INT32 19 | dims: -1 20 | dims: 1 21 | } 22 | input { 23 | name: "item_id_raw" 24 | data_type: TYPE_INT32 25 | dims: -1 26 | dims: 1 27 | } 28 | input { 29 | name: "item_shop" 30 | data_type: TYPE_INT32 31 | dims: -1 32 | dims: 1 33 | } 34 | input { 35 | name: "user_age" 36 | data_type: TYPE_INT32 37 | dims: -1 38 | dims: 1 39 | } 40 | input { 41 | name: "user_brands" 42 | data_type: TYPE_INT32 43 | dims: -1 44 | dims: 1 45 | } 46 | input { 47 | name: "user_categories" 48 | data_type: TYPE_INT32 49 | dims: -1 50 | dims: 1 51 | } 52 | input { 53 | name: "user_consumption_2" 54 | data_type: TYPE_INT32 55 | dims: -1 56 | dims: 1 57 | } 58 | input { 59 | name: "user_gender" 60 | data_type: TYPE_INT32 61 | dims: -1 62 | dims: 1 63 | } 64 | input { 65 | name: "user_geography" 66 | data_type: TYPE_INT32 67 | dims: -1 68 | dims: 1 69 | } 70 | input { 71 | name: "user_group" 72 | data_type: TYPE_INT32 73 | dims: -1 74 | dims: 1 75 | } 76 | input { 77 | name: "user_id" 78 | data_type: TYPE_INT32 79 | dims: -1 80 | dims: 1 81 | } 82 | input { 83 | name: "user_id_raw" 84 | data_type: TYPE_INT32 85 | dims: -1 86 | dims: 1 87 | } 88 | input { 89 | name: "user_intentions" 90 | data_type: TYPE_INT32 91 | dims: -1 92 | dims: 1 93 | } 94 | input { 95 | name: "user_is_occupied" 96 | data_type: TYPE_INT32 97 | dims: -1 98 | dims: 1 99 | } 100 | input { 101 | name: "user_profile" 102 | data_type: TYPE_INT32 103 | dims: -1 104 | dims: 1 105 | } 106 | input { 107 | name: "user_shops" 108 | data_type: TYPE_INT32 109 | dims: -1 110 | dims: 1 111 | } 112 | output { 113 | name: "click/binary_classification_task" 114 | data_type: TYPE_FP32 115 | dims: -1 116 | dims: 1 117 | } 118 | parameters { 119 | key: "TF_GRAPH_TAG" 120 | value { 121 | string_value: "serve" 122 | } 123 | } 124 | parameters { 125 | key: "TF_SIGNATURE_DEF" 126 | value { 127 | string_value: "serving_default" 128 | } 129 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_id_raw" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "click/binary_classification_task" 13 | data_type: TYPE_FP32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "ordered_ids" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: -1 22 | } 23 | parameters { 24 | key: "softmax_config" 25 | value { 26 | string_value: "{\"topk\": \"16\"}" 27 | } 28 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/03-remove-feast-sdk/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/03-remove-feast-sdk/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/0-query-user-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | from redis import client 8 | import triton_python_backend_utils as pb_utils 9 | 10 | from merlin.dag import DictArray 11 | 12 | 13 | class TritonPythonModel: 14 | """Your Python model must use the same class name. Every Python model 15 | that is created must have "TritonPythonModel" as the class name. 16 | """ 17 | 18 | def initialize(self, args): 19 | """`initialize` is called only once when the model is being loaded. 20 | Implementing `initialize` function is optional. This function allows 21 | the model to intialize any state associated with this model. 22 | Parameters 23 | ---------- 24 | args : dict 25 | Both keys and values are strings. The dictionary keys and values are: 26 | * model_config: A JSON string containing the model configuration 27 | * model_instance_kind: A string containing model instance kind 28 | * model_instance_device_id: A string containing model instance device ID 29 | * model_repository: Model repository path 30 | * model_version: Model version 31 | * model_name: Model name 32 | """ 33 | 34 | # Parse model_config. JSON string is not parsed here 35 | logging.info("Parsing model config") 36 | self.model_config = model_config = json.loads(args['model_config']) 37 | self.output_names = [output["name"] for output in self.model_config["output"]] 38 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 39 | self.entity_column = "user_id_raw" 40 | 41 | host = "52.9.82.78" 42 | port = 18376 43 | self._client = client.Redis(host=host, port=port, decode_responses=True) 44 | 45 | def execute(self, requests): 46 | """`execute` MUST be implemented in every Python model. `execute` 47 | function receives a list of pb_utils.InferenceRequest as the only 48 | argument. This function is called when an inference request is made 49 | for this model. Depending on the batching configuration (e.g. Dynamic 50 | Batching) used, `requests` may contain multiple requests. Every 51 | Python model, must create one pb_utils.InferenceResponse for every 52 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 53 | set the error argument when creating a pb_utils.InferenceResponse 54 | Parameters 55 | ---------- 56 | requests : list 57 | A list of pb_utils.InferenceRequest 58 | Returns 59 | ------- 60 | list 61 | A list of pb_utils.InferenceResponse. The length of this list must 62 | be the same as `requests` 63 | """ 64 | responses = [] 65 | 66 | # Every Python backend must iterate over everyone of the requests 67 | # and create a pb_utils.InferenceResponse for each of them. 68 | for request in requests: 69 | # Get Inputs 70 | input_tensor = { 71 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 72 | for name in self.input_names 73 | } 74 | input_df = DictArray(input_tensor) 75 | entity_ids = input_df[self.entity_column] 76 | if len(entity_ids) < 1: 77 | raise ValueError( 78 | "must provide entity_id" 79 | ) 80 | 81 | user_features = self._client.hgetall(f"user:{str(entity_ids[0][0])}") 82 | # Loop through features to create separate output tensors 83 | output_tensors = [] 84 | for output_name in self.output_names: 85 | feature_value = int(user_features[output_name]) 86 | feature_array = np.array(feature_value).T.astype(np.int32).reshape(-1, 1) 87 | out_tensor = pb_utils.Tensor(output_name, feature_array) 88 | output_tensors.append(out_tensor) 89 | 90 | # Create InferenceResponse and append to responses 91 | inference_response = pb_utils.InferenceResponse(output_tensors) 92 | responses.append(inference_response) 93 | 94 | # Return a list of pb_utils.InferenceResponse 95 | return responses 96 | 97 | def finalize(self): 98 | """`finalize` is called only once when the model is being unloaded. 99 | Implementing `finalize` function is OPTIONAL. This function allows 100 | the model to perform any necessary clean ups before exit. 101 | """ 102 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU, count: 2 }] 5 | input { 6 | name: "user_id_raw" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | output { 12 | name: "user_shops" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "user_profile" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | output { 24 | name: "user_group" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | output { 30 | name: "user_gender" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | output { 36 | name: "user_age" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | output { 42 | name: "user_consumption_2" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | output { 48 | name: "user_is_occupied" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | output { 54 | name: "user_geography" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | output { 60 | name: "user_intentions" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | output { 66 | name: "user_brands" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | output { 72 | name: "user_categories" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | output { 78 | name: "user_id" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | dynamic_batching {} 5 | input { 6 | name: "user_age" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "user_brands" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "user_categories" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "user_consumption_2" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "user_gender" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_geography" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_group" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_id" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_intentions" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_is_occupied" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_profile" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_shops" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | output { 78 | name: "output_1" 79 | data_type: TYPE_FP32 80 | dims: -1 81 | dims: 64 82 | } 83 | parameters { 84 | key: "TF_GRAPH_TAG" 85 | value { 86 | string_value: "serve" 87 | } 88 | } 89 | parameters { 90 | key: "TF_SIGNATURE_DEF" 91 | value { 92 | string_value: "serving_default" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU, count: 4 }] 5 | input { 6 | name: "output_1" 7 | data_type: TYPE_FP32 8 | dims: -1 9 | dims: 64 10 | } 11 | output { 12 | name: "item_category" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "item_shop" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | output { 24 | name: "item_brand" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | output { 30 | name: "item_id_raw" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | output { 36 | name: "item_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | parameters { 42 | key: "vector_db_config" 43 | value { 44 | string_value: "{\"index_name\": \"candidate_index_2\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 45 | } 46 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_category" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "item_shop" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "item_brand" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "item_id_raw" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "item_id" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_shops" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_profile" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_group" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_gender" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_age" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_consumption_2" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | input { 78 | name: "user_is_occupied" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } 83 | input { 84 | name: "user_geography" 85 | data_type: TYPE_INT32 86 | dims: -1 87 | dims: 1 88 | } 89 | input { 90 | name: "user_intentions" 91 | data_type: TYPE_INT32 92 | dims: -1 93 | dims: 1 94 | } 95 | input { 96 | name: "user_brands" 97 | data_type: TYPE_INT32 98 | dims: -1 99 | dims: 1 100 | } 101 | input { 102 | name: "user_categories" 103 | data_type: TYPE_INT32 104 | dims: -1 105 | dims: 1 106 | } 107 | output { 108 | name: "item_category" 109 | data_type: TYPE_INT32 110 | dims: -1 111 | dims: 1 112 | } 113 | output { 114 | name: "item_shop" 115 | data_type: TYPE_INT32 116 | dims: -1 117 | dims: 1 118 | } 119 | output { 120 | name: "item_brand" 121 | data_type: TYPE_INT32 122 | dims: -1 123 | dims: 1 124 | } 125 | output { 126 | name: "item_id_raw" 127 | data_type: TYPE_INT32 128 | dims: -1 129 | dims: 1 130 | } 131 | output { 132 | name: "item_id" 133 | data_type: TYPE_INT32 134 | dims: -1 135 | dims: 1 136 | } 137 | output { 138 | name: "user_id" 139 | data_type: TYPE_INT32 140 | dims: -1 141 | dims: 1 142 | } 143 | output { 144 | name: "user_shops" 145 | data_type: TYPE_INT32 146 | dims: -1 147 | dims: 1 148 | } 149 | output { 150 | name: "user_profile" 151 | data_type: TYPE_INT32 152 | dims: -1 153 | dims: 1 154 | } 155 | output { 156 | name: "user_group" 157 | data_type: TYPE_INT32 158 | dims: -1 159 | dims: 1 160 | } 161 | output { 162 | name: "user_gender" 163 | data_type: TYPE_INT32 164 | dims: -1 165 | dims: 1 166 | } 167 | output { 168 | name: "user_age" 169 | data_type: TYPE_INT32 170 | dims: -1 171 | dims: 1 172 | } 173 | output { 174 | name: "user_consumption_2" 175 | data_type: TYPE_INT32 176 | dims: -1 177 | dims: 1 178 | } 179 | output { 180 | name: "user_is_occupied" 181 | data_type: TYPE_INT32 182 | dims: -1 183 | dims: 1 184 | } 185 | output { 186 | name: "user_geography" 187 | data_type: TYPE_INT32 188 | dims: -1 189 | dims: 1 190 | } 191 | output { 192 | name: "user_intentions" 193 | data_type: TYPE_INT32 194 | dims: -1 195 | dims: 1 196 | } 197 | output { 198 | name: "user_brands" 199 | data_type: TYPE_INT32 200 | dims: -1 201 | dims: 1 202 | } 203 | output { 204 | name: "user_categories" 205 | data_type: TYPE_INT32 206 | dims: -1 207 | dims: 1 208 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | dynamic_batching {} 4 | input { 5 | name: "item_brand" 6 | data_type: TYPE_INT32 7 | dims: -1 8 | dims: 1 9 | } 10 | input { 11 | name: "item_category" 12 | data_type: TYPE_INT32 13 | dims: -1 14 | dims: 1 15 | } 16 | input { 17 | name: "item_id" 18 | data_type: TYPE_INT32 19 | dims: -1 20 | dims: 1 21 | } 22 | input { 23 | name: "item_id_raw" 24 | data_type: TYPE_INT32 25 | dims: -1 26 | dims: 1 27 | } 28 | input { 29 | name: "item_shop" 30 | data_type: TYPE_INT32 31 | dims: -1 32 | dims: 1 33 | } 34 | input { 35 | name: "user_age" 36 | data_type: TYPE_INT32 37 | dims: -1 38 | dims: 1 39 | } 40 | input { 41 | name: "user_brands" 42 | data_type: TYPE_INT32 43 | dims: -1 44 | dims: 1 45 | } 46 | input { 47 | name: "user_categories" 48 | data_type: TYPE_INT32 49 | dims: -1 50 | dims: 1 51 | } 52 | input { 53 | name: "user_consumption_2" 54 | data_type: TYPE_INT32 55 | dims: -1 56 | dims: 1 57 | } 58 | input { 59 | name: "user_gender" 60 | data_type: TYPE_INT32 61 | dims: -1 62 | dims: 1 63 | } 64 | input { 65 | name: "user_geography" 66 | data_type: TYPE_INT32 67 | dims: -1 68 | dims: 1 69 | } 70 | input { 71 | name: "user_group" 72 | data_type: TYPE_INT32 73 | dims: -1 74 | dims: 1 75 | } 76 | input { 77 | name: "user_id" 78 | data_type: TYPE_INT32 79 | dims: -1 80 | dims: 1 81 | } 82 | input { 83 | name: "user_id_raw" 84 | data_type: TYPE_INT32 85 | dims: -1 86 | dims: 1 87 | } 88 | input { 89 | name: "user_intentions" 90 | data_type: TYPE_INT32 91 | dims: -1 92 | dims: 1 93 | } 94 | input { 95 | name: "user_is_occupied" 96 | data_type: TYPE_INT32 97 | dims: -1 98 | dims: 1 99 | } 100 | input { 101 | name: "user_profile" 102 | data_type: TYPE_INT32 103 | dims: -1 104 | dims: 1 105 | } 106 | input { 107 | name: "user_shops" 108 | data_type: TYPE_INT32 109 | dims: -1 110 | dims: 1 111 | } 112 | output { 113 | name: "click/binary_classification_task" 114 | data_type: TYPE_FP32 115 | dims: -1 116 | dims: 1 117 | } 118 | parameters { 119 | key: "TF_GRAPH_TAG" 120 | value { 121 | string_value: "serve" 122 | } 123 | } 124 | parameters { 125 | key: "TF_SIGNATURE_DEF" 126 | value { 127 | string_value: "serving_default" 128 | } 129 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_id_raw" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "click/binary_classification_task" 13 | data_type: TYPE_FP32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "ordered_ids" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: -1 22 | } 23 | parameters { 24 | key: "softmax_config" 25 | value { 26 | string_value: "{\"topk\": \"16\"}" 27 | } 28 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/04-vss-retrieval/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/04-vss-retrieval/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/0-query-user-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | from redis import client 8 | import triton_python_backend_utils as pb_utils 9 | 10 | from merlin.dag import DictArray 11 | 12 | 13 | class TritonPythonModel: 14 | """Your Python model must use the same class name. Every Python model 15 | that is created must have "TritonPythonModel" as the class name. 16 | """ 17 | 18 | def initialize(self, args): 19 | """`initialize` is called only once when the model is being loaded. 20 | Implementing `initialize` function is optional. This function allows 21 | the model to intialize any state associated with this model. 22 | Parameters 23 | ---------- 24 | args : dict 25 | Both keys and values are strings. The dictionary keys and values are: 26 | * model_config: A JSON string containing the model configuration 27 | * model_instance_kind: A string containing model instance kind 28 | * model_instance_device_id: A string containing model instance device ID 29 | * model_repository: Model repository path 30 | * model_version: Model version 31 | * model_name: Model name 32 | """ 33 | 34 | # Parse model_config. JSON string is not parsed here 35 | logging.info("Parsing model config") 36 | self.model_config = model_config = json.loads(args['model_config']) 37 | self.output_names = [output["name"] for output in self.model_config["output"]] 38 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 39 | self.entity_column = "user_id_raw" 40 | 41 | host = "52.9.82.78" 42 | port = 18376 43 | self._client = client.Redis(host=host, port=port, decode_responses=True) 44 | 45 | def execute(self, requests): 46 | """`execute` MUST be implemented in every Python model. `execute` 47 | function receives a list of pb_utils.InferenceRequest as the only 48 | argument. This function is called when an inference request is made 49 | for this model. Depending on the batching configuration (e.g. Dynamic 50 | Batching) used, `requests` may contain multiple requests. Every 51 | Python model, must create one pb_utils.InferenceResponse for every 52 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 53 | set the error argument when creating a pb_utils.InferenceResponse 54 | Parameters 55 | ---------- 56 | requests : list 57 | A list of pb_utils.InferenceRequest 58 | Returns 59 | ------- 60 | list 61 | A list of pb_utils.InferenceResponse. The length of this list must 62 | be the same as `requests` 63 | """ 64 | responses = [] 65 | 66 | # Every Python backend must iterate over everyone of the requests 67 | # and create a pb_utils.InferenceResponse for each of them. 68 | for request in requests: 69 | # Get Inputs 70 | input_tensor = { 71 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 72 | for name in self.input_names 73 | } 74 | input_df = DictArray(input_tensor) 75 | entity_ids = input_df[self.entity_column] 76 | if len(entity_ids) < 1: 77 | raise ValueError( 78 | "must provide entity_id" 79 | ) 80 | 81 | user_features = self._client.hgetall(f"user:{str(entity_ids[0][0])}") 82 | # Loop through features to create separate output tensors 83 | output_tensors = [] 84 | for output_name in self.output_names: 85 | feature_value = int(user_features[output_name]) 86 | feature_array = np.array(feature_value).T.astype(np.int32).reshape(-1, 1) 87 | out_tensor = pb_utils.Tensor(output_name, feature_array) 88 | output_tensors.append(out_tensor) 89 | 90 | # Create InferenceResponse and append to responses 91 | inference_response = pb_utils.InferenceResponse(output_tensors) 92 | responses.append(inference_response) 93 | 94 | # Return a list of pb_utils.InferenceResponse 95 | return responses 96 | 97 | def finalize(self): 98 | """`finalize` is called only once when the model is being unloaded. 99 | Implementing `finalize` function is OPTIONAL. This function allows 100 | the model to perform any necessary clean ups before exit. 101 | """ 102 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/0-query-user-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "0-query-user-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | response_cache { 5 | enable: True 6 | } 7 | instance_group [{ kind: KIND_CPU, count: 2 }] 8 | input { 9 | name: "user_id_raw" 10 | data_type: TYPE_INT32 11 | dims: -1 12 | dims: 1 13 | } 14 | output { 15 | name: "user_shops" 16 | data_type: TYPE_INT32 17 | dims: -1 18 | dims: 1 19 | } 20 | output { 21 | name: "user_profile" 22 | data_type: TYPE_INT32 23 | dims: -1 24 | dims: 1 25 | } 26 | output { 27 | name: "user_group" 28 | data_type: TYPE_INT32 29 | dims: -1 30 | dims: 1 31 | } 32 | output { 33 | name: "user_gender" 34 | data_type: TYPE_INT32 35 | dims: -1 36 | dims: 1 37 | } 38 | output { 39 | name: "user_age" 40 | data_type: TYPE_INT32 41 | dims: -1 42 | dims: 1 43 | } 44 | output { 45 | name: "user_consumption_2" 46 | data_type: TYPE_INT32 47 | dims: -1 48 | dims: 1 49 | } 50 | output { 51 | name: "user_is_occupied" 52 | data_type: TYPE_INT32 53 | dims: -1 54 | dims: 1 55 | } 56 | output { 57 | name: "user_geography" 58 | data_type: TYPE_INT32 59 | dims: -1 60 | dims: 1 61 | } 62 | output { 63 | name: "user_intentions" 64 | data_type: TYPE_INT32 65 | dims: -1 66 | dims: 1 67 | } 68 | output { 69 | name: "user_brands" 70 | data_type: TYPE_INT32 71 | dims: -1 72 | dims: 1 73 | } 74 | output { 75 | name: "user_categories" 76 | data_type: TYPE_INT32 77 | dims: -1 78 | dims: 1 79 | } 80 | output { 81 | name: "user_id" 82 | data_type: TYPE_INT32 83 | dims: -1 84 | dims: 1 85 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/1-user-embeddings/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "1-user-embeddings" 2 | platform: "tensorflow_savedmodel" 3 | backend: "tensorflow" 4 | dynamic_batching {} 5 | instance_group [{ kind: KIND_GPU, count: 2 }] 6 | input { 7 | name: "user_age" 8 | data_type: TYPE_INT32 9 | dims: -1 10 | dims: 1 11 | } 12 | input { 13 | name: "user_brands" 14 | data_type: TYPE_INT32 15 | dims: -1 16 | dims: 1 17 | } 18 | input { 19 | name: "user_categories" 20 | data_type: TYPE_INT32 21 | dims: -1 22 | dims: 1 23 | } 24 | input { 25 | name: "user_consumption_2" 26 | data_type: TYPE_INT32 27 | dims: -1 28 | dims: 1 29 | } 30 | input { 31 | name: "user_gender" 32 | data_type: TYPE_INT32 33 | dims: -1 34 | dims: 1 35 | } 36 | input { 37 | name: "user_geography" 38 | data_type: TYPE_INT32 39 | dims: -1 40 | dims: 1 41 | } 42 | input { 43 | name: "user_group" 44 | data_type: TYPE_INT32 45 | dims: -1 46 | dims: 1 47 | } 48 | input { 49 | name: "user_id" 50 | data_type: TYPE_INT32 51 | dims: -1 52 | dims: 1 53 | } 54 | input { 55 | name: "user_intentions" 56 | data_type: TYPE_INT32 57 | dims: -1 58 | dims: 1 59 | } 60 | input { 61 | name: "user_is_occupied" 62 | data_type: TYPE_INT32 63 | dims: -1 64 | dims: 1 65 | } 66 | input { 67 | name: "user_profile" 68 | data_type: TYPE_INT32 69 | dims: -1 70 | dims: 1 71 | } 72 | input { 73 | name: "user_shops" 74 | data_type: TYPE_INT32 75 | dims: -1 76 | dims: 1 77 | } 78 | output { 79 | name: "output_1" 80 | data_type: TYPE_FP32 81 | dims: -1 82 | dims: 64 83 | } 84 | parameters { 85 | key: "TF_GRAPH_TAG" 86 | value { 87 | string_value: "serve" 88 | } 89 | } 90 | parameters { 91 | key: "TF_SIGNATURE_DEF" 92 | value { 93 | string_value: "serving_default" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/2-redis-vss-candidates/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "2-redis-vss-candidates" 2 | backend: "python" 3 | dynamic_batching {} 4 | response_cache { 5 | enable: True 6 | } 7 | instance_group [{ kind: KIND_CPU, count: 4 }] 8 | input { 9 | name: "output_1" 10 | data_type: TYPE_FP32 11 | dims: -1 12 | dims: 64 13 | } 14 | output { 15 | name: "item_category" 16 | data_type: TYPE_INT32 17 | dims: -1 18 | dims: 1 19 | } 20 | output { 21 | name: "item_shop" 22 | data_type: TYPE_INT32 23 | dims: -1 24 | dims: 1 25 | } 26 | output { 27 | name: "item_brand" 28 | data_type: TYPE_INT32 29 | dims: -1 30 | dims: 1 31 | } 32 | output { 33 | name: "item_id_raw" 34 | data_type: TYPE_INT32 35 | dims: -1 36 | dims: 1 37 | } 38 | output { 39 | name: "item_id" 40 | data_type: TYPE_INT32 41 | dims: -1 42 | dims: 1 43 | } 44 | parameters { 45 | key: "vector_db_config" 46 | value { 47 | string_value: "{\"index_name\": \"candidate_index_2\", \"vector_field_name\": \"item_embedding\", \"topk\": \"64\"}" 48 | } 49 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/4-unroll-features/1/model.py: -------------------------------------------------------------------------------- 1 | import io 2 | import json 3 | import logging 4 | import sys 5 | 6 | import numpy as np 7 | import triton_python_backend_utils as pb_utils 8 | 9 | from merlin.dag import DictArray 10 | 11 | 12 | class TritonPythonModel: 13 | """Your Python model must use the same class name. Every Python model 14 | that is created must have "TritonPythonModel" as the class name. 15 | """ 16 | 17 | def initialize(self, args): 18 | """`initialize` is called only once when the model is being loaded. 19 | Implementing `initialize` function is optional. This function allows 20 | the model to intialize any state associated with this model. 21 | Parameters 22 | ---------- 23 | args : dict 24 | Both keys and values are strings. The dictionary keys and values are: 25 | * model_config: A JSON string containing the model configuration 26 | * model_instance_kind: A string containing model instance kind 27 | * model_instance_device_id: A string containing model instance device ID 28 | * model_repository: Model repository path 29 | * model_version: Model version 30 | * model_name: Model name 31 | """ 32 | 33 | # Parse model_config. JSON string is not parsed here 34 | logging.info("Parsing model config") 35 | self.model_config = model_config = json.loads(args['model_config']) 36 | self.output_names = [output["name"] for output in self.model_config["output"]] 37 | self.input_names = [inp["name"] for inp in self.model_config["input"]] 38 | 39 | self.item_id_col = "item_id" 40 | self.unroll_col_names = [ 41 | "user_id", 42 | "user_shops", 43 | "user_profile", 44 | "user_group", 45 | "user_gender", 46 | "user_age", 47 | "user_consumption_2", 48 | "user_is_occupied", 49 | "user_geography", 50 | "user_intentions", 51 | "user_brands", 52 | "user_categories", 53 | ] 54 | 55 | def execute(self, requests): 56 | """`execute` MUST be implemented in every Python model. `execute` 57 | function receives a list of pb_utils.InferenceRequest as the only 58 | argument. This function is called when an inference request is made 59 | for this model. Depending on the batching configuration (e.g. Dynamic 60 | Batching) used, `requests` may contain multiple requests. Every 61 | Python model, must create one pb_utils.InferenceResponse for every 62 | pb_utils.InferenceRequest in `requests`. If there is an error, you can 63 | set the error argument when creating a pb_utils.InferenceResponse 64 | Parameters 65 | ---------- 66 | requests : list 67 | A list of pb_utils.InferenceRequest 68 | Returns 69 | ------- 70 | list 71 | A list of pb_utils.InferenceResponse. The length of this list must 72 | be the same as `requests` 73 | """ 74 | 75 | responses = [] 76 | 77 | # Every Python backend must iterate over everyone of the requests 78 | # and create a pb_utils.InferenceResponse for each of them. 79 | for request in requests: 80 | # Get Inputs 81 | input_tensor = { 82 | name: pb_utils.get_input_tensor_by_name(request, name).as_numpy() 83 | for name in self.input_names 84 | } 85 | input_df = DictArray(input_tensor) 86 | num_items = input_df[self.item_id_col].shape[0] 87 | 88 | output_tensors = [] 89 | 90 | for col_name, col_value in input_df.items(): 91 | if col_name in self.unroll_col_names: 92 | # Unroll user features to match dimensionality of item features 93 | col_value = np.repeat(col_value, num_items, axis=0).reshape(-1, 1) 94 | 95 | out_tensor = pb_utils.Tensor(col_name, col_value) 96 | output_tensors.append(out_tensor) 97 | 98 | # Create InferenceResponse and append to responses 99 | inference_response = pb_utils.InferenceResponse(output_tensors) 100 | responses.append(inference_response) 101 | 102 | # Return a list of pb_utils.InferenceResponse 103 | return responses 104 | 105 | def finalize(self): 106 | """`finalize` is called only once when the model is being unloaded. 107 | Implementing `finalize` function is OPTIONAL. This function allows 108 | the model to perform any necessary clean ups before exit. 109 | """ 110 | logging.info('Cleaning up...') -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/4-unroll-features/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "4-unroll-features" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_category" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "item_shop" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "item_brand" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "item_id_raw" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "item_id" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_id" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_shops" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_profile" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_group" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_gender" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_age" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_consumption_2" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | input { 78 | name: "user_is_occupied" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } 83 | input { 84 | name: "user_geography" 85 | data_type: TYPE_INT32 86 | dims: -1 87 | dims: 1 88 | } 89 | input { 90 | name: "user_intentions" 91 | data_type: TYPE_INT32 92 | dims: -1 93 | dims: 1 94 | } 95 | input { 96 | name: "user_brands" 97 | data_type: TYPE_INT32 98 | dims: -1 99 | dims: 1 100 | } 101 | input { 102 | name: "user_categories" 103 | data_type: TYPE_INT32 104 | dims: -1 105 | dims: 1 106 | } 107 | output { 108 | name: "item_category" 109 | data_type: TYPE_INT32 110 | dims: -1 111 | dims: 1 112 | } 113 | output { 114 | name: "item_shop" 115 | data_type: TYPE_INT32 116 | dims: -1 117 | dims: 1 118 | } 119 | output { 120 | name: "item_brand" 121 | data_type: TYPE_INT32 122 | dims: -1 123 | dims: 1 124 | } 125 | output { 126 | name: "item_id_raw" 127 | data_type: TYPE_INT32 128 | dims: -1 129 | dims: 1 130 | } 131 | output { 132 | name: "item_id" 133 | data_type: TYPE_INT32 134 | dims: -1 135 | dims: 1 136 | } 137 | output { 138 | name: "user_id" 139 | data_type: TYPE_INT32 140 | dims: -1 141 | dims: 1 142 | } 143 | output { 144 | name: "user_shops" 145 | data_type: TYPE_INT32 146 | dims: -1 147 | dims: 1 148 | } 149 | output { 150 | name: "user_profile" 151 | data_type: TYPE_INT32 152 | dims: -1 153 | dims: 1 154 | } 155 | output { 156 | name: "user_group" 157 | data_type: TYPE_INT32 158 | dims: -1 159 | dims: 1 160 | } 161 | output { 162 | name: "user_gender" 163 | data_type: TYPE_INT32 164 | dims: -1 165 | dims: 1 166 | } 167 | output { 168 | name: "user_age" 169 | data_type: TYPE_INT32 170 | dims: -1 171 | dims: 1 172 | } 173 | output { 174 | name: "user_consumption_2" 175 | data_type: TYPE_INT32 176 | dims: -1 177 | dims: 1 178 | } 179 | output { 180 | name: "user_is_occupied" 181 | data_type: TYPE_INT32 182 | dims: -1 183 | dims: 1 184 | } 185 | output { 186 | name: "user_geography" 187 | data_type: TYPE_INT32 188 | dims: -1 189 | dims: 1 190 | } 191 | output { 192 | name: "user_intentions" 193 | data_type: TYPE_INT32 194 | dims: -1 195 | dims: 1 196 | } 197 | output { 198 | name: "user_brands" 199 | data_type: TYPE_INT32 200 | dims: -1 201 | dims: 1 202 | } 203 | output { 204 | name: "user_categories" 205 | data_type: TYPE_INT32 206 | dims: -1 207 | dims: 1 208 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/keras_metadata.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/keras_metadata.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/saved_model.pb -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/variables/variables.data-00000-of-00001 -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/1/model.savedmodel/variables/variables.index -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/5-ranking/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "5-ranking" 2 | platform: "tensorflow_savedmodel" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_GPU, count: 2 }] 5 | input { 6 | name: "item_brand" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "item_category" 13 | data_type: TYPE_INT32 14 | dims: -1 15 | dims: 1 16 | } 17 | input { 18 | name: "item_id" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: 1 22 | } 23 | input { 24 | name: "item_id_raw" 25 | data_type: TYPE_INT32 26 | dims: -1 27 | dims: 1 28 | } 29 | input { 30 | name: "item_shop" 31 | data_type: TYPE_INT32 32 | dims: -1 33 | dims: 1 34 | } 35 | input { 36 | name: "user_age" 37 | data_type: TYPE_INT32 38 | dims: -1 39 | dims: 1 40 | } 41 | input { 42 | name: "user_brands" 43 | data_type: TYPE_INT32 44 | dims: -1 45 | dims: 1 46 | } 47 | input { 48 | name: "user_categories" 49 | data_type: TYPE_INT32 50 | dims: -1 51 | dims: 1 52 | } 53 | input { 54 | name: "user_consumption_2" 55 | data_type: TYPE_INT32 56 | dims: -1 57 | dims: 1 58 | } 59 | input { 60 | name: "user_gender" 61 | data_type: TYPE_INT32 62 | dims: -1 63 | dims: 1 64 | } 65 | input { 66 | name: "user_geography" 67 | data_type: TYPE_INT32 68 | dims: -1 69 | dims: 1 70 | } 71 | input { 72 | name: "user_group" 73 | data_type: TYPE_INT32 74 | dims: -1 75 | dims: 1 76 | } 77 | input { 78 | name: "user_id" 79 | data_type: TYPE_INT32 80 | dims: -1 81 | dims: 1 82 | } 83 | input { 84 | name: "user_id_raw" 85 | data_type: TYPE_INT32 86 | dims: -1 87 | dims: 1 88 | } 89 | input { 90 | name: "user_intentions" 91 | data_type: TYPE_INT32 92 | dims: -1 93 | dims: 1 94 | } 95 | input { 96 | name: "user_is_occupied" 97 | data_type: TYPE_INT32 98 | dims: -1 99 | dims: 1 100 | } 101 | input { 102 | name: "user_profile" 103 | data_type: TYPE_INT32 104 | dims: -1 105 | dims: 1 106 | } 107 | input { 108 | name: "user_shops" 109 | data_type: TYPE_INT32 110 | dims: -1 111 | dims: 1 112 | } 113 | output { 114 | name: "click/binary_classification_task" 115 | data_type: TYPE_FP32 116 | dims: -1 117 | dims: 1 118 | } 119 | parameters { 120 | key: "TF_GRAPH_TAG" 121 | value { 122 | string_value: "serve" 123 | } 124 | } 125 | parameters { 126 | key: "TF_SIGNATURE_DEF" 127 | value { 128 | string_value: "serving_default" 129 | } 130 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/6-softmax-sampling/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "6-softmax-sampling" 2 | backend: "python" 3 | dynamic_batching {} 4 | instance_group [{ kind: KIND_CPU }] 5 | input { 6 | name: "item_id_raw" 7 | data_type: TYPE_INT32 8 | dims: -1 9 | dims: 1 10 | } 11 | input { 12 | name: "click/binary_classification_task" 13 | data_type: TYPE_FP32 14 | dims: -1 15 | dims: 1 16 | } 17 | output { 18 | name: "ordered_ids" 19 | data_type: TYPE_INT32 20 | dims: -1 21 | dims: -1 22 | } 23 | parameters { 24 | key: "softmax_config" 25 | value { 26 | string_value: "{\"topk\": \"16\"}" 27 | } 28 | } -------------------------------------------------------------------------------- /online-multi-stage-recsys/revisions/05-vss-retrieval-opt/ensemble-model/1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redis-developer/redis-nvidia-recsys/bad10224bf75d45845fa7040afccc6ef12fd1dd6/online-multi-stage-recsys/revisions/05-vss-retrieval-opt/ensemble-model/1/.gitkeep -------------------------------------------------------------------------------- /online-multi-stage-recsys/sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [{ 3 | "user_id_raw": [23] 4 | }] 5 | } 6 | -------------------------------------------------------------------------------- /online-multi-stage-recsys/sample_32.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "user_id_raw": [1] 5 | }, 6 | { 7 | "user_id_raw": [2] 8 | }, 9 | { 10 | "user_id_raw": [3] 11 | }, 12 | { 13 | "user_id_raw": [4] 14 | }, 15 | { 16 | "user_id_raw": [5] 17 | }, 18 | { 19 | "user_id_raw": [6] 20 | }, 21 | { 22 | "user_id_raw": [7] 23 | }, 24 | { 25 | "user_id_raw": [8] 26 | }, 27 | { 28 | "user_id_raw": [9] 29 | }, 30 | { 31 | "user_id_raw": [10] 32 | }, 33 | { 34 | "user_id_raw": [11] 35 | }, 36 | { 37 | "user_id_raw": [12] 38 | }, 39 | { 40 | "user_id_raw": [13] 41 | }, 42 | { 43 | "user_id_raw": [14] 44 | }, 45 | { 46 | "user_id_raw": [15] 47 | }, 48 | { 49 | "user_id_raw": [16] 50 | }, 51 | { 52 | "user_id_raw": [17] 53 | }, 54 | { 55 | "user_id_raw": [18] 56 | }, 57 | { 58 | "user_id_raw": [19] 59 | }, 60 | { 61 | "user_id_raw": [20] 62 | }, 63 | { 64 | "user_id_raw": [21] 65 | }, 66 | { 67 | "user_id_raw": [22] 68 | }, 69 | { 70 | "user_id_raw": [23] 71 | }, 72 | { 73 | "user_id_raw": [24] 74 | }, 75 | { 76 | "user_id_raw": [25] 77 | }, 78 | { 79 | "user_id_raw": [26] 80 | }, 81 | { 82 | "user_id_raw": [27] 83 | }, 84 | { 85 | "user_id_raw": [28] 86 | }, 87 | { 88 | "user_id_raw": [29] 89 | }, 90 | { 91 | "user_id_raw": [30] 92 | }, 93 | { 94 | "user_id_raw": [31] 95 | }, 96 | { 97 | "user_id_raw": [32] 98 | } 99 | ] 100 | } 101 | --------------------------------------------------------------------------------