From 939d4b172de7847019a06f73019b69b04e89d8c5 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Wed, 12 Jun 2024 15:15:16 +0200 Subject: [PATCH 01/19] Add grpcio, grpcio-tools and protobuf --- poetry.lock | 207 +++++++++++++++++++++++-------------- proto/action_webhook.proto | 46 +++++++++ pyproject.toml | 3 + rasa_sdk/grpc_server.py | 0 4 files changed, 177 insertions(+), 79 deletions(-) create mode 100644 proto/action_webhook.proto create mode 100644 rasa_sdk/grpc_server.py diff --git a/poetry.lock b/poetry.lock index 2f688c463..557bc709d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -367,77 +367,137 @@ test = ["pytest (>=6)"] [[package]] name = "googleapis-common-protos" -version = "1.56.2" +version = "1.56.1" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.6" files = [ - {file = "googleapis-common-protos-1.56.2.tar.gz", hash = "sha256:b09b56f5463070c2153753ef123f07d2e49235e89148e9b2459ec8ed2f68d7d3"}, - {file = "googleapis_common_protos-1.56.2-py2.py3-none-any.whl", hash = "sha256:023eaea9d8c1cceccd9587c6af6c20f33eeeb05d4148670f2b0322dc1511700c"}, + {file = "googleapis-common-protos-1.56.1.tar.gz", hash = "sha256:6b5ee59dc646eb61a8eb65ee1db186d3df6687c8804830024f32573298bca19b"}, + {file = "googleapis_common_protos-1.56.1-py2.py3-none-any.whl", hash = "sha256:ddcd955b5bb6589368f659fa475373faa1ed7d09cde5ba25e88513d87007e174"}, ] [package.dependencies] -protobuf = ">=3.15.0,<4.0.0dev" +protobuf = ">=3.15.0" [package.extras] -grpc = ["grpcio (>=1.0.0,<2.0.0dev)"] +grpc = ["grpcio (>=1.0.0)"] [[package]] name = "grpcio" -version = "1.56.2" +version = "1.64.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "grpcio-1.56.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:bf0b9959e673505ee5869950642428046edb91f99942607c2ecf635f8a4b31c9"}, - {file = "grpcio-1.56.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:5144feb20fe76e73e60c7d73ec3bf54f320247d1ebe737d10672480371878b48"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a72797549935c9e0b9bc1def1768c8b5a709538fa6ab0678e671aec47ebfd55e"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3f3237a57e42f79f1e560726576aedb3a7ef931f4e3accb84ebf6acc485d316"}, - {file = "grpcio-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:900bc0096c2ca2d53f2e5cebf98293a7c32f532c4aeb926345e9747452233950"}, - {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:97e0efaebbfd222bcaac2f1735c010c1d3b167112d9d237daebbeedaaccf3d1d"}, - {file = "grpcio-1.56.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c0c85c5cbe8b30a32fa6d802588d55ffabf720e985abe9590c7c886919d875d4"}, - {file = "grpcio-1.56.2-cp310-cp310-win32.whl", hash = "sha256:06e84ad9ae7668a109e970c7411e7992751a116494cba7c4fb877656527f9a57"}, - {file = "grpcio-1.56.2-cp310-cp310-win_amd64.whl", hash = "sha256:10954662f77dc36c9a1fb5cc4a537f746580d6b5734803be1e587252682cda8d"}, - {file = "grpcio-1.56.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:c435f5ce1705de48e08fcbcfaf8aee660d199c90536e3e06f2016af7d6a938dd"}, - {file = "grpcio-1.56.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:6108e5933eb8c22cd3646e72d5b54772c29f57482fd4c41a0640aab99eb5071d"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8391cea5ce72f4a12368afd17799474015d5d3dc00c936a907eb7c7eaaea98a5"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750de923b456ca8c0f1354d6befca45d1f3b3a789e76efc16741bd4132752d95"}, - {file = "grpcio-1.56.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fda2783c12f553cdca11c08e5af6eecbd717280dc8fbe28a110897af1c15a88c"}, - {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9e04d4e4cfafa7c5264e535b5d28e786f0571bea609c3f0aaab13e891e933e9c"}, - {file = "grpcio-1.56.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:89a49cc5ad08a38b6141af17e00d1dd482dc927c7605bc77af457b5a0fca807c"}, - {file = "grpcio-1.56.2-cp311-cp311-win32.whl", hash = "sha256:6a007a541dff984264981fbafeb052bfe361db63578948d857907df9488d8774"}, - {file = "grpcio-1.56.2-cp311-cp311-win_amd64.whl", hash = "sha256:af4063ef2b11b96d949dccbc5a987272f38d55c23c4c01841ea65a517906397f"}, - {file = "grpcio-1.56.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:a6ff459dac39541e6a2763a4439c4ca6bc9ecb4acc05a99b79246751f9894756"}, - {file = "grpcio-1.56.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:f20fd21f7538f8107451156dd1fe203300b79a9ddceba1ee0ac8132521a008ed"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:d1fbad1f9077372b6587ec589c1fc120b417b6c8ad72d3e3cc86bbbd0a3cee93"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ee26e9dfb3996aff7c870f09dc7ad44a5f6732b8bdb5a5f9905737ac6fd4ef1"}, - {file = "grpcio-1.56.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c60abd950d6de3e4f1ddbc318075654d275c29c846ab6a043d6ed2c52e4c8c"}, - {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1c31e52a04e62c8577a7bf772b3e7bed4df9c9e0dd90f92b6ffa07c16cab63c9"}, - {file = "grpcio-1.56.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:345356b307cce5d14355e8e055b4ca5f99bc857c33a3dc1ddbc544fca9cd0475"}, - {file = "grpcio-1.56.2-cp37-cp37m-win_amd64.whl", hash = "sha256:42e63904ee37ae46aa23de50dac8b145b3596f43598fa33fe1098ab2cbda6ff5"}, - {file = "grpcio-1.56.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:7c5ede2e2558f088c49a1ddda19080e4c23fb5d171de80a726b61b567e3766ed"}, - {file = "grpcio-1.56.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:33971197c47965cc1d97d78d842163c283e998223b151bab0499b951fd2c0b12"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d39f5d4af48c138cb146763eda14eb7d8b3ccbbec9fe86fb724cd16e0e914c64"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ded637176addc1d3eef35331c39acc598bac550d213f0a1bedabfceaa2244c87"}, - {file = "grpcio-1.56.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c90da4b124647547a68cf2f197174ada30c7bb9523cb976665dfd26a9963d328"}, - {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3ccb621749a81dc7755243665a70ce45536ec413ef5818e013fe8dfbf5aa497b"}, - {file = "grpcio-1.56.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4eb37dd8dd1aa40d601212afa27ca5be255ba792e2e0b24d67b8af5e012cdb7d"}, - {file = "grpcio-1.56.2-cp38-cp38-win32.whl", hash = "sha256:ddb4a6061933bd9332b74eac0da25f17f32afa7145a33a0f9711ad74f924b1b8"}, - {file = "grpcio-1.56.2-cp38-cp38-win_amd64.whl", hash = "sha256:8940d6de7068af018dfa9a959a3510e9b7b543f4c405e88463a1cbaa3b2b379a"}, - {file = "grpcio-1.56.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:51173e8fa6d9a2d85c14426bdee5f5c4a0654fd5fddcc21fe9d09ab0f6eb8b35"}, - {file = "grpcio-1.56.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:373b48f210f43327a41e397391715cd11cfce9ded2fe76a5068f9bacf91cc226"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:42a3bbb2bc07aef72a7d97e71aabecaf3e4eb616d39e5211e2cfe3689de860ca"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5344be476ac37eb9c9ad09c22f4ea193c1316bf074f1daf85bddb1b31fda5116"}, - {file = "grpcio-1.56.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3fa3ab0fb200a2c66493828ed06ccd1a94b12eddbfb985e7fd3e5723ff156c6"}, - {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b975b85d1d5efc36cf8b237c5f3849b64d1ba33d6282f5e991f28751317504a1"}, - {file = "grpcio-1.56.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cbdf2c498e077282cd427cfd88bdce4668019791deef0be8155385ab2ba7837f"}, - {file = "grpcio-1.56.2-cp39-cp39-win32.whl", hash = "sha256:139f66656a762572ae718fa0d1f2dce47c05e9fbf7a16acd704c354405b97df9"}, - {file = "grpcio-1.56.2-cp39-cp39-win_amd64.whl", hash = "sha256:830215173ad45d670140ff99aac3b461f9be9a6b11bee1a17265aaaa746a641a"}, - {file = "grpcio-1.56.2.tar.gz", hash = "sha256:0ff789ae7d8ddd76d2ac02e7d13bfef6fc4928ac01e1dcaa182be51b6bcc0aaa"}, + {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, + {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, + {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, + {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, + {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, + {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, + {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, + {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, + {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, + {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, + {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, + {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, + {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, + {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, + {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, + {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, + {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, + {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, + {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, + {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, + {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, + {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, + {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, + {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, + {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, + {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, + {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, + {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, + {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, + {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, + {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.56.2)"] +protobuf = ["grpcio-tools (>=1.64.1)"] + +[[package]] +name = "grpcio-tools" +version = "1.56.2" +description = "Protobuf code generator for gRPC" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpcio-tools-1.56.2.tar.gz", hash = "sha256:82af2f4040084141a732f0ef1ecf3f14fdf629923d74d850415e4d09a077e77a"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:42272376e9a5a1c631863cda056c143c98d21e5b670db5c8c5b7ed0ba3a1a6eb"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a8735d7aa34be99dddfbd476eff6005e684bb2c893c0f62a5811528b84c5b371"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:68ef3aa7509e5e7a6e7c0ecc183e28118e73da4bef0fc77079648601ce35e58f"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:380985b8d95ea2469e103945bd83a815d1213e370f580631fdd5a3dbaa17e446"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bfb375eb4f1946d68b8bc7b963c756defa31aa573a35c152a7233d06c0ad6ad"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:13388a22fcba9a1a87f217130a1a01365716af74bd5d0a8a54fc383b8e048ef2"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7a26160bc0ea5b464715789d4d2a66f01816271677673d65da39bac65b9ea838"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-win32.whl", hash = "sha256:ff16dd0b086e75f574dbc122e018a44dbd1c6dae3f3621ea99e8e5a6b2706e12"}, + {file = "grpcio_tools-1.56.2-cp310-cp310-win_amd64.whl", hash = "sha256:2037109c1ce253a8e013c9e3ad3722e887d28a1807acdeb1a51b295c8200137b"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:6dc43300189a69807857c52a3d782e9d3bbfb1cb72dcb27b4043c25161919601"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:e7009623635ebcd3dd7fe974883fc2d9a3ff0fcef419bfc0a2da8071b372d9f5"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c7ca2272022f90b73efe900244aaebe9dd7cf3b379e99e08a88984e2fdd229c2"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493775d17ea09cea6047ba81e4d3f0eb82e34d2fbd3b96e43f72b44ce74726ee"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41af279cf5359b123138236c0980440f4cb4d3d18f03b5c1c314cc1512048351"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:857d72e991d449ec4d2f8337e5e24ddf77b4539965f5cabc84d4b63585832982"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c0640728d63c9fa56e9a1679943ae4e33ad43a10802dd7a93255870731f44d07"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-win32.whl", hash = "sha256:355204d1b33c7a19e7d69afda411e6595d39ba1e9cbf561770ac1d5403296554"}, + {file = "grpcio_tools-1.56.2-cp311-cp311-win_amd64.whl", hash = "sha256:ea5d108d28b4cd2e28539241c6aee96bda83086d8888c36785d9f84ea690d896"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:28444615b7a76b3d9267f81d1487fcad21a581d00564164d9e25ccc28635a811"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:45d8b5ad6716848d5b68d9cee29a1a9c5c4baa1824ec5b92d9e35acedddba076"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:31d1183d28ffc8da242333cb9f683f5093941da80dd5281db0fa93077aecb518"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0059dfc9bea8f7bca69c15ca62c88904c4f907fde1137e0743b5eee054661873"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24fc857252181c9950ed2d8cee3df5bd0f42861c4ad0db2a57400186827f96e5"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3a74a5e4fc8121a51401665f96f9a70aee50a2f1221e4a199e67b3b8f55881e8"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bec47db5d8b5c3b2a44afdbc3f3bf306e34279289a206d20222824381ca3cb13"}, + {file = "grpcio_tools-1.56.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c0dbaac63a25c088f864295f394230eeb7be48dac2264433fda2603f86c36b25"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:0a4f9cce5a16613b6d3123c89f9d50e0d13b466799af17bc723dc7d2901a54e4"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:ea5fc1b49514b44a3e5a45156c025002f172ade4c509e58c51967865c7c6fa45"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:54da410124547bacb97a54546c1a95f1af0125e48edc8b5679412ef8b2844f81"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5223668649172d879ee780253b8e4a79144c56a3cc1bb021847f583508c2b0be"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:483256d5f5be6a77b24d8a5f06ca152d1571c62bf5c738834da61107c7563afe"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f334718eb796799bfadbac5567456fb745cee8c7b438c93b74d1ce676c6ad07"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:26751f69cbbc8ea19cf0657b7d109a6db7df81f80caf16380ebcd20eea27652c"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-win32.whl", hash = "sha256:4056ff13e30813d42a30ce1cdfeaeb6bbee915515c161c1df896dac3143ae643"}, + {file = "grpcio_tools-1.56.2-cp38-cp38-win_amd64.whl", hash = "sha256:878b9269ceb0dd934b61697a9dd9a5c3e9552521e8f46ab32cf4d72a223f7b6c"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:216e86d3a6ccc31b27fa4c12491981a0a39d4787d2358b6df05baffa40084494"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:778224fcbc1cc7eaf222ce94676afbac8d72b4f84cf4239e30b01d2450a46126"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:14120fb2c6f7894fac5b689934368c692ec50f50a320e8073277ab7778fd612f"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:014da3ed176beb2b1c8430ccc34c8fe962cdd5480e56fb4ab9de60e60c315f3f"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8febb4f90b8fab3179f5bdaa159f1d2a20523ea17ec0d66bdec7732f9532de91"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ffae7df3318266614f7aa440acb2098c064b6b5ae061fc22125092386349e526"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7d86e24eb6e3973c55e9c74412ff755d1b9d15518c4eaf95676acff49d0162a2"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-win32.whl", hash = "sha256:506d00a86950adf4017395551a4547c0b7fcefa90e4c220135fc3e34e31be81b"}, + {file = "grpcio_tools-1.56.2-cp39-cp39-win_amd64.whl", hash = "sha256:8da04f033b8f4c597e8fc990e2f626bad2b269227bdd554592ea618f624f1aa9"}, +] + +[package.dependencies] +grpcio = ">=1.56.2" +protobuf = ">=4.21.6,<5.0dev" +setuptools = "*" [[package]] name = "h11" @@ -1079,33 +1139,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "3.20.3" -description = "Protocol Buffers" +version = "4.25.3" +description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "protobuf-3.20.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99"}, - {file = "protobuf-3.20.3-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e"}, - {file = "protobuf-3.20.3-cp310-cp310-win32.whl", hash = "sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c"}, - {file = "protobuf-3.20.3-cp310-cp310-win_amd64.whl", hash = "sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7"}, - {file = "protobuf-3.20.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469"}, - {file = "protobuf-3.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4"}, - {file = "protobuf-3.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454"}, - {file = "protobuf-3.20.3-cp37-cp37m-win32.whl", hash = "sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905"}, - {file = "protobuf-3.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c"}, - {file = "protobuf-3.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"}, - {file = "protobuf-3.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050"}, - {file = "protobuf-3.20.3-cp38-cp38-win32.whl", hash = "sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86"}, - {file = "protobuf-3.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9"}, - {file = "protobuf-3.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b"}, - {file = "protobuf-3.20.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402"}, - {file = "protobuf-3.20.3-cp39-cp39-win32.whl", hash = "sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480"}, - {file = "protobuf-3.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7"}, - {file = "protobuf-3.20.3-py2.py3-none-any.whl", hash = "sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db"}, - {file = "protobuf-3.20.3.tar.gz", hash = "sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2"}, + {file = "protobuf-4.25.3-cp310-abi3-win32.whl", hash = "sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa"}, + {file = "protobuf-4.25.3-cp310-abi3-win_amd64.whl", hash = "sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8"}, + {file = "protobuf-4.25.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019"}, + {file = "protobuf-4.25.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d"}, + {file = "protobuf-4.25.3-cp38-cp38-win32.whl", hash = "sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2"}, + {file = "protobuf-4.25.3-cp38-cp38-win_amd64.whl", hash = "sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4"}, + {file = "protobuf-4.25.3-cp39-cp39-win32.whl", hash = "sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4"}, + {file = "protobuf-4.25.3-cp39-cp39-win_amd64.whl", hash = "sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c"}, + {file = "protobuf-4.25.3-py3-none-any.whl", hash = "sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9"}, + {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, ] [[package]] @@ -1851,4 +1900,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "0c854c620eb789aa7b5697a6430b0af5b2c5d9f29a2f045adfe71be21b35ed48" +content-hash = "154b5b360e5a822ebeacf1cc569bfdb45c9c2e318a4ff69031210117abb7c47a" diff --git a/proto/action_webhook.proto b/proto/action_webhook.proto new file mode 100644 index 000000000..19ce1d979 --- /dev/null +++ b/proto/action_webhook.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +package action_server_webhook; +import "google/protobuf/any.proto"; + +service ActionServerWebhook { + rpc webhook (WebhookRequest) returns (WebhookResponse); +} + +message Tracker { + string conversation_id = 1; + map slots = 2; + map latest_message = 3; + double latest_event_time = 4; + string followup_action = 5; + bool paused = 6; + repeated string events = 7; + string latest_input_channel = 8; + map active_loop = 9; + map latest_action = 10; +} + +message Domain { + map config = 1; + map session_config = 2; + repeated string intents = 3; + repeated string entities = 4; + map slots = 5; + map responses = 6; + repeated string actions = 7; + map forms = 8; + repeated string e2e_actions = 9; +} + +message WebhookRequest { + string next_action = 1; + string sender_id = 2; + Tracker tracker = 3; + Domain domain = 4; + string version = 5; +} + +message WebhookResponse { + repeated google.protobuf.Any events = 1; + repeated google.protobuf.Any responses = 2; +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c4c03b318..7d4105334 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,9 @@ opentelemetry-api = "~1.15.0" opentelemetry-sdk = "~1.15.0" opentelemetry-exporter-jaeger = "~1.15.0" opentelemetry-exporter-otlp = "~1.15.0" +grpcio = "1.64.1" +protobuf = "4.25.3" +grpcio-tools = "1.56.2" [tool.poetry.dev-dependencies] pytest-cov = "^4.1.0" diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py new file mode 100644 index 000000000..e69de29bb From dc988a2faf391b5764a9bbc9b6b7599877c76943 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Wed, 12 Jun 2024 16:50:43 +0200 Subject: [PATCH 02/19] Add grpc python code --- Makefile | 11 +- poetry.lock | 13 +- pyproject.toml | 1 + rasa_sdk/__main__.py | 50 +++++-- rasa_sdk/cli/arguments.py | 5 + rasa_sdk/grpc_py/__init__.py | 0 rasa_sdk/grpc_py/action_webhook_pb2.py | 71 ++++++++++ rasa_sdk/grpc_py/action_webhook_pb2.pyi | 138 ++++++++++++++++++++ rasa_sdk/grpc_py/action_webhook_pb2_grpc.py | 66 ++++++++++ rasa_sdk/grpc_server.py | 110 ++++++++++++++++ rasa_sdk/tracing/utils.py | 8 +- 11 files changed, 458 insertions(+), 15 deletions(-) create mode 100644 rasa_sdk/grpc_py/__init__.py create mode 100644 rasa_sdk/grpc_py/action_webhook_pb2.py create mode 100644 rasa_sdk/grpc_py/action_webhook_pb2.pyi create mode 100644 rasa_sdk/grpc_py/action_webhook_pb2_grpc.py diff --git a/Makefile b/Makefile index 2f2c72679..173d03dda 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,6 @@ install: poetry run python -m pip install -U pip poetry install - clean: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + @@ -62,3 +61,13 @@ cleanup-generated-changelog: release: poetry run python scripts/release.py +download-protoc-compiler: + curl -0L https://github.com/protocolbuffers/protobuf/releases/download/v25.0/protoc-25.0-osx-aarch_64.zip --output protoc-25.0-osx-aarch_64.zip + +generate-grpc: + python -m grpc_tools.protoc \ + -Irasa_sdk/grpc_py=./proto \ + --python_out=. \ + --grpc_python_out=. \ + --pyi_out=. \ + proto/action_webhook.proto \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 557bc709d..e603df7b7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1564,6 +1564,17 @@ tomli = {version = "*", markers = "python_version < \"3.11\""} [package.extras] dev = ["furo", "packaging", "sphinx (>=5)", "twisted"] +[[package]] +name = "types-protobuf" +version = "4.25.0.20240417" +description = "Typing stubs for protobuf" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-protobuf-4.25.0.20240417.tar.gz", hash = "sha256:c34eff17b9b3a0adb6830622f0f302484e4c089f533a46e3f147568313544352"}, + {file = "types_protobuf-4.25.0.20240417-py3-none-any.whl", hash = "sha256:e9b613227c2127e3d4881d75d93c93b4d6fd97b5f6a099a0b654a05351c8685d"}, +] + [[package]] name = "typing-extensions" version = "4.9.0" @@ -1900,4 +1911,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "154b5b360e5a822ebeacf1cc569bfdb45c9c2e318a4ff69031210117abb7c47a" +content-hash = "6fab19810735e6ca4cd28bc038f641e64545379904ba604b9f405088d0e2bfbe" diff --git a/pyproject.toml b/pyproject.toml index 7d4105334..6dfc4372b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,3 +114,4 @@ asyncio_mode = "auto" [tool.poetry.group.dev.dependencies] ruff = ">=0.0.256,<0.0.286" pytest-asyncio = "^0.21.0" +types-protobuf = "4.25.0.20240417" diff --git a/rasa_sdk/__main__.py b/rasa_sdk/__main__.py index 67b9fb497..dcd6e70a0 100644 --- a/rasa_sdk/__main__.py +++ b/rasa_sdk/__main__.py @@ -1,8 +1,24 @@ import logging +import asyncio +import signal from rasa_sdk import utils from rasa_sdk.endpoint import create_argument_parser, run from rasa_sdk.constants import APPLICATION_ROOT_LOGGER_NAME +from rasa_sdk.grpc_server import run_grpc + +logger = logging.getLogger(__name__) + + +def initialise_interrupts() -> None: + """Initialise handlers for kernel signal interrupts.""" + + def handle_sigint(signum, frame): + logger.info("Received SIGINT, exiting") + asyncio.get_event_loop().stop() + + signal.signal(signal.SIGINT, handle_sigint) + signal.signal(signal.SIGTERM, handle_sigint) def main_from_args(args): @@ -18,16 +34,30 @@ def main_from_args(args): ) utils.update_sanic_log_level() - run( - args.actions, - args.port, - args.cors, - args.ssl_certificate, - args.ssl_keyfile, - args.ssl_password, - args.auto_reload, - args.endpoints, - ) + initialise_interrupts() + + if args.grpc: + asyncio.run( + run_grpc( + args.actions, + args.port, + args.ssl_certificate, + args.ssl_keyfile, + args.ssl_password, + args.endpoints, + ) + ) + else: + run( + args.actions, + args.port, + args.cors, + args.ssl_certificate, + args.ssl_keyfile, + args.ssl_password, + args.auto_reload, + args.endpoints, + ) def main(): diff --git a/rasa_sdk/cli/arguments.py b/rasa_sdk/cli/arguments.py index c520d72af..5a805ef8d 100644 --- a/rasa_sdk/cli/arguments.py +++ b/rasa_sdk/cli/arguments.py @@ -59,3 +59,8 @@ def add_endpoint_arguments(parser): default=DEFAULT_ENDPOINTS_PATH, help="Configuration file for the assistant as a yml file.", ) + parser.add_argument( + "--grpc", + help="Starts grpc server instead of http", + action="store_true" + ) diff --git a/rasa_sdk/grpc_py/__init__.py b/rasa_sdk/grpc_py/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.py b/rasa_sdk/grpc_py/action_webhook_pb2.py new file mode 100644 index 000000000..9bb03ad36 --- /dev/null +++ b/rasa_sdk/grpc_py/action_webhook_pb2.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: rasa_sdk/grpc_py/action_webhook.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%rasa_sdk/grpc_py/action_webhook.proto\x12\x15\x61\x63tion_server_webhook\x1a\x19google/protobuf/any.proto\"\xf3\x04\n\x07Tracker\x12\x17\n\x0f\x63onversation_id\x18\x01 \x01(\t\x12\x38\n\x05slots\x18\x02 \x03(\x0b\x32).action_server_webhook.Tracker.SlotsEntry\x12I\n\x0elatest_message\x18\x03 \x03(\x0b\x32\x31.action_server_webhook.Tracker.LatestMessageEntry\x12\x19\n\x11latest_event_time\x18\x04 \x01(\x01\x12\x17\n\x0f\x66ollowup_action\x18\x05 \x01(\t\x12\x0e\n\x06paused\x18\x06 \x01(\x08\x12\x0e\n\x06\x65vents\x18\x07 \x03(\t\x12\x1c\n\x14latest_input_channel\x18\x08 \x01(\t\x12\x43\n\x0b\x61\x63tive_loop\x18\t \x03(\x0b\x32..action_server_webhook.Tracker.ActiveLoopEntry\x12G\n\rlatest_action\x18\n \x03(\x0b\x32\x30.action_server_webhook.Tracker.LatestActionEntry\x1a,\n\nSlotsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x34\n\x12LatestMessageEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x31\n\x0f\x41\x63tiveLoopEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11LatestActionEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xfc\x04\n\x06\x44omain\x12\x39\n\x06\x63onfig\x18\x01 \x03(\x0b\x32).action_server_webhook.Domain.ConfigEntry\x12H\n\x0esession_config\x18\x02 \x03(\x0b\x32\x30.action_server_webhook.Domain.SessionConfigEntry\x12\x0f\n\x07intents\x18\x03 \x03(\t\x12\x10\n\x08\x65ntities\x18\x04 \x03(\t\x12\x37\n\x05slots\x18\x05 \x03(\x0b\x32(.action_server_webhook.Domain.SlotsEntry\x12?\n\tresponses\x18\x06 \x03(\x0b\x32,.action_server_webhook.Domain.ResponsesEntry\x12\x0f\n\x07\x61\x63tions\x18\x07 \x03(\t\x12\x37\n\x05\x66orms\x18\x08 \x03(\x0b\x32(.action_server_webhook.Domain.FormsEntry\x12\x13\n\x0b\x65\x32\x65_actions\x18\t \x03(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x34\n\x12SessionConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a,\n\nSlotsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eResponsesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a,\n\nFormsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa9\x01\n\x0eWebhookRequest\x12\x13\n\x0bnext_action\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12/\n\x07tracker\x18\x03 \x01(\x0b\x32\x1e.action_server_webhook.Tracker\x12-\n\x06\x64omain\x18\x04 \x01(\x0b\x32\x1d.action_server_webhook.Domain\x12\x0f\n\x07version\x18\x05 \x01(\t\"`\n\x0fWebhookResponse\x12$\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x14.google.protobuf.Any\x12\'\n\tresponses\x18\x02 \x03(\x0b\x32\x14.google.protobuf.Any2o\n\x13\x41\x63tionServerWebhook\x12X\n\x07webhook\x12%.action_server_webhook.WebhookRequest\x1a&.action_server_webhook.WebhookResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rasa_sdk.grpc_py.action_webhook_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _TRACKER_SLOTSENTRY._options = None + _TRACKER_SLOTSENTRY._serialized_options = b'8\001' + _TRACKER_LATESTMESSAGEENTRY._options = None + _TRACKER_LATESTMESSAGEENTRY._serialized_options = b'8\001' + _TRACKER_ACTIVELOOPENTRY._options = None + _TRACKER_ACTIVELOOPENTRY._serialized_options = b'8\001' + _TRACKER_LATESTACTIONENTRY._options = None + _TRACKER_LATESTACTIONENTRY._serialized_options = b'8\001' + _DOMAIN_CONFIGENTRY._options = None + _DOMAIN_CONFIGENTRY._serialized_options = b'8\001' + _DOMAIN_SESSIONCONFIGENTRY._options = None + _DOMAIN_SESSIONCONFIGENTRY._serialized_options = b'8\001' + _DOMAIN_SLOTSENTRY._options = None + _DOMAIN_SLOTSENTRY._serialized_options = b'8\001' + _DOMAIN_RESPONSESENTRY._options = None + _DOMAIN_RESPONSESENTRY._serialized_options = b'8\001' + _DOMAIN_FORMSENTRY._options = None + _DOMAIN_FORMSENTRY._serialized_options = b'8\001' + _globals['_TRACKER']._serialized_start=92 + _globals['_TRACKER']._serialized_end=719 + _globals['_TRACKER_SLOTSENTRY']._serialized_start=517 + _globals['_TRACKER_SLOTSENTRY']._serialized_end=561 + _globals['_TRACKER_LATESTMESSAGEENTRY']._serialized_start=563 + _globals['_TRACKER_LATESTMESSAGEENTRY']._serialized_end=615 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_start=617 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_end=666 + _globals['_TRACKER_LATESTACTIONENTRY']._serialized_start=668 + _globals['_TRACKER_LATESTACTIONENTRY']._serialized_end=719 + _globals['_DOMAIN']._serialized_start=722 + _globals['_DOMAIN']._serialized_end=1358 + _globals['_DOMAIN_CONFIGENTRY']._serialized_start=1117 + _globals['_DOMAIN_CONFIGENTRY']._serialized_end=1162 + _globals['_DOMAIN_SESSIONCONFIGENTRY']._serialized_start=1164 + _globals['_DOMAIN_SESSIONCONFIGENTRY']._serialized_end=1216 + _globals['_DOMAIN_SLOTSENTRY']._serialized_start=517 + _globals['_DOMAIN_SLOTSENTRY']._serialized_end=561 + _globals['_DOMAIN_RESPONSESENTRY']._serialized_start=1264 + _globals['_DOMAIN_RESPONSESENTRY']._serialized_end=1312 + _globals['_DOMAIN_FORMSENTRY']._serialized_start=1314 + _globals['_DOMAIN_FORMSENTRY']._serialized_end=1358 + _globals['_WEBHOOKREQUEST']._serialized_start=1361 + _globals['_WEBHOOKREQUEST']._serialized_end=1530 + _globals['_WEBHOOKRESPONSE']._serialized_start=1532 + _globals['_WEBHOOKRESPONSE']._serialized_end=1628 + _globals['_ACTIONSERVERWEBHOOK']._serialized_start=1630 + _globals['_ACTIONSERVERWEBHOOK']._serialized_end=1741 +# @@protoc_insertion_point(module_scope) diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.pyi b/rasa_sdk/grpc_py/action_webhook_pb2.pyi new file mode 100644 index 000000000..23caab556 --- /dev/null +++ b/rasa_sdk/grpc_py/action_webhook_pb2.pyi @@ -0,0 +1,138 @@ +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Tracker(_message.Message): + __slots__ = ["conversation_id", "slots", "latest_message", "latest_event_time", "followup_action", "paused", "events", "latest_input_channel", "active_loop", "latest_action"] + class SlotsEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class LatestMessageEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class ActiveLoopEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class LatestActionEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CONVERSATION_ID_FIELD_NUMBER: _ClassVar[int] + SLOTS_FIELD_NUMBER: _ClassVar[int] + LATEST_MESSAGE_FIELD_NUMBER: _ClassVar[int] + LATEST_EVENT_TIME_FIELD_NUMBER: _ClassVar[int] + FOLLOWUP_ACTION_FIELD_NUMBER: _ClassVar[int] + PAUSED_FIELD_NUMBER: _ClassVar[int] + EVENTS_FIELD_NUMBER: _ClassVar[int] + LATEST_INPUT_CHANNEL_FIELD_NUMBER: _ClassVar[int] + ACTIVE_LOOP_FIELD_NUMBER: _ClassVar[int] + LATEST_ACTION_FIELD_NUMBER: _ClassVar[int] + conversation_id: str + slots: _containers.ScalarMap[str, str] + latest_message: _containers.ScalarMap[str, str] + latest_event_time: float + followup_action: str + paused: bool + events: _containers.RepeatedScalarFieldContainer[str] + latest_input_channel: str + active_loop: _containers.ScalarMap[str, str] + latest_action: _containers.ScalarMap[str, str] + def __init__(self, conversation_id: _Optional[str] = ..., slots: _Optional[_Mapping[str, str]] = ..., latest_message: _Optional[_Mapping[str, str]] = ..., latest_event_time: _Optional[float] = ..., followup_action: _Optional[str] = ..., paused: bool = ..., events: _Optional[_Iterable[str]] = ..., latest_input_channel: _Optional[str] = ..., active_loop: _Optional[_Mapping[str, str]] = ..., latest_action: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class Domain(_message.Message): + __slots__ = ["config", "session_config", "intents", "entities", "slots", "responses", "actions", "forms", "e2e_actions"] + class ConfigEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class SessionConfigEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class SlotsEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class ResponsesEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class FormsEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CONFIG_FIELD_NUMBER: _ClassVar[int] + SESSION_CONFIG_FIELD_NUMBER: _ClassVar[int] + INTENTS_FIELD_NUMBER: _ClassVar[int] + ENTITIES_FIELD_NUMBER: _ClassVar[int] + SLOTS_FIELD_NUMBER: _ClassVar[int] + RESPONSES_FIELD_NUMBER: _ClassVar[int] + ACTIONS_FIELD_NUMBER: _ClassVar[int] + FORMS_FIELD_NUMBER: _ClassVar[int] + E2E_ACTIONS_FIELD_NUMBER: _ClassVar[int] + config: _containers.ScalarMap[str, str] + session_config: _containers.ScalarMap[str, str] + intents: _containers.RepeatedScalarFieldContainer[str] + entities: _containers.RepeatedScalarFieldContainer[str] + slots: _containers.ScalarMap[str, str] + responses: _containers.ScalarMap[str, str] + actions: _containers.RepeatedScalarFieldContainer[str] + forms: _containers.ScalarMap[str, str] + e2e_actions: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, config: _Optional[_Mapping[str, str]] = ..., session_config: _Optional[_Mapping[str, str]] = ..., intents: _Optional[_Iterable[str]] = ..., entities: _Optional[_Iterable[str]] = ..., slots: _Optional[_Mapping[str, str]] = ..., responses: _Optional[_Mapping[str, str]] = ..., actions: _Optional[_Iterable[str]] = ..., forms: _Optional[_Mapping[str, str]] = ..., e2e_actions: _Optional[_Iterable[str]] = ...) -> None: ... + +class WebhookRequest(_message.Message): + __slots__ = ["next_action", "sender_id", "tracker", "domain", "version"] + NEXT_ACTION_FIELD_NUMBER: _ClassVar[int] + SENDER_ID_FIELD_NUMBER: _ClassVar[int] + TRACKER_FIELD_NUMBER: _ClassVar[int] + DOMAIN_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + next_action: str + sender_id: str + tracker: Tracker + domain: Domain + version: str + def __init__(self, next_action: _Optional[str] = ..., sender_id: _Optional[str] = ..., tracker: _Optional[_Union[Tracker, _Mapping]] = ..., domain: _Optional[_Union[Domain, _Mapping]] = ..., version: _Optional[str] = ...) -> None: ... + +class WebhookResponse(_message.Message): + __slots__ = ["events", "responses"] + EVENTS_FIELD_NUMBER: _ClassVar[int] + RESPONSES_FIELD_NUMBER: _ClassVar[int] + events: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + responses: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + def __init__(self, events: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., responses: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ...) -> None: ... diff --git a/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py b/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py new file mode 100644 index 000000000..215a4062a --- /dev/null +++ b/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from rasa_sdk.grpc_py import action_webhook_pb2 as rasa__sdk_dot_grpc__py_dot_action__webhook__pb2 + + +class ActionServerWebhookStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.webhook = channel.unary_unary( + '/action_server_webhook.ActionServerWebhook/webhook', + request_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.SerializeToString, + response_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.FromString, + ) + + +class ActionServerWebhookServicer(object): + """Missing associated documentation comment in .proto file.""" + + def webhook(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_ActionServerWebhookServicer_to_server(servicer, server): + rpc_method_handlers = { + 'webhook': grpc.unary_unary_rpc_method_handler( + servicer.webhook, + request_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.FromString, + response_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'action_server_webhook.ActionServerWebhook', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class ActionServerWebhook(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def webhook(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/action_server_webhook.ActionServerWebhook/webhook', + rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.SerializeToString, + rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index e69de29bb..78b015de4 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -0,0 +1,110 @@ +import grpc +import utils +import logging +import ssl +import types +from typing import Text, Union, Optional +from concurrent import futures +from grpc import aio +from google.protobuf.json_format import MessageToDict, ParseDict + +from rasa_sdk.constants import DEFAULT_SERVER_PORT, DEFAULT_ENDPOINTS_PATH +from rasa_sdk.executor import ActionExecutor +from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc +from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest +from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException +from rasa_sdk.tracing.utils import ( + get_tracer_and_context, + TracerProvider, + get_tracer_provider, +) + +logger = logging.getLogger(__name__) + + +class ActionServerWebhook(action_webhook_pb2_grpc.ActionServerWebhookServicer): + def __init__( + self, + executor: ActionExecutor, + tracer_provider: Optional[TracerProvider] = None, + ) -> None: + """Initializes the ActionServerWebhook. + + Args: + tracer_provider: The tracer provider. + executor: The action executor. + """ + self.tracer_provider = tracer_provider + self.executor = executor + + async def webhook(self, request: WebhookRequest, context): + tracer, context, span_name = get_tracer_and_context( + self.tracer_provider, request + ) + with tracer.start_as_current_span(span_name, context=context) as span: + utils.check_version_compatibility(request.version) + try: + action_call = MessageToDict(request) + result = await self.executor.run(action_call) + except ActionExecutionRejection as e: + logger.debug(e) + body = {"error": e.message, "action_name": e.action_name} + context.set_code(grpc.StatusCode.INTERNAL) + context.set_details(str(body)) + return action_webhook_pb2.WebhookResponse() + except ActionNotFoundException as e: + logger.error(e) + body = {"error": e.message, "action_name": e.action_name} + context.set_code(grpc.StatusCode.NOTFOUND) + context.set_details(str(body)) + return action_webhook_pb2.WebhookResponse() + if not result: + return action_webhook_pb2.WebhookResponse() + # set_span_attributes(span, request) + response = action_webhook_pb2.WebhookResponse() + + return ParseDict(result, response) + + +def get_ssl_password_callback(ssl_password): + def password_callback(*args, **kwargs): + return ssl_password.encode() if ssl_password else None + + return password_callback + + +async def run_grpc( + action_package_name: Union[Text, types.ModuleType], + port: int = DEFAULT_SERVER_PORT, + ssl_certificate: Optional[Text] = None, + ssl_keyfile: Optional[Text] = None, + ssl_password: Optional[Text] = None, + endpoints: str = DEFAULT_ENDPOINTS_PATH, +): + workers = utils.number_of_sanic_workers() + server = aio.server(futures.ThreadPoolExecutor(max_workers=workers)) + executor = ActionExecutor() + executor.register_package(action_package_name) + # tracer_provider = get_tracer_provider(endpoints) + tracer_provider = None + action_webhook_pb2_grpc.add_ActionServerWebhookServicer_to_server( + ActionServerWebhook(executor, tracer_provider), server + ) + if ssl_certificate and ssl_keyfile: + # Use SSL/TLS if certificate and key are provided + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ssl_context.load_cert_chain( + ssl_certificate, + keyfile=ssl_keyfile, + password=get_ssl_password_callback(ssl_password), + ) + server.add_secure_port( + f"[::]:{port}", server_credentials=grpc.ssl_server_credentials(ssl_context) + ) + else: + # Use insecure connection if no SSL/TLS information is provided + server.add_insecure_port(f"[::]:{port}") + + await server.start() + print(f"gRPC Server started on port {port}") + await server.wait_for_termination() diff --git a/rasa_sdk/tracing/utils.py b/rasa_sdk/tracing/utils.py index 32b759b24..bbf56374c 100644 --- a/rasa_sdk/tracing/utils.py +++ b/rasa_sdk/tracing/utils.py @@ -1,3 +1,4 @@ +from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest from rasa_sdk.tracing import config from opentelemetry import trace from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator @@ -5,7 +6,7 @@ from opentelemetry.sdk.trace import TracerProvider from sanic.request import Request -from typing import Optional, Tuple, Any, Text +from typing import Optional, Tuple, Any, Text, Union def get_tracer_provider(endpoints_file: str) -> Optional[TracerProvider]: @@ -17,11 +18,12 @@ def get_tracer_provider(endpoints_file: str) -> Optional[TracerProvider]: def get_tracer_and_context( - tracer_provider: Optional[TracerProvider], request: Request + tracer_provider: Optional[TracerProvider], request: Union[Request, WebhookRequest] ) -> Tuple[Any, Any, Text]: """Gets tracer and context.""" span_name = "create_app.webhook" - if tracer_provider is None: + + if tracer_provider is None or isinstance(request, WebhookRequest): tracer = trace.get_tracer(span_name) context = None else: From 01e786b8893d4c2a149d80fda7109c68ec054bd7 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Thu, 13 Jun 2024 15:00:21 +0200 Subject: [PATCH 03/19] Update grpcio to 1.59.3 --- poetry.lock | 108 ++++++++++++++++++++++++++----------------------- pyproject.toml | 2 +- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/poetry.lock b/poetry.lock index e603df7b7..ee3bfb215 100644 --- a/poetry.lock +++ b/poetry.lock @@ -384,61 +384,69 @@ grpc = ["grpcio (>=1.0.0)"] [[package]] name = "grpcio" -version = "1.64.1" +version = "1.59.3" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "grpcio-1.64.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:55697ecec192bc3f2f3cc13a295ab670f51de29884ca9ae6cd6247df55df2502"}, - {file = "grpcio-1.64.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3b64ae304c175671efdaa7ec9ae2cc36996b681eb63ca39c464958396697daff"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:bac71b4b28bc9af61efcdc7630b166440bbfbaa80940c9a697271b5e1dabbc61"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c024ffc22d6dc59000faf8ad781696d81e8e38f4078cb0f2630b4a3cf231a90"}, - {file = "grpcio-1.64.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7cd5c1325f6808b8ae31657d281aadb2a51ac11ab081ae335f4f7fc44c1721d"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0a2813093ddb27418a4c99f9b1c223fab0b053157176a64cc9db0f4557b69bd9"}, - {file = "grpcio-1.64.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2981c7365a9353f9b5c864595c510c983251b1ab403e05b1ccc70a3d9541a73b"}, - {file = "grpcio-1.64.1-cp310-cp310-win32.whl", hash = "sha256:1262402af5a511c245c3ae918167eca57342c72320dffae5d9b51840c4b2f86d"}, - {file = "grpcio-1.64.1-cp310-cp310-win_amd64.whl", hash = "sha256:19264fc964576ddb065368cae953f8d0514ecc6cb3da8903766d9fb9d4554c33"}, - {file = "grpcio-1.64.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:58b1041e7c870bb30ee41d3090cbd6f0851f30ae4eb68228955d973d3efa2e61"}, - {file = "grpcio-1.64.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc5b1d78a7822b0a84c6f8917faa986c1a744e65d762ef6d8be9d75677af2ca"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5841dd1f284bd1b3d8a6eca3a7f062b06f1eec09b184397e1d1d43447e89a7ae"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8caee47e970b92b3dd948371230fcceb80d3f2277b3bf7fbd7c0564e7d39068e"}, - {file = "grpcio-1.64.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73819689c169417a4f978e562d24f2def2be75739c4bed1992435d007819da1b"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6503b64c8b2dfad299749cad1b595c650c91e5b2c8a1b775380fcf8d2cbba1e9"}, - {file = "grpcio-1.64.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1de403fc1305fd96cfa75e83be3dee8538f2413a6b1685b8452301c7ba33c294"}, - {file = "grpcio-1.64.1-cp311-cp311-win32.whl", hash = "sha256:d4d29cc612e1332237877dfa7fe687157973aab1d63bd0f84cf06692f04c0367"}, - {file = "grpcio-1.64.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e56462b05a6f860b72f0fa50dca06d5b26543a4e88d0396259a07dc30f4e5aa"}, - {file = "grpcio-1.64.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:4657d24c8063e6095f850b68f2d1ba3b39f2b287a38242dcabc166453e950c59"}, - {file = "grpcio-1.64.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:62b4e6eb7bf901719fce0ca83e3ed474ae5022bb3827b0a501e056458c51c0a1"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ee73a2f5ca4ba44fa33b4d7d2c71e2c8a9e9f78d53f6507ad68e7d2ad5f64a22"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:198908f9b22e2672a998870355e226a725aeab327ac4e6ff3a1399792ece4762"}, - {file = "grpcio-1.64.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39b9d0acaa8d835a6566c640f48b50054f422d03e77e49716d4c4e8e279665a1"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5e42634a989c3aa6049f132266faf6b949ec2a6f7d302dbb5c15395b77d757eb"}, - {file = "grpcio-1.64.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b1a82e0b9b3022799c336e1fc0f6210adc019ae84efb7321d668129d28ee1efb"}, - {file = "grpcio-1.64.1-cp312-cp312-win32.whl", hash = "sha256:55260032b95c49bee69a423c2f5365baa9369d2f7d233e933564d8a47b893027"}, - {file = "grpcio-1.64.1-cp312-cp312-win_amd64.whl", hash = "sha256:c1a786ac592b47573a5bb7e35665c08064a5d77ab88a076eec11f8ae86b3e3f6"}, - {file = "grpcio-1.64.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:a011ac6c03cfe162ff2b727bcb530567826cec85eb8d4ad2bfb4bd023287a52d"}, - {file = "grpcio-1.64.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4d6dab6124225496010bd22690f2d9bd35c7cbb267b3f14e7a3eb05c911325d4"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a5e771d0252e871ce194d0fdcafd13971f1aae0ddacc5f25615030d5df55c3a2"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c3c1b90ab93fed424e454e93c0ed0b9d552bdf1b0929712b094f5ecfe7a23ad"}, - {file = "grpcio-1.64.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20405cb8b13fd779135df23fabadc53b86522d0f1cba8cca0e87968587f50650"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0cc79c982ccb2feec8aad0e8fb0d168bcbca85bc77b080d0d3c5f2f15c24ea8f"}, - {file = "grpcio-1.64.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a3a035c37ce7565b8f4f35ff683a4db34d24e53dc487e47438e434eb3f701b2a"}, - {file = "grpcio-1.64.1-cp38-cp38-win32.whl", hash = "sha256:1257b76748612aca0f89beec7fa0615727fd6f2a1ad580a9638816a4b2eb18fd"}, - {file = "grpcio-1.64.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a12ddb1678ebc6a84ec6b0487feac020ee2b1659cbe69b80f06dbffdb249122"}, - {file = "grpcio-1.64.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:75dbbf415026d2862192fe1b28d71f209e2fd87079d98470db90bebe57b33179"}, - {file = "grpcio-1.64.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e3d9f8d1221baa0ced7ec7322a981e28deb23749c76eeeb3d33e18b72935ab62"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f8b75f64d5d324c565b263c67dbe4f0af595635bbdd93bb1a88189fc62ed2e5"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c84ad903d0d94311a2b7eea608da163dace97c5fe9412ea311e72c3684925602"}, - {file = "grpcio-1.64.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:940e3ec884520155f68a3b712d045e077d61c520a195d1a5932c531f11883489"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f10193c69fc9d3d726e83bbf0f3d316f1847c3071c8c93d8090cf5f326b14309"}, - {file = "grpcio-1.64.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ac15b6c2c80a4d1338b04d42a02d376a53395ddf0ec9ab157cbaf44191f3ffdd"}, - {file = "grpcio-1.64.1-cp39-cp39-win32.whl", hash = "sha256:03b43d0ccf99c557ec671c7dede64f023c7da9bb632ac65dbc57f166e4970040"}, - {file = "grpcio-1.64.1-cp39-cp39-win_amd64.whl", hash = "sha256:ed6091fa0adcc7e4ff944090cf203a52da35c37a130efa564ded02b7aff63bcd"}, - {file = "grpcio-1.64.1.tar.gz", hash = "sha256:8d51dd1c59d5fa0f34266b80a3805ec29a1f26425c2a54736133f6d87fc4968a"}, + {file = "grpcio-1.59.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc"}, + {file = "grpcio-1.59.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8"}, + {file = "grpcio-1.59.3-cp310-cp310-win32.whl", hash = "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab"}, + {file = "grpcio-1.59.3-cp310-cp310-win_amd64.whl", hash = "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a"}, + {file = "grpcio-1.59.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86"}, + {file = "grpcio-1.59.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473"}, + {file = "grpcio-1.59.3-cp311-cp311-win32.whl", hash = "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3"}, + {file = "grpcio-1.59.3-cp311-cp311-win_amd64.whl", hash = "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9"}, + {file = "grpcio-1.59.3-cp312-cp312-linux_armv7l.whl", hash = "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7"}, + {file = "grpcio-1.59.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718"}, + {file = "grpcio-1.59.3-cp312-cp312-win32.whl", hash = "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79"}, + {file = "grpcio-1.59.3-cp312-cp312-win_amd64.whl", hash = "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07"}, + {file = "grpcio-1.59.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94"}, + {file = "grpcio-1.59.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb"}, + {file = "grpcio-1.59.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e"}, + {file = "grpcio-1.59.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556"}, + {file = "grpcio-1.59.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881"}, + {file = "grpcio-1.59.3-cp38-cp38-win32.whl", hash = "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0"}, + {file = "grpcio-1.59.3-cp38-cp38-win_amd64.whl", hash = "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391"}, + {file = "grpcio-1.59.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552"}, + {file = "grpcio-1.59.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7"}, + {file = "grpcio-1.59.3-cp39-cp39-win32.whl", hash = "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281"}, + {file = "grpcio-1.59.3-cp39-cp39-win_amd64.whl", hash = "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e"}, + {file = "grpcio-1.59.3.tar.gz", hash = "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.64.1)"] +protobuf = ["grpcio-tools (>=1.59.3)"] [[package]] name = "grpcio-tools" @@ -1911,4 +1919,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "6fab19810735e6ca4cd28bc038f641e64545379904ba604b9f405088d0e2bfbe" +content-hash = "f2dc2ff13fb249529e99c45faa6a4f309b33dcec8245c7e04c78c0e651566ef2" diff --git a/pyproject.toml b/pyproject.toml index 6dfc4372b..658dd4b56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,7 @@ opentelemetry-api = "~1.15.0" opentelemetry-sdk = "~1.15.0" opentelemetry-exporter-jaeger = "~1.15.0" opentelemetry-exporter-otlp = "~1.15.0" -grpcio = "1.64.1" +grpcio = "1.59.3" protobuf = "4.25.3" grpcio-tools = "1.56.2" From 7605fe859c281129418960b2390590e3c0be7737 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Fri, 14 Jun 2024 00:37:16 +0200 Subject: [PATCH 04/19] Update protobuf definition --- proto/action_webhook.proto | 59 ++++++---- rasa_sdk/grpc_py/action_webhook_pb2.py | 66 ++++------- rasa_sdk/grpc_py/action_webhook_pb2.pyi | 144 +++++++++--------------- rasa_sdk/grpc_server.py | 4 +- rasa_sdk/interfaces.py | 4 +- 5 files changed, 118 insertions(+), 159 deletions(-) diff --git a/proto/action_webhook.proto b/proto/action_webhook.proto index 19ce1d979..9cae517b1 100644 --- a/proto/action_webhook.proto +++ b/proto/action_webhook.proto @@ -1,35 +1,49 @@ syntax = "proto3"; package action_server_webhook; -import "google/protobuf/any.proto"; +import "google/protobuf/struct.proto"; service ActionServerWebhook { rpc webhook (WebhookRequest) returns (WebhookResponse); } message Tracker { - string conversation_id = 1; - map slots = 2; - map latest_message = 3; - double latest_event_time = 4; - string followup_action = 5; - bool paused = 6; - repeated string events = 7; - string latest_input_channel = 8; - map active_loop = 9; - map latest_action = 10; + string sender_id = 1; + google.protobuf.Struct slots = 2; + google.protobuf.Struct latest_message = 3; + repeated google.protobuf.Struct events = 4; + bool paused = 5; + optional string followup_action = 6; + map active_loop = 7; + optional string latest_action_name = 8; + repeated google.protobuf.Struct stack = 9; +} + +message Intent { + string string_value = 1; + google.protobuf.Struct dict_value = 2; +} + +message Entity { + string string_value = 1; + google.protobuf.Struct dict_value = 2; +} + +message Action { + string string_value = 1; + google.protobuf.Struct dict_value = 2; } message Domain { - map config = 1; - map session_config = 2; - repeated string intents = 3; - repeated string entities = 4; - map slots = 5; - map responses = 6; - repeated string actions = 7; - map forms = 8; - repeated string e2e_actions = 9; + google.protobuf.Struct config = 1; + google.protobuf.Struct session_config = 2; + repeated Intent intents = 3; + repeated Entity entities = 4; + google.protobuf.Struct slots = 5; + google.protobuf.Struct responses = 6; + repeated Action actions = 7; + google.protobuf.Struct forms = 8; + repeated google.protobuf.Struct e2e_actions = 9; } message WebhookRequest { @@ -38,9 +52,10 @@ message WebhookRequest { Tracker tracker = 3; Domain domain = 4; string version = 5; + optional string domain_digest = 6; } message WebhookResponse { - repeated google.protobuf.Any events = 1; - repeated google.protobuf.Any responses = 2; + repeated google.protobuf.Struct events = 1; + repeated google.protobuf.Struct responses = 2; } \ No newline at end of file diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.py b/rasa_sdk/grpc_py/action_webhook_pb2.py index 9bb03ad36..b8b7b1101 100644 --- a/rasa_sdk/grpc_py/action_webhook_pb2.py +++ b/rasa_sdk/grpc_py/action_webhook_pb2.py @@ -11,10 +11,10 @@ _sym_db = _symbol_database.Default() -from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%rasa_sdk/grpc_py/action_webhook.proto\x12\x15\x61\x63tion_server_webhook\x1a\x19google/protobuf/any.proto\"\xf3\x04\n\x07Tracker\x12\x17\n\x0f\x63onversation_id\x18\x01 \x01(\t\x12\x38\n\x05slots\x18\x02 \x03(\x0b\x32).action_server_webhook.Tracker.SlotsEntry\x12I\n\x0elatest_message\x18\x03 \x03(\x0b\x32\x31.action_server_webhook.Tracker.LatestMessageEntry\x12\x19\n\x11latest_event_time\x18\x04 \x01(\x01\x12\x17\n\x0f\x66ollowup_action\x18\x05 \x01(\t\x12\x0e\n\x06paused\x18\x06 \x01(\x08\x12\x0e\n\x06\x65vents\x18\x07 \x03(\t\x12\x1c\n\x14latest_input_channel\x18\x08 \x01(\t\x12\x43\n\x0b\x61\x63tive_loop\x18\t \x03(\x0b\x32..action_server_webhook.Tracker.ActiveLoopEntry\x12G\n\rlatest_action\x18\n \x03(\x0b\x32\x30.action_server_webhook.Tracker.LatestActionEntry\x1a,\n\nSlotsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x34\n\x12LatestMessageEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x31\n\x0f\x41\x63tiveLoopEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11LatestActionEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xfc\x04\n\x06\x44omain\x12\x39\n\x06\x63onfig\x18\x01 \x03(\x0b\x32).action_server_webhook.Domain.ConfigEntry\x12H\n\x0esession_config\x18\x02 \x03(\x0b\x32\x30.action_server_webhook.Domain.SessionConfigEntry\x12\x0f\n\x07intents\x18\x03 \x03(\t\x12\x10\n\x08\x65ntities\x18\x04 \x03(\t\x12\x37\n\x05slots\x18\x05 \x03(\x0b\x32(.action_server_webhook.Domain.SlotsEntry\x12?\n\tresponses\x18\x06 \x03(\x0b\x32,.action_server_webhook.Domain.ResponsesEntry\x12\x0f\n\x07\x61\x63tions\x18\x07 \x03(\t\x12\x37\n\x05\x66orms\x18\x08 \x03(\x0b\x32(.action_server_webhook.Domain.FormsEntry\x12\x13\n\x0b\x65\x32\x65_actions\x18\t \x03(\t\x1a-\n\x0b\x43onfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x34\n\x12SessionConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a,\n\nSlotsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0eResponsesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a,\n\nFormsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa9\x01\n\x0eWebhookRequest\x12\x13\n\x0bnext_action\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12/\n\x07tracker\x18\x03 \x01(\x0b\x32\x1e.action_server_webhook.Tracker\x12-\n\x06\x64omain\x18\x04 \x01(\x0b\x32\x1d.action_server_webhook.Domain\x12\x0f\n\x07version\x18\x05 \x01(\t\"`\n\x0fWebhookResponse\x12$\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x14.google.protobuf.Any\x12\'\n\tresponses\x18\x02 \x03(\x0b\x32\x14.google.protobuf.Any2o\n\x13\x41\x63tionServerWebhook\x12X\n\x07webhook\x12%.action_server_webhook.WebhookRequest\x1a&.action_server_webhook.WebhookResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%rasa_sdk/grpc_py/action_webhook.proto\x12\x15\x61\x63tion_server_webhook\x1a\x1cgoogle/protobuf/struct.proto\"\xb8\x03\n\x07Tracker\x12\x11\n\tsender_id\x18\x01 \x01(\t\x12&\n\x05slots\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0elatest_message\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65vents\x18\x04 \x03(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06paused\x18\x05 \x01(\x08\x12\x1c\n\x0f\x66ollowup_action\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x43\n\x0b\x61\x63tive_loop\x18\x07 \x03(\x0b\x32..action_server_webhook.Tracker.ActiveLoopEntry\x12\x1f\n\x12latest_action_name\x18\x08 \x01(\tH\x01\x88\x01\x01\x12&\n\x05stack\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\x1a\x31\n\x0f\x41\x63tiveLoopEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x12\n\x10_followup_actionB\x15\n\x13_latest_action_name\"K\n\x06Intent\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x45ntity\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x41\x63tion\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x9d\x03\n\x06\x44omain\x12\'\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0esession_config\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07intents\x18\x03 \x03(\x0b\x32\x1d.action_server_webhook.Intent\x12/\n\x08\x65ntities\x18\x04 \x03(\x0b\x32\x1d.action_server_webhook.Entity\x12&\n\x05slots\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07\x61\x63tions\x18\x07 \x03(\x0b\x32\x1d.action_server_webhook.Action\x12&\n\x05\x66orms\x18\x08 \x01(\x0b\x32\x17.google.protobuf.Struct\x12,\n\x0b\x65\x32\x65_actions\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\"\xd7\x01\n\x0eWebhookRequest\x12\x13\n\x0bnext_action\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12/\n\x07tracker\x18\x03 \x01(\x0b\x32\x1e.action_server_webhook.Tracker\x12-\n\x06\x64omain\x18\x04 \x01(\x0b\x32\x1d.action_server_webhook.Domain\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x1a\n\rdomain_digest\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x10\n\x0e_domain_digest\"f\n\x0fWebhookResponse\x12\'\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Struct2o\n\x13\x41\x63tionServerWebhook\x12X\n\x07webhook\x12%.action_server_webhook.WebhookRequest\x1a&.action_server_webhook.WebhookResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,50 +22,24 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None - _TRACKER_SLOTSENTRY._options = None - _TRACKER_SLOTSENTRY._serialized_options = b'8\001' - _TRACKER_LATESTMESSAGEENTRY._options = None - _TRACKER_LATESTMESSAGEENTRY._serialized_options = b'8\001' _TRACKER_ACTIVELOOPENTRY._options = None _TRACKER_ACTIVELOOPENTRY._serialized_options = b'8\001' - _TRACKER_LATESTACTIONENTRY._options = None - _TRACKER_LATESTACTIONENTRY._serialized_options = b'8\001' - _DOMAIN_CONFIGENTRY._options = None - _DOMAIN_CONFIGENTRY._serialized_options = b'8\001' - _DOMAIN_SESSIONCONFIGENTRY._options = None - _DOMAIN_SESSIONCONFIGENTRY._serialized_options = b'8\001' - _DOMAIN_SLOTSENTRY._options = None - _DOMAIN_SLOTSENTRY._serialized_options = b'8\001' - _DOMAIN_RESPONSESENTRY._options = None - _DOMAIN_RESPONSESENTRY._serialized_options = b'8\001' - _DOMAIN_FORMSENTRY._options = None - _DOMAIN_FORMSENTRY._serialized_options = b'8\001' - _globals['_TRACKER']._serialized_start=92 - _globals['_TRACKER']._serialized_end=719 - _globals['_TRACKER_SLOTSENTRY']._serialized_start=517 - _globals['_TRACKER_SLOTSENTRY']._serialized_end=561 - _globals['_TRACKER_LATESTMESSAGEENTRY']._serialized_start=563 - _globals['_TRACKER_LATESTMESSAGEENTRY']._serialized_end=615 - _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_start=617 - _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_end=666 - _globals['_TRACKER_LATESTACTIONENTRY']._serialized_start=668 - _globals['_TRACKER_LATESTACTIONENTRY']._serialized_end=719 - _globals['_DOMAIN']._serialized_start=722 - _globals['_DOMAIN']._serialized_end=1358 - _globals['_DOMAIN_CONFIGENTRY']._serialized_start=1117 - _globals['_DOMAIN_CONFIGENTRY']._serialized_end=1162 - _globals['_DOMAIN_SESSIONCONFIGENTRY']._serialized_start=1164 - _globals['_DOMAIN_SESSIONCONFIGENTRY']._serialized_end=1216 - _globals['_DOMAIN_SLOTSENTRY']._serialized_start=517 - _globals['_DOMAIN_SLOTSENTRY']._serialized_end=561 - _globals['_DOMAIN_RESPONSESENTRY']._serialized_start=1264 - _globals['_DOMAIN_RESPONSESENTRY']._serialized_end=1312 - _globals['_DOMAIN_FORMSENTRY']._serialized_start=1314 - _globals['_DOMAIN_FORMSENTRY']._serialized_end=1358 - _globals['_WEBHOOKREQUEST']._serialized_start=1361 - _globals['_WEBHOOKREQUEST']._serialized_end=1530 - _globals['_WEBHOOKRESPONSE']._serialized_start=1532 - _globals['_WEBHOOKRESPONSE']._serialized_end=1628 - _globals['_ACTIONSERVERWEBHOOK']._serialized_start=1630 - _globals['_ACTIONSERVERWEBHOOK']._serialized_end=1741 + _globals['_TRACKER']._serialized_start=95 + _globals['_TRACKER']._serialized_end=535 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_start=443 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_end=492 + _globals['_INTENT']._serialized_start=537 + _globals['_INTENT']._serialized_end=612 + _globals['_ENTITY']._serialized_start=614 + _globals['_ENTITY']._serialized_end=689 + _globals['_ACTION']._serialized_start=691 + _globals['_ACTION']._serialized_end=766 + _globals['_DOMAIN']._serialized_start=769 + _globals['_DOMAIN']._serialized_end=1182 + _globals['_WEBHOOKREQUEST']._serialized_start=1185 + _globals['_WEBHOOKREQUEST']._serialized_end=1400 + _globals['_WEBHOOKRESPONSE']._serialized_start=1402 + _globals['_WEBHOOKRESPONSE']._serialized_end=1504 + _globals['_ACTIONSERVERWEBHOOK']._serialized_start=1506 + _globals['_ACTIONSERVERWEBHOOK']._serialized_end=1617 # @@protoc_insertion_point(module_scope) diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.pyi b/rasa_sdk/grpc_py/action_webhook_pb2.pyi index 23caab556..566596e3b 100644 --- a/rasa_sdk/grpc_py/action_webhook_pb2.pyi +++ b/rasa_sdk/grpc_py/action_webhook_pb2.pyi @@ -1,4 +1,4 @@ -from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import struct_pb2 as _struct_pb2 from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message @@ -7,21 +7,7 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class Tracker(_message.Message): - __slots__ = ["conversation_id", "slots", "latest_message", "latest_event_time", "followup_action", "paused", "events", "latest_input_channel", "active_loop", "latest_action"] - class SlotsEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class LatestMessageEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + __slots__ = ["sender_id", "slots", "latest_message", "events", "paused", "followup_action", "active_loop", "latest_action_name", "stack"] class ActiveLoopEntry(_message.Message): __slots__ = ["key", "value"] KEY_FIELD_NUMBER: _ClassVar[int] @@ -29,72 +15,52 @@ class Tracker(_message.Message): key: str value: str def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class LatestActionEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - CONVERSATION_ID_FIELD_NUMBER: _ClassVar[int] + SENDER_ID_FIELD_NUMBER: _ClassVar[int] SLOTS_FIELD_NUMBER: _ClassVar[int] LATEST_MESSAGE_FIELD_NUMBER: _ClassVar[int] - LATEST_EVENT_TIME_FIELD_NUMBER: _ClassVar[int] - FOLLOWUP_ACTION_FIELD_NUMBER: _ClassVar[int] - PAUSED_FIELD_NUMBER: _ClassVar[int] EVENTS_FIELD_NUMBER: _ClassVar[int] - LATEST_INPUT_CHANNEL_FIELD_NUMBER: _ClassVar[int] + PAUSED_FIELD_NUMBER: _ClassVar[int] + FOLLOWUP_ACTION_FIELD_NUMBER: _ClassVar[int] ACTIVE_LOOP_FIELD_NUMBER: _ClassVar[int] - LATEST_ACTION_FIELD_NUMBER: _ClassVar[int] - conversation_id: str - slots: _containers.ScalarMap[str, str] - latest_message: _containers.ScalarMap[str, str] - latest_event_time: float - followup_action: str + LATEST_ACTION_NAME_FIELD_NUMBER: _ClassVar[int] + STACK_FIELD_NUMBER: _ClassVar[int] + sender_id: str + slots: _struct_pb2.Struct + latest_message: _struct_pb2.Struct + events: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] paused: bool - events: _containers.RepeatedScalarFieldContainer[str] - latest_input_channel: str + followup_action: str active_loop: _containers.ScalarMap[str, str] - latest_action: _containers.ScalarMap[str, str] - def __init__(self, conversation_id: _Optional[str] = ..., slots: _Optional[_Mapping[str, str]] = ..., latest_message: _Optional[_Mapping[str, str]] = ..., latest_event_time: _Optional[float] = ..., followup_action: _Optional[str] = ..., paused: bool = ..., events: _Optional[_Iterable[str]] = ..., latest_input_channel: _Optional[str] = ..., active_loop: _Optional[_Mapping[str, str]] = ..., latest_action: _Optional[_Mapping[str, str]] = ...) -> None: ... + latest_action_name: str + stack: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + def __init__(self, sender_id: _Optional[str] = ..., slots: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., latest_message: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., events: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ..., paused: bool = ..., followup_action: _Optional[str] = ..., active_loop: _Optional[_Mapping[str, str]] = ..., latest_action_name: _Optional[str] = ..., stack: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ...) -> None: ... + +class Intent(_message.Message): + __slots__ = ["string_value", "dict_value"] + STRING_VALUE_FIELD_NUMBER: _ClassVar[int] + DICT_VALUE_FIELD_NUMBER: _ClassVar[int] + string_value: str + dict_value: _struct_pb2.Struct + def __init__(self, string_value: _Optional[str] = ..., dict_value: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... + +class Entity(_message.Message): + __slots__ = ["string_value", "dict_value"] + STRING_VALUE_FIELD_NUMBER: _ClassVar[int] + DICT_VALUE_FIELD_NUMBER: _ClassVar[int] + string_value: str + dict_value: _struct_pb2.Struct + def __init__(self, string_value: _Optional[str] = ..., dict_value: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... + +class Action(_message.Message): + __slots__ = ["string_value", "dict_value"] + STRING_VALUE_FIELD_NUMBER: _ClassVar[int] + DICT_VALUE_FIELD_NUMBER: _ClassVar[int] + string_value: str + dict_value: _struct_pb2.Struct + def __init__(self, string_value: _Optional[str] = ..., dict_value: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... class Domain(_message.Message): __slots__ = ["config", "session_config", "intents", "entities", "slots", "responses", "actions", "forms", "e2e_actions"] - class ConfigEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class SessionConfigEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class SlotsEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class ResponsesEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... - class FormsEntry(_message.Message): - __slots__ = ["key", "value"] - KEY_FIELD_NUMBER: _ClassVar[int] - VALUE_FIELD_NUMBER: _ClassVar[int] - key: str - value: str - def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... CONFIG_FIELD_NUMBER: _ClassVar[int] SESSION_CONFIG_FIELD_NUMBER: _ClassVar[int] INTENTS_FIELD_NUMBER: _ClassVar[int] @@ -104,35 +70,37 @@ class Domain(_message.Message): ACTIONS_FIELD_NUMBER: _ClassVar[int] FORMS_FIELD_NUMBER: _ClassVar[int] E2E_ACTIONS_FIELD_NUMBER: _ClassVar[int] - config: _containers.ScalarMap[str, str] - session_config: _containers.ScalarMap[str, str] - intents: _containers.RepeatedScalarFieldContainer[str] - entities: _containers.RepeatedScalarFieldContainer[str] - slots: _containers.ScalarMap[str, str] - responses: _containers.ScalarMap[str, str] - actions: _containers.RepeatedScalarFieldContainer[str] - forms: _containers.ScalarMap[str, str] - e2e_actions: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, config: _Optional[_Mapping[str, str]] = ..., session_config: _Optional[_Mapping[str, str]] = ..., intents: _Optional[_Iterable[str]] = ..., entities: _Optional[_Iterable[str]] = ..., slots: _Optional[_Mapping[str, str]] = ..., responses: _Optional[_Mapping[str, str]] = ..., actions: _Optional[_Iterable[str]] = ..., forms: _Optional[_Mapping[str, str]] = ..., e2e_actions: _Optional[_Iterable[str]] = ...) -> None: ... + config: _struct_pb2.Struct + session_config: _struct_pb2.Struct + intents: _containers.RepeatedCompositeFieldContainer[Intent] + entities: _containers.RepeatedCompositeFieldContainer[Entity] + slots: _struct_pb2.Struct + responses: _struct_pb2.Struct + actions: _containers.RepeatedCompositeFieldContainer[Action] + forms: _struct_pb2.Struct + e2e_actions: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + def __init__(self, config: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., session_config: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., intents: _Optional[_Iterable[_Union[Intent, _Mapping]]] = ..., entities: _Optional[_Iterable[_Union[Entity, _Mapping]]] = ..., slots: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., responses: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., actions: _Optional[_Iterable[_Union[Action, _Mapping]]] = ..., forms: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., e2e_actions: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ...) -> None: ... class WebhookRequest(_message.Message): - __slots__ = ["next_action", "sender_id", "tracker", "domain", "version"] + __slots__ = ["next_action", "sender_id", "tracker", "domain", "version", "domain_digest"] NEXT_ACTION_FIELD_NUMBER: _ClassVar[int] SENDER_ID_FIELD_NUMBER: _ClassVar[int] TRACKER_FIELD_NUMBER: _ClassVar[int] DOMAIN_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] + DOMAIN_DIGEST_FIELD_NUMBER: _ClassVar[int] next_action: str sender_id: str tracker: Tracker domain: Domain version: str - def __init__(self, next_action: _Optional[str] = ..., sender_id: _Optional[str] = ..., tracker: _Optional[_Union[Tracker, _Mapping]] = ..., domain: _Optional[_Union[Domain, _Mapping]] = ..., version: _Optional[str] = ...) -> None: ... + domain_digest: str + def __init__(self, next_action: _Optional[str] = ..., sender_id: _Optional[str] = ..., tracker: _Optional[_Union[Tracker, _Mapping]] = ..., domain: _Optional[_Union[Domain, _Mapping]] = ..., version: _Optional[str] = ..., domain_digest: _Optional[str] = ...) -> None: ... class WebhookResponse(_message.Message): __slots__ = ["events", "responses"] EVENTS_FIELD_NUMBER: _ClassVar[int] RESPONSES_FIELD_NUMBER: _ClassVar[int] - events: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] - responses: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] - def __init__(self, events: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., responses: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ...) -> None: ... + events: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + responses: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + def __init__(self, events: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ..., responses: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ...) -> None: ... diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 78b015de4..a2ff8ecea 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -45,7 +45,9 @@ async def webhook(self, request: WebhookRequest, context): utils.check_version_compatibility(request.version) try: action_call = MessageToDict(request) - result = await self.executor.run(action_call) + logger.info(f"Received request to run action '{action_call.get('next_action')}'.") + result = None + # result = await self.executor.run(action_call) except ActionExecutionRejection as e: logger.debug(e) body = {"error": e.message, "action_name": e.action_name} diff --git a/rasa_sdk/interfaces.py b/rasa_sdk/interfaces.py index 454ad24b7..e21462e46 100644 --- a/rasa_sdk/interfaces.py +++ b/rasa_sdk/interfaces.py @@ -46,7 +46,7 @@ def __init__( followup_action: Optional[Text], active_loop: Dict[Text, Any], latest_action_name: Optional[Text], - stack: List[Dict[Text, Any]] = [], + stack: Optional[List[Dict[Text, Any]]] = None, ) -> None: """Initialize the tracker.""" @@ -68,7 +68,7 @@ def __init__( self.latest_message = latest_message if latest_message else {} self.active_loop = active_loop self.latest_action_name = latest_action_name - self.stack = stack + self.stack = stack if stack else [] @property def active_form(self) -> Dict[Text, Any]: From 39af61ed9c5c8052cc8de85b923a2fb355663025 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Fri, 14 Jun 2024 15:18:43 +0200 Subject: [PATCH 05/19] Return error when domain is not found --- poetry.lock | 126 +++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + rasa_sdk/grpc_exceptions.py | 19 ++++++ rasa_sdk/grpc_server.py | 37 ++++++++--- 4 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 rasa_sdk/grpc_exceptions.py diff --git a/poetry.lock b/poetry.lock index ee3bfb215..f155c4f6b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -11,6 +11,20 @@ files = [ {file = "aiofiles-22.1.0.tar.gz", hash = "sha256:9107f1ca0b2a5553987a94a3c9959fe5b491fdf731389aa5b7b1bd0733e32de6"}, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + [[package]] name = "anyio" version = "3.6.2" @@ -1165,6 +1179,116 @@ files = [ {file = "protobuf-4.25.3.tar.gz", hash = "sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c"}, ] +[[package]] +name = "pydantic" +version = "2.6.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.6.4-py3-none-any.whl", hash = "sha256:cc46fce86607580867bdc3361ad462bab9c222ef042d3da86f2fb333e1d916c5"}, + {file = "pydantic-2.6.4.tar.gz", hash = "sha256:b1704e0847db01817624a6b86766967f552dd9dbf3afba4004409f908dcc84e6"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.16.3" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.3" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:75b81e678d1c1ede0785c7f46690621e4c6e63ccd9192af1f0bd9d504bbb6bf4"}, + {file = "pydantic_core-2.16.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c865a7ee6f93783bd5d781af5a4c43dadc37053a5b42f7d18dc019f8c9d2bd1"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:162e498303d2b1c036b957a1278fa0899d02b2842f1ff901b6395104c5554a45"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f583bd01bbfbff4eaee0868e6fc607efdfcc2b03c1c766b06a707abbc856187"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b926dd38db1519ed3043a4de50214e0d600d404099c3392f098a7f9d75029ff8"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:716b542728d4c742353448765aa7cdaa519a7b82f9564130e2b3f6766018c9ec"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f"}, + {file = "pydantic_core-2.16.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd87f48924f360e5d1c5f770d6155ce0e7d83f7b4e10c2f9ec001c73cf475c99"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0df446663464884297c793874573549229f9eca73b59360878f382a0fc085979"}, + {file = "pydantic_core-2.16.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4df8a199d9f6afc5ae9a65f8f95ee52cae389a8c6b20163762bde0426275b7db"}, + {file = "pydantic_core-2.16.3-cp310-none-win32.whl", hash = "sha256:456855f57b413f077dff513a5a28ed838dbbb15082ba00f80750377eed23d132"}, + {file = "pydantic_core-2.16.3-cp310-none-win_amd64.whl", hash = "sha256:732da3243e1b8d3eab8c6ae23ae6a58548849d2e4a4e03a1924c8ddf71a387cb"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:519ae0312616026bf4cedc0fe459e982734f3ca82ee8c7246c19b650b60a5ee4"}, + {file = "pydantic_core-2.16.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b3992a322a5617ded0a9f23fd06dbc1e4bd7cf39bc4ccf344b10f80af58beacd"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d62da299c6ecb04df729e4b5c52dc0d53f4f8430b4492b93aa8de1f541c4aac"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2acca2be4bb2f2147ada8cac612f8a98fc09f41c89f87add7256ad27332c2fda"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b662180108c55dfbf1280d865b2d116633d436cfc0bba82323554873967b340"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7c6ed0dc9d8e65f24f5824291550139fe6f37fac03788d4580da0d33bc00c97"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1bb0827f56654b4437955555dc3aeeebeddc47c2d7ed575477f082622c49e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e56f8186d6210ac7ece503193ec84104da7ceb98f68ce18c07282fcc2452e76f"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:936e5db01dd49476fa8f4383c259b8b1303d5dd5fb34c97de194560698cc2c5e"}, + {file = "pydantic_core-2.16.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33809aebac276089b78db106ee692bdc9044710e26f24a9a2eaa35a0f9fa70ba"}, + {file = "pydantic_core-2.16.3-cp311-none-win32.whl", hash = "sha256:ded1c35f15c9dea16ead9bffcde9bb5c7c031bff076355dc58dcb1cb436c4721"}, + {file = "pydantic_core-2.16.3-cp311-none-win_amd64.whl", hash = "sha256:d89ca19cdd0dd5f31606a9329e309d4fcbb3df860960acec32630297d61820df"}, + {file = "pydantic_core-2.16.3-cp311-none-win_arm64.whl", hash = "sha256:6162f8d2dc27ba21027f261e4fa26f8bcb3cf9784b7f9499466a311ac284b5b9"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f56ae86b60ea987ae8bcd6654a887238fd53d1384f9b222ac457070b7ac4cff"}, + {file = "pydantic_core-2.16.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9bd22a2a639e26171068f8ebb5400ce2c1bc7d17959f60a3b753ae13c632975"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4204e773b4b408062960e65468d5346bdfe139247ee5f1ca2a378983e11388a2"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f651dd19363c632f4abe3480a7c87a9773be27cfe1341aef06e8759599454120"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf09e615a0bf98d406657e0008e4a8701b11481840be7d31755dc9f97c44053"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8e47755d8152c1ab5b55928ab422a76e2e7b22b5ed8e90a7d584268dd49e9c6b"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:500960cb3a0543a724a81ba859da816e8cf01b0e6aaeedf2c3775d12ee49cade"}, + {file = "pydantic_core-2.16.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf6204fe865da605285c34cf1172879d0314ff267b1c35ff59de7154f35fdc2e"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d33dd21f572545649f90c38c227cc8631268ba25c460b5569abebdd0ec5974ca"}, + {file = "pydantic_core-2.16.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49d5d58abd4b83fb8ce763be7794d09b2f50f10aa65c0f0c1696c677edeb7cbf"}, + {file = "pydantic_core-2.16.3-cp312-none-win32.whl", hash = "sha256:f53aace168a2a10582e570b7736cc5bef12cae9cf21775e3eafac597e8551fbe"}, + {file = "pydantic_core-2.16.3-cp312-none-win_amd64.whl", hash = "sha256:0d32576b1de5a30d9a97f300cc6a3f4694c428d956adbc7e6e2f9cad279e45ed"}, + {file = "pydantic_core-2.16.3-cp312-none-win_arm64.whl", hash = "sha256:ec08be75bb268473677edb83ba71e7e74b43c008e4a7b1907c6d57e940bf34b6"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1f6f5938d63c6139860f044e2538baeee6f0b251a1816e7adb6cbce106a1f01"}, + {file = "pydantic_core-2.16.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2a1ef6a36fdbf71538142ed604ad19b82f67b05749512e47f247a6ddd06afdc7"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704d35ecc7e9c31d48926150afada60401c55efa3b46cd1ded5a01bdffaf1d48"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d937653a696465677ed583124b94a4b2d79f5e30b2c46115a68e482c6a591c8a"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9803edf8e29bd825f43481f19c37f50d2b01899448273b3a7758441b512acf8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72282ad4892a9fb2da25defeac8c2e84352c108705c972db82ab121d15f14e6d"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f752826b5b8361193df55afcdf8ca6a57d0232653494ba473630a83ba50d8c9"}, + {file = "pydantic_core-2.16.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4384a8f68ddb31a0b0c3deae88765f5868a1b9148939c3f4121233314ad5532c"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4b2bf78342c40b3dc830880106f54328928ff03e357935ad26c7128bbd66ce8"}, + {file = "pydantic_core-2.16.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:13dcc4802961b5f843a9385fc821a0b0135e8c07fc3d9949fd49627c1a5e6ae5"}, + {file = "pydantic_core-2.16.3-cp38-none-win32.whl", hash = "sha256:e3e70c94a0c3841e6aa831edab1619ad5c511199be94d0c11ba75fe06efe107a"}, + {file = "pydantic_core-2.16.3-cp38-none-win_amd64.whl", hash = "sha256:ecdf6bf5f578615f2e985a5e1f6572e23aa632c4bd1dc67f8f406d445ac115ed"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bda1ee3e08252b8d41fa5537413ffdddd58fa73107171a126d3b9ff001b9b820"}, + {file = "pydantic_core-2.16.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:21b888c973e4f26b7a96491c0965a8a312e13be108022ee510248fe379a5fa23"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be0ec334369316fa73448cc8c982c01e5d2a81c95969d58b8f6e272884df0074"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b6079cc452a7c53dd378c6f881ac528246b3ac9aae0f8eef98498a75657805"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee8d5f878dccb6d499ba4d30d757111847b6849ae07acdd1205fffa1fc1253c"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7233d65d9d651242a68801159763d09e9ec96e8a158dbf118dc090cd77a104c9"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6119dc90483a5cb50a1306adb8d52c66e447da88ea44f323e0ae1a5fcb14256"}, + {file = "pydantic_core-2.16.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:578114bc803a4c1ff9946d977c221e4376620a46cf78da267d946397dc9514a8"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8f99b147ff3fcf6b3cc60cb0c39ea443884d5559a30b1481e92495f2310ff2b"}, + {file = "pydantic_core-2.16.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4ac6b4ce1e7283d715c4b729d8f9dab9627586dafce81d9eaa009dd7f25dd972"}, + {file = "pydantic_core-2.16.3-cp39-none-win32.whl", hash = "sha256:e7774b570e61cb998490c5235740d475413a1f6de823169b4cf94e2fe9e9f6b2"}, + {file = "pydantic_core-2.16.3-cp39-none-win_amd64.whl", hash = "sha256:9091632a25b8b87b9a605ec0e61f241c456e9248bfdcf7abdf344fdb169c81cf"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:36fa178aacbc277bc6b62a2c3da95226520da4f4e9e206fdf076484363895d2c"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:dcca5d2bf65c6fb591fff92da03f94cd4f315972f97c21975398bd4bd046854a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a72fb9963cba4cd5793854fd12f4cfee731e86df140f59ff52a49b3552db241"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60cc1a081f80a2105a59385b92d82278b15d80ebb3adb200542ae165cd7d183"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cbcc558401de90a746d02ef330c528f2e668c83350f045833543cd57ecead1ad"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4cb85f693044e0f71f394ff76c98ddc1bc0953e48c061725e540396d5c8a2e1"}, + {file = "pydantic_core-2.16.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b29eeb887aa931c2fcef5aa515d9d176d25006794610c264ddc114c053bf96fe"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a425479ee40ff021f8216c9d07a6a3b54b31c8267c6e17aa88b70d7ebd0e5e5b"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:5c5cbc703168d1b7a838668998308018a2718c2130595e8e190220238addc96f"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b6add4c0b39a513d323d3b93bc173dac663c27b99860dd5bf491b240d26137"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f76ee558751746d6a38f89d60b6228fa174e5172d143886af0f85aa306fd89"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:00ee1c97b5364b84cb0bd82e9bbf645d5e2871fb8c58059d158412fee2d33d8a"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:287073c66748f624be4cef893ef9174e3eb88fe0b8a78dc22e88eca4bc357ca6"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed25e1835c00a332cb10c683cd39da96a719ab1dfc08427d476bce41b92531fc"}, + {file = "pydantic_core-2.16.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:86b3d0033580bd6bbe07590152007275bd7af95f98eaa5bd36f3da219dcd93da"}, + {file = "pydantic_core-2.16.3.tar.gz", hash = "sha256:1cac689f80a3abab2d3c0048b29eea5751114054f032a941a32de4c852c59cad"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + [[package]] name = "pyparsing" version = "3.0.9" @@ -1919,4 +2043,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "f2dc2ff13fb249529e99c45faa6a4f309b33dcec8245c7e04c78c0e651566ef2" +content-hash = "2c8e39ac94a55fe5bc581cb40e0a6bd16bd3ba1982904d82ce331373faf0f58b" diff --git a/pyproject.toml b/pyproject.toml index 658dd4b56..5a00ed652 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -90,6 +90,7 @@ opentelemetry-exporter-otlp = "~1.15.0" grpcio = "1.59.3" protobuf = "4.25.3" grpcio-tools = "1.56.2" +pydantic = "2.6.4" [tool.poetry.dev-dependencies] pytest-cov = "^4.1.0" diff --git a/rasa_sdk/grpc_exceptions.py b/rasa_sdk/grpc_exceptions.py new file mode 100644 index 000000000..7e38a9b6e --- /dev/null +++ b/rasa_sdk/grpc_exceptions.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from pydantic import BaseModel, Field + +import dataclasses + +from enum import Enum + + +class ResourceNotFoundType(str, Enum): + """Type of resource that was not found.""" + ACTION = "ACTION" + DOMAIN = "DOMAIN" + + +class ResourceNotFound(BaseModel): + action_name: str = Field(alias="action_name") + message: str = Field(alias="message") + resource_type: ResourceNotFoundType = Field(alias="resource_type") diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index a2ff8ecea..bd7d2d3ef 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import grpc import utils import logging @@ -10,9 +12,10 @@ from rasa_sdk.constants import DEFAULT_SERVER_PORT, DEFAULT_ENDPOINTS_PATH from rasa_sdk.executor import ActionExecutor +from rasa_sdk.grpc_exceptions import ResourceNotFound, ResourceNotFoundType from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest -from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException +from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException, ActionMissingDomainException from rasa_sdk.tracing.utils import ( get_tracer_and_context, TracerProvider, @@ -38,16 +41,14 @@ def __init__( self.executor = executor async def webhook(self, request: WebhookRequest, context): - tracer, context, span_name = get_tracer_and_context( + tracer, tracer_context, span_name = get_tracer_and_context( self.tracer_provider, request ) - with tracer.start_as_current_span(span_name, context=context) as span: + with tracer.start_as_current_span(span_name, context=tracer_context) as span: utils.check_version_compatibility(request.version) try: - action_call = MessageToDict(request) - logger.info(f"Received request to run action '{action_call.get('next_action')}'.") - result = None - # result = await self.executor.run(action_call) + action_call = MessageToDict(request, preserving_proto_field_name=True) + result = await self.executor.run(action_call) except ActionExecutionRejection as e: logger.debug(e) body = {"error": e.message, "action_name": e.action_name} @@ -56,9 +57,25 @@ async def webhook(self, request: WebhookRequest, context): return action_webhook_pb2.WebhookResponse() except ActionNotFoundException as e: logger.error(e) - body = {"error": e.message, "action_name": e.action_name} - context.set_code(grpc.StatusCode.NOTFOUND) - context.set_details(str(body)) + resource_not_found = ResourceNotFound( + action_name=e.action_name, + message=e.message, + resource_type=ResourceNotFoundType.ACTION, + ) + body = resource_not_found.json() + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(body) + return action_webhook_pb2.WebhookResponse() + except ActionMissingDomainException as e: + logger.error(e) + resource_not_found = ResourceNotFound( + action_name=e.action_name, + message=e.message, + resource_type=ResourceNotFoundType.DOMAIN, + ) + body = resource_not_found.json() + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details(body) return action_webhook_pb2.WebhookResponse() if not result: return action_webhook_pb2.WebhookResponse() From b612b4ba3ebdf447434a5fbc3937930ab174500c Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Sun, 16 Jun 2024 00:16:10 +0200 Subject: [PATCH 06/19] Add code docs for gRPC server --- rasa_sdk/executor.py | 53 ++++++++++++++++--- .../{grpc_exceptions.py => grpc_errors.py} | 3 +- rasa_sdk/grpc_server.py | 53 +++++++++++++++---- 3 files changed, 91 insertions(+), 18 deletions(-) rename rasa_sdk/{grpc_exceptions.py => grpc_errors.py} (87%) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index 802d84a7a..0be809ba1 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -63,6 +63,15 @@ def utter_message( # deprecated def utter_custom_message(self, *elements: Dict[Text, Any], **kwargs: Any) -> None: + """Sends a message with custom elements to the output channel. + + Deprecated: + Use `utter_message(elements=)` instead. + + Args: + elements: List of elements to be sent to the output channel. + kwargs: Additional parameters to be sent to the output channel. + """ warnings.warn( "Use of `utter_custom_message` is deprecated. " "Use `utter_message(elements=)` instead.", @@ -166,6 +175,13 @@ def __init__(self) -> None: self.domain_digest: Optional[Text] = None def register_action(self, action: Union[Type[Action], Action]) -> None: + """Register an action with the executor. + + Args: + action: Action to be registered. It can either be an instance of + `Action` subclass class or an actual `Action` subclass. + """ + if inspect.isclass(action): action = cast(Type[Action], action) if action.__module__.startswith("rasa."): @@ -189,7 +205,13 @@ def register_action(self, action: Union[Type[Action], Action]) -> None: "a function, use `register_function` instead." ) - def register_function(self, name: Text, f: Callable) -> None: + def register_function(self, action_name: Text, f: Callable) -> None: + """Register an executor function for an action. + + Args: + action_name: Name of the action. + f: Function to be registered. + """ valid_keys = utils.arguments_of(f) if len(valid_keys) < 3: raise Exception( @@ -200,12 +222,12 @@ def register_function(self, name: Text, f: Callable) -> None: "parameters." ) - if name in self.actions: - logger.info(f"Re-registered function for '{name}'.") + if action_name in self.actions: + logger.info(f"Re-registered function for '{action_name}'.") else: - logger.info(f"Registered function for '{name}'.") + logger.info(f"Registered function for '{action_name}'.") - self.actions[name] = f + self.actions[action_name] = f def _import_submodules( self, package: Union[Text, types.ModuleType], recursive: bool = True @@ -349,7 +371,17 @@ def _create_api_response( return {"events": events, "responses": messages} @staticmethod - def validate_events(events: List[Dict[Text, Any]], action_name: Text): + def validate_events(events: List[Dict[Text, Any]], action_name: Text) -> List[Dict[Text, Any]]: + """Validate the events returned by the action. + + Args: + events: List of events returned by the action. + + action_name: Name of the action that should be executed. + + Returns: + List of validated events. + """ validated = [] for event in events: if isinstance(event, dict): @@ -425,6 +457,15 @@ def update_and_return_domain( return self.domain async def run(self, action_call: Dict[Text, Any]) -> Optional[Dict[Text, Any]]: + """Run the action and return the response. + + Args: + action_call: Request payload containing the action data. + + Returns: + Response containing the events and messages or None if + the action does not exist. + """ from rasa_sdk.interfaces import Tracker action_name = action_call.get("next_action") diff --git a/rasa_sdk/grpc_exceptions.py b/rasa_sdk/grpc_errors.py similarity index 87% rename from rasa_sdk/grpc_exceptions.py rename to rasa_sdk/grpc_errors.py index 7e38a9b6e..b56a4b464 100644 --- a/rasa_sdk/grpc_exceptions.py +++ b/rasa_sdk/grpc_errors.py @@ -2,8 +2,6 @@ from pydantic import BaseModel, Field -import dataclasses - from enum import Enum @@ -14,6 +12,7 @@ class ResourceNotFoundType(str, Enum): class ResourceNotFound(BaseModel): + """Error which indicates that a resource was not found.""" action_name: str = Field(alias="action_name") message: str = Field(alias="message") resource_type: ResourceNotFoundType = Field(alias="resource_type") diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index bd7d2d3ef..436c6b02c 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -5,14 +5,14 @@ import logging import ssl import types -from typing import Text, Union, Optional +from typing import Union, Optional, Callable from concurrent import futures from grpc import aio from google.protobuf.json_format import MessageToDict, ParseDict from rasa_sdk.constants import DEFAULT_SERVER_PORT, DEFAULT_ENDPOINTS_PATH from rasa_sdk.executor import ActionExecutor -from rasa_sdk.grpc_exceptions import ResourceNotFound, ResourceNotFoundType +from rasa_sdk.grpc_errors import ResourceNotFound, ResourceNotFoundType from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException, ActionMissingDomainException @@ -40,7 +40,15 @@ def __init__( self.tracer_provider = tracer_provider self.executor = executor - async def webhook(self, request: WebhookRequest, context): + async def webhook(self, + request: WebhookRequest, context, + ) -> action_webhook_pb2.WebhookResponse: + """Handle RPC request for the webhook. + + Args: + request: The webhook request. + context: The context of the request. + """ tracer, tracer_context, span_name = get_tracer_and_context( self.tracer_provider, request ) @@ -85,21 +93,40 @@ async def webhook(self, request: WebhookRequest, context): return ParseDict(result, response) -def get_ssl_password_callback(ssl_password): - def password_callback(*args, **kwargs): +def get_ssl_password_callback(ssl_password: str) -> Callable[[], bytes]: + """Return a password callback function for the SSL key file. + + Args: + ssl_password: Password for the SSL key file. + + Returns: + A password callback function. + """ + def password_callback() -> bytes: + """Return the SSL password as bytes.""" return ssl_password.encode() if ssl_password else None return password_callback async def run_grpc( - action_package_name: Union[Text, types.ModuleType], + action_package_name: Union[str, types.ModuleType], port: int = DEFAULT_SERVER_PORT, - ssl_certificate: Optional[Text] = None, - ssl_keyfile: Optional[Text] = None, - ssl_password: Optional[Text] = None, + ssl_certificate: Optional[str] = None, + ssl_keyfile: Optional[str] = None, + ssl_password: Optional[str] = None, endpoints: str = DEFAULT_ENDPOINTS_PATH, ): + """Start a gRPC server to handle incoming action requests. + + Args: + action_package_name: Name of the package which contains the custom actions. + port: Port to start the server on. + ssl_certificate: File path to the SSL certificate. + ssl_keyfile: File path to the SSL key file. + ssl_password: Password for the SSL key file. + endpoints: Path to the endpoints file. + """ workers = utils.number_of_sanic_workers() server = aio.server(futures.ThreadPoolExecutor(max_workers=workers)) executor = ActionExecutor() @@ -117,13 +144,19 @@ async def run_grpc( keyfile=ssl_keyfile, password=get_ssl_password_callback(ssl_password), ) + logger.info( + f"Starting gRPC server with SSL support on port {port}" + ) server.add_secure_port( f"[::]:{port}", server_credentials=grpc.ssl_server_credentials(ssl_context) ) else: + logger.info( + f"Starting gRPC server without SSL on port {port}" + ) # Use insecure connection if no SSL/TLS information is provided server.add_insecure_port(f"[::]:{port}") await server.start() - print(f"gRPC Server started on port {port}") + logger.info(f"gRPC Server started on port {port}") await server.wait_for_termination() From fc6bc9bcac6a18decaa061053d43fda8fdaf4e92 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Sun, 16 Jun 2024 01:15:19 +0200 Subject: [PATCH 07/19] Handle SIGINT and SIGTERM signals when gRPC server is running --- rasa_sdk/__main__.py | 14 -------------- rasa_sdk/grpc_server.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/rasa_sdk/__main__.py b/rasa_sdk/__main__.py index dcd6e70a0..7433b9e57 100644 --- a/rasa_sdk/__main__.py +++ b/rasa_sdk/__main__.py @@ -1,6 +1,5 @@ import logging import asyncio -import signal from rasa_sdk import utils from rasa_sdk.endpoint import create_argument_parser, run @@ -10,17 +9,6 @@ logger = logging.getLogger(__name__) -def initialise_interrupts() -> None: - """Initialise handlers for kernel signal interrupts.""" - - def handle_sigint(signum, frame): - logger.info("Received SIGINT, exiting") - asyncio.get_event_loop().stop() - - signal.signal(signal.SIGINT, handle_sigint) - signal.signal(signal.SIGTERM, handle_sigint) - - def main_from_args(args): """Run with arguments.""" logging.getLogger("matplotlib").setLevel(logging.WARN) @@ -34,8 +22,6 @@ def main_from_args(args): ) utils.update_sanic_log_level() - initialise_interrupts() - if args.grpc: asyncio.run( run_grpc( diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 436c6b02c..9d40d3fd1 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -1,5 +1,13 @@ from __future__ import annotations +import time + +import sys + +import signal + +import asyncio + import grpc import utils import logging @@ -49,6 +57,7 @@ async def webhook(self, request: The webhook request. context: The context of the request. """ + await asyncio.sleep(50) tracer, tracer_context, span_name = get_tracer_and_context( self.tracer_provider, request ) @@ -109,6 +118,28 @@ def password_callback() -> bytes: return password_callback +def get_signal_name(signal_number: int) -> str: + """Return the signal name for the given signal number.""" + return signal.Signals(signal_number).name + + +def initialise_interrupts(server: grpc.aio.Server) -> None: + """Initialise handlers for kernel signal interrupts.""" + + async def handle_sigint(signal_received: int): + logger.info( + f"Received {get_signal_name(signal_received)} signal. Stopping gRPC server..." + ) + await server.stop(0) + logger.info("gRPC server stopped.") + + loop = asyncio.get_event_loop() + loop.add_signal_handler(signal.SIGINT, + lambda: asyncio.create_task(handle_sigint(signal.SIGINT))) + loop.add_signal_handler(signal.SIGTERM, + lambda: asyncio.create_task(handle_sigint(signal.SIGTERM))) + + async def run_grpc( action_package_name: Union[str, types.ModuleType], port: int = DEFAULT_SERVER_PORT, @@ -129,6 +160,7 @@ async def run_grpc( """ workers = utils.number_of_sanic_workers() server = aio.server(futures.ThreadPoolExecutor(max_workers=workers)) + initialise_interrupts(server) executor = ActionExecutor() executor.register_package(action_package_name) # tracer_provider = get_tracer_provider(endpoints) From 5f94cabb618f1713440b71984d9d703b481b5cd2 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Mon, 17 Jun 2024 10:31:07 +0200 Subject: [PATCH 08/19] Remove SSL password provider function --- rasa_sdk/constants.py | 1 + rasa_sdk/executor.py | 5 ++- rasa_sdk/grpc_errors.py | 21 +++++++++- rasa_sdk/grpc_server.py | 93 +++++++++++++++++++---------------------- 4 files changed, 68 insertions(+), 52 deletions(-) diff --git a/rasa_sdk/constants.py b/rasa_sdk/constants.py index 16637dfb1..4af04e6e9 100644 --- a/rasa_sdk/constants.py +++ b/rasa_sdk/constants.py @@ -11,3 +11,4 @@ "https://docs.python.org/3/library/logging.config.html#dictionary-schema-details" ) DEFAULT_ENDPOINTS_PATH = "endpoints.yml" +NO_GRACE_PERIOD = 0 diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index 0be809ba1..d3f246edd 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -371,7 +371,10 @@ def _create_api_response( return {"events": events, "responses": messages} @staticmethod - def validate_events(events: List[Dict[Text, Any]], action_name: Text) -> List[Dict[Text, Any]]: + def validate_events( + events: List[Dict[Text, Any]], + action_name: Text, + ) -> List[Dict[Text, Any]]: """Validate the events returned by the action. Args: diff --git a/rasa_sdk/grpc_errors.py b/rasa_sdk/grpc_errors.py index b56a4b464..a9d0f0121 100644 --- a/rasa_sdk/grpc_errors.py +++ b/rasa_sdk/grpc_errors.py @@ -5,14 +5,33 @@ from enum import Enum +class ActionExecutionFailed(BaseModel): + """Error which indicates that an action execution failed. + + Attributes: + action_name: Name of the action that failed. + message: Message which describes the error. + """ + + action_name: str = Field(alias="action_name") + message: str = Field(alias="message") + + class ResourceNotFoundType(str, Enum): """Type of resource that was not found.""" + ACTION = "ACTION" DOMAIN = "DOMAIN" class ResourceNotFound(BaseModel): - """Error which indicates that a resource was not found.""" + """Error which indicates that a resource was not found. + + Attributes: + action_name: Name of the action that was not found. + message: Message which describes the error. + """ + action_name: str = Field(alias="action_name") message: str = Field(alias="message") resource_type: ResourceNotFoundType = Field(alias="resource_type") diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 9d40d3fd1..5fce7dc8a 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -1,9 +1,5 @@ from __future__ import annotations -import time - -import sys - import signal import asyncio @@ -13,21 +9,32 @@ import logging import ssl import types -from typing import Union, Optional, Callable +from typing import Union, Optional from concurrent import futures from grpc import aio from google.protobuf.json_format import MessageToDict, ParseDict -from rasa_sdk.constants import DEFAULT_SERVER_PORT, DEFAULT_ENDPOINTS_PATH +from rasa_sdk.constants import ( + DEFAULT_SERVER_PORT, + DEFAULT_ENDPOINTS_PATH, + NO_GRACE_PERIOD, +) from rasa_sdk.executor import ActionExecutor -from rasa_sdk.grpc_errors import ResourceNotFound, ResourceNotFoundType +from rasa_sdk.grpc_errors import ( + ResourceNotFound, + ResourceNotFoundType, + ActionExecutionFailed, +) from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest -from rasa_sdk.interfaces import ActionExecutionRejection, ActionNotFoundException, ActionMissingDomainException +from rasa_sdk.interfaces import ( + ActionExecutionRejection, + ActionNotFoundException, + ActionMissingDomainException, +) from rasa_sdk.tracing.utils import ( get_tracer_and_context, TracerProvider, - get_tracer_provider, ) logger = logging.getLogger(__name__) @@ -48,9 +55,11 @@ def __init__( self.tracer_provider = tracer_provider self.executor = executor - async def webhook(self, - request: WebhookRequest, context, - ) -> action_webhook_pb2.WebhookResponse: + async def webhook( + self, + request: WebhookRequest, + context, + ) -> action_webhook_pb2.WebhookResponse: """Handle RPC request for the webhook. Args: @@ -61,36 +70,37 @@ async def webhook(self, tracer, tracer_context, span_name = get_tracer_and_context( self.tracer_provider, request ) - with tracer.start_as_current_span(span_name, context=tracer_context) as span: + with tracer.start_as_current_span(span_name, context=tracer_context): utils.check_version_compatibility(request.version) try: action_call = MessageToDict(request, preserving_proto_field_name=True) result = await self.executor.run(action_call) except ActionExecutionRejection as e: logger.debug(e) - body = {"error": e.message, "action_name": e.action_name} + + body = ActionExecutionFailed( + action_name=e.action_name, message=e.message + ).model_dump() context.set_code(grpc.StatusCode.INTERNAL) - context.set_details(str(body)) + context.set_details(body) return action_webhook_pb2.WebhookResponse() except ActionNotFoundException as e: logger.error(e) - resource_not_found = ResourceNotFound( + body = ResourceNotFound( action_name=e.action_name, message=e.message, resource_type=ResourceNotFoundType.ACTION, - ) - body = resource_not_found.json() + ).model_dump() context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details(body) return action_webhook_pb2.WebhookResponse() except ActionMissingDomainException as e: logger.error(e) - resource_not_found = ResourceNotFound( + body = ResourceNotFound( action_name=e.action_name, message=e.message, resource_type=ResourceNotFoundType.DOMAIN, - ) - body = resource_not_found.json() + ).model_dump() context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details(body) return action_webhook_pb2.WebhookResponse() @@ -102,22 +112,6 @@ async def webhook(self, return ParseDict(result, response) -def get_ssl_password_callback(ssl_password: str) -> Callable[[], bytes]: - """Return a password callback function for the SSL key file. - - Args: - ssl_password: Password for the SSL key file. - - Returns: - A password callback function. - """ - def password_callback() -> bytes: - """Return the SSL password as bytes.""" - return ssl_password.encode() if ssl_password else None - - return password_callback - - def get_signal_name(signal_number: int) -> str: """Return the signal name for the given signal number.""" return signal.Signals(signal_number).name @@ -128,16 +122,19 @@ def initialise_interrupts(server: grpc.aio.Server) -> None: async def handle_sigint(signal_received: int): logger.info( - f"Received {get_signal_name(signal_received)} signal. Stopping gRPC server..." + f"Received {get_signal_name(signal_received)} signal." + "Stopping gRPC server..." ) - await server.stop(0) + await server.stop(NO_GRACE_PERIOD) logger.info("gRPC server stopped.") loop = asyncio.get_event_loop() - loop.add_signal_handler(signal.SIGINT, - lambda: asyncio.create_task(handle_sigint(signal.SIGINT))) - loop.add_signal_handler(signal.SIGTERM, - lambda: asyncio.create_task(handle_sigint(signal.SIGTERM))) + loop.add_signal_handler( + signal.SIGINT, lambda: asyncio.create_task(handle_sigint(signal.SIGINT)) + ) + loop.add_signal_handler( + signal.SIGTERM, lambda: asyncio.create_task(handle_sigint(signal.SIGTERM)) + ) async def run_grpc( @@ -174,18 +171,14 @@ async def run_grpc( ssl_context.load_cert_chain( ssl_certificate, keyfile=ssl_keyfile, - password=get_ssl_password_callback(ssl_password), - ) - logger.info( - f"Starting gRPC server with SSL support on port {port}" + password=ssl_password if ssl_password else None, ) + logger.info(f"Starting gRPC server with SSL support on port {port}") server.add_secure_port( f"[::]:{port}", server_credentials=grpc.ssl_server_credentials(ssl_context) ) else: - logger.info( - f"Starting gRPC server without SSL on port {port}" - ) + logger.info(f"Starting gRPC server without SSL on port {port}") # Use insecure connection if no SSL/TLS information is provided server.add_insecure_port(f"[::]:{port}") From ed053935b33a44f9bf6f650a6fb4e1592444ee7b Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Mon, 17 Jun 2024 14:21:51 +0200 Subject: [PATCH 09/19] Use partial import form utils --- rasa_sdk/grpc_server.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 5fce7dc8a..27796fe74 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -5,7 +5,6 @@ import asyncio import grpc -import utils import logging import ssl import types @@ -36,11 +35,14 @@ get_tracer_and_context, TracerProvider, ) +from rasa_sdk.utils import check_version_compatibility, number_of_sanic_workers logger = logging.getLogger(__name__) -class ActionServerWebhook(action_webhook_pb2_grpc.ActionServerWebhookServicer): +class GRPCActionServerWebhook(action_webhook_pb2_grpc.ActionServerWebhookServicer): + """Runs webhook RPC which is served through gRPC server.""" + def __init__( self, executor: ActionExecutor, @@ -65,13 +67,15 @@ async def webhook( Args: request: The webhook request. context: The context of the request. + + Returns: + gRPC response. """ - await asyncio.sleep(50) tracer, tracer_context, span_name = get_tracer_and_context( self.tracer_provider, request ) with tracer.start_as_current_span(span_name, context=tracer_context): - utils.check_version_compatibility(request.version) + check_version_compatibility(request.version) try: action_call = MessageToDict(request, preserving_proto_field_name=True) result = await self.executor.run(action_call) @@ -121,6 +125,7 @@ def initialise_interrupts(server: grpc.aio.Server) -> None: """Initialise handlers for kernel signal interrupts.""" async def handle_sigint(signal_received: int): + """Handle the received signal.""" logger.info( f"Received {get_signal_name(signal_received)} signal." "Stopping gRPC server..." @@ -155,7 +160,7 @@ async def run_grpc( ssl_password: Password for the SSL key file. endpoints: Path to the endpoints file. """ - workers = utils.number_of_sanic_workers() + workers = number_of_sanic_workers() server = aio.server(futures.ThreadPoolExecutor(max_workers=workers)) initialise_interrupts(server) executor = ActionExecutor() @@ -163,7 +168,7 @@ async def run_grpc( # tracer_provider = get_tracer_provider(endpoints) tracer_provider = None action_webhook_pb2_grpc.add_ActionServerWebhookServicer_to_server( - ActionServerWebhook(executor, tracer_provider), server + GRPCActionServerWebhook(executor, tracer_provider), server ) if ssl_certificate and ssl_keyfile: # Use SSL/TLS if certificate and key are provided From b49f87478d8d7396790a64f305eb78acd5102728 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Mon, 17 Jun 2024 18:55:26 +0200 Subject: [PATCH 10/19] Initialise SSL for gRPC --- rasa_sdk/grpc_server.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 27796fe74..136e12927 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -6,7 +6,6 @@ import grpc import logging -import ssl import types from typing import Union, Optional from concurrent import futures @@ -94,7 +93,7 @@ async def webhook( action_name=e.action_name, message=e.message, resource_type=ResourceNotFoundType.ACTION, - ).model_dump() + ).model_dump_json() context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details(body) return action_webhook_pb2.WebhookResponse() @@ -104,7 +103,7 @@ async def webhook( action_name=e.action_name, message=e.message, resource_type=ResourceNotFoundType.DOMAIN, - ).model_dump() + ).model_dump_json() context.set_code(grpc.StatusCode.NOT_FOUND) context.set_details(body) return action_webhook_pb2.WebhookResponse() @@ -172,15 +171,20 @@ async def run_grpc( ) if ssl_certificate and ssl_keyfile: # Use SSL/TLS if certificate and key are provided - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - ssl_context.load_cert_chain( - ssl_certificate, - keyfile=ssl_keyfile, - password=ssl_password if ssl_password else None, - ) + # ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + # ssl_context.load_cert_chain( + # ssl_certificate, + # keyfile=ssl_keyfile, + # password=ssl_password if ssl_password else None, + # ) + private_key = open(ssl_keyfile, "rb").read() + certificate_chain = open(ssl_certificate, "rb").read() logger.info(f"Starting gRPC server with SSL support on port {port}") server.add_secure_port( - f"[::]:{port}", server_credentials=grpc.ssl_server_credentials(ssl_context) + f"[::]:{port}", + server_credentials=grpc.ssl_server_credentials( + [(private_key, certificate_chain)] + ), ) else: logger.info(f"Starting gRPC server without SSL on port {port}") From 5410a908eede5b691019f20782bde6a1902bbdb4 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 09:36:27 +0200 Subject: [PATCH 11/19] Add healthcheck for gRPC --- Makefile | 6 +-- proto/health.proto | 11 +++++ rasa_sdk/grpc_py/health_pb2.py | 30 +++++++++++++ rasa_sdk/grpc_py/health_pb2.pyi | 13 ++++++ rasa_sdk/grpc_py/health_pb2_grpc.py | 66 +++++++++++++++++++++++++++++ rasa_sdk/grpc_server.py | 14 +++--- 6 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 proto/health.proto create mode 100644 rasa_sdk/grpc_py/health_pb2.py create mode 100644 rasa_sdk/grpc_py/health_pb2.pyi create mode 100644 rasa_sdk/grpc_py/health_pb2_grpc.py diff --git a/Makefile b/Makefile index 173d03dda..42bfecddf 100644 --- a/Makefile +++ b/Makefile @@ -61,13 +61,11 @@ cleanup-generated-changelog: release: poetry run python scripts/release.py -download-protoc-compiler: - curl -0L https://github.com/protocolbuffers/protobuf/releases/download/v25.0/protoc-25.0-osx-aarch_64.zip --output protoc-25.0-osx-aarch_64.zip - generate-grpc: python -m grpc_tools.protoc \ -Irasa_sdk/grpc_py=./proto \ --python_out=. \ --grpc_python_out=. \ --pyi_out=. \ - proto/action_webhook.proto \ No newline at end of file + proto/action_webhook.proto \ + proto/health.proto \ No newline at end of file diff --git a/proto/health.proto b/proto/health.proto new file mode 100644 index 000000000..4f81c56d4 --- /dev/null +++ b/proto/health.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package grpc.health.v1; + +message HealthCheckRequest {} + +message HealthCheckResponse {} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} \ No newline at end of file diff --git a/rasa_sdk/grpc_py/health_pb2.py b/rasa_sdk/grpc_py/health_pb2.py new file mode 100644 index 000000000..36205e83c --- /dev/null +++ b/rasa_sdk/grpc_py/health_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: rasa_sdk/grpc_py/health.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1drasa_sdk/grpc_py/health.proto\x12\x0egrpc.health.v1\"\x14\n\x12HealthCheckRequest\"\x15\n\x13HealthCheckResponse2Z\n\x06Health\x12P\n\x05\x43heck\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponseb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rasa_sdk.grpc_py.health_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _globals['_HEALTHCHECKREQUEST']._serialized_start=49 + _globals['_HEALTHCHECKREQUEST']._serialized_end=69 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=71 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=92 + _globals['_HEALTH']._serialized_start=94 + _globals['_HEALTH']._serialized_end=184 +# @@protoc_insertion_point(module_scope) diff --git a/rasa_sdk/grpc_py/health_pb2.pyi b/rasa_sdk/grpc_py/health_pb2.pyi new file mode 100644 index 000000000..a58962527 --- /dev/null +++ b/rasa_sdk/grpc_py/health_pb2.pyi @@ -0,0 +1,13 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class HealthCheckRequest(_message.Message): + __slots__ = [] + def __init__(self) -> None: ... + +class HealthCheckResponse(_message.Message): + __slots__ = [] + def __init__(self) -> None: ... diff --git a/rasa_sdk/grpc_py/health_pb2_grpc.py b/rasa_sdk/grpc_py/health_pb2_grpc.py new file mode 100644 index 000000000..f20a40588 --- /dev/null +++ b/rasa_sdk/grpc_py/health_pb2_grpc.py @@ -0,0 +1,66 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from rasa_sdk.grpc_py import health_pb2 as rasa__sdk_dot_grpc__py_dot_health__pb2 + + +class HealthStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Check = channel.unary_unary( + '/grpc.health.v1.Health/Check', + request_serializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckRequest.SerializeToString, + response_deserializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckResponse.FromString, + ) + + +class HealthServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Check(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_HealthServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Check': grpc.unary_unary_rpc_method_handler( + servicer.Check, + request_deserializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckRequest.FromString, + response_serializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'grpc.health.v1.Health', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class Health(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Check(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/grpc.health.v1.Health/Check', + rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckRequest.SerializeToString, + rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 136e12927..1b4f98d1e 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -3,6 +3,7 @@ import signal import asyncio +import ssl import grpc import logging @@ -171,12 +172,13 @@ async def run_grpc( ) if ssl_certificate and ssl_keyfile: # Use SSL/TLS if certificate and key are provided - # ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - # ssl_context.load_cert_chain( - # ssl_certificate, - # keyfile=ssl_keyfile, - # password=ssl_password if ssl_password else None, - # ) + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ssl_context.load_cert_chain( + ssl_certificate, + keyfile=ssl_keyfile, + password=ssl_password if ssl_password else None, + ) + grpc.ssl_channel_credentials() private_key = open(ssl_keyfile, "rb").read() certificate_chain = open(ssl_certificate, "rb").read() logger.info(f"Starting gRPC server with SSL support on port {port}") From 6cf9bdb73c6671096677f2ab7f295cb5c3b109ca Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 10:06:51 +0200 Subject: [PATCH 12/19] Add support for client cert auth in gRPC --- rasa_sdk/__main__.py | 4 ++-- rasa_sdk/cli/arguments.py | 32 +++++++++++++++++++++++++++----- rasa_sdk/grpc_server.py | 29 +++++++++++++++-------------- rasa_sdk/utils.py | 32 ++++++++++++++++++++++++++++---- 4 files changed, 72 insertions(+), 25 deletions(-) diff --git a/rasa_sdk/__main__.py b/rasa_sdk/__main__.py index 7433b9e57..c642f35ec 100644 --- a/rasa_sdk/__main__.py +++ b/rasa_sdk/__main__.py @@ -29,7 +29,7 @@ def main_from_args(args): args.port, args.ssl_certificate, args.ssl_keyfile, - args.ssl_password, + args.ssl_ca_file, args.endpoints, ) ) @@ -47,7 +47,7 @@ def main_from_args(args): def main(): - # Running as standalone python application + """Runs the action server as standalone application.""" arg_parser = create_argument_parser() cmdline_args = arg_parser.parse_args() diff --git a/rasa_sdk/cli/arguments.py b/rasa_sdk/cli/arguments.py index 5a805ef8d..5a81f5e03 100644 --- a/rasa_sdk/cli/arguments.py +++ b/rasa_sdk/cli/arguments.py @@ -3,17 +3,31 @@ from rasa_sdk.constants import DEFAULT_SERVER_PORT, DEFAULT_ENDPOINTS_PATH -def action_arg(action): - if "/" in action: +def action_arg(actions_module_path: str) -> str: + """Validate the action module path. + + Valid action module path is python module, so it should not contain a slash. + + Args: + actions_module_path: Path to the actions python module. + + Returns: + actions_module_path: If provided module path is valid. + + Raises: + argparse.ArgumentTypeError: If the module path is invalid. + """ + if "/" in actions_module_path: raise argparse.ArgumentTypeError( "Invalid actions format. Actions file should be a python module " "and passed with module notation (e.g. directory.actions)." ) else: - return action + return actions_module_path -def add_endpoint_arguments(parser): +def add_endpoint_arguments(parser: argparse.ArgumentParser) -> None: + """Add all the arguments to the argument parser.""" parser.add_argument( "-p", "--port", @@ -47,7 +61,15 @@ def add_endpoint_arguments(parser): "--ssl-password", default=None, help="If your ssl-keyfile is protected by a password, you can specify it " - "using this paramer.", + "using this parameter. " + "Not supported in grpc mode.", + ) + parser.add_argument( + "--ssl-ca-file", + default=None, + help="If you want to authenticate the client using a certificate, you can " + "specify the CA certificate of the client using this parameter. " + "Supported only in grpc mode.", ) parser.add_argument( "--auto-reload", diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 1b4f98d1e..089d212ef 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -3,7 +3,6 @@ import signal import asyncio -import ssl import grpc import logging @@ -35,7 +34,10 @@ get_tracer_and_context, TracerProvider, ) -from rasa_sdk.utils import check_version_compatibility, number_of_sanic_workers +from rasa_sdk.utils import ( + check_version_compatibility, + number_of_sanic_workers, file_as_bytes, +) logger = logging.getLogger(__name__) @@ -84,7 +86,7 @@ async def webhook( body = ActionExecutionFailed( action_name=e.action_name, message=e.message - ).model_dump() + ).model_dump_json() context.set_code(grpc.StatusCode.INTERNAL) context.set_details(body) return action_webhook_pb2.WebhookResponse() @@ -147,7 +149,7 @@ async def run_grpc( port: int = DEFAULT_SERVER_PORT, ssl_certificate: Optional[str] = None, ssl_keyfile: Optional[str] = None, - ssl_password: Optional[str] = None, + ssl_ca_file: Optional[str] = None, endpoints: str = DEFAULT_ENDPOINTS_PATH, ): """Start a gRPC server to handle incoming action requests. @@ -157,7 +159,7 @@ async def run_grpc( port: Port to start the server on. ssl_certificate: File path to the SSL certificate. ssl_keyfile: File path to the SSL key file. - ssl_password: Password for the SSL key file. + ssl_ca_file: File path to the SSL CA certificate file. endpoints: Path to the endpoints file. """ workers = number_of_sanic_workers() @@ -170,22 +172,21 @@ async def run_grpc( action_webhook_pb2_grpc.add_ActionServerWebhookServicer_to_server( GRPCActionServerWebhook(executor, tracer_provider), server ) + + ca_cert = file_as_bytes(ssl_ca_file) if ssl_ca_file else None + if ssl_certificate and ssl_keyfile: # Use SSL/TLS if certificate and key are provided - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - ssl_context.load_cert_chain( - ssl_certificate, - keyfile=ssl_keyfile, - password=ssl_password if ssl_password else None, - ) grpc.ssl_channel_credentials() - private_key = open(ssl_keyfile, "rb").read() - certificate_chain = open(ssl_certificate, "rb").read() + private_key = file_as_bytes(ssl_keyfile) + certificate_chain = file_as_bytes(ssl_certificate) logger.info(f"Starting gRPC server with SSL support on port {port}") server.add_secure_port( f"[::]:{port}", server_credentials=grpc.ssl_server_credentials( - [(private_key, certificate_chain)] + private_key_certificate_chain_pairs=[(private_key, certificate_chain)], + root_certificates = ca_cert, + require_client_auth = True if ca_cert else False, ), ) else: diff --git a/rasa_sdk/utils.py b/rasa_sdk/utils.py index a9fb35cc9..c058e033b 100644 --- a/rasa_sdk/utils.py +++ b/rasa_sdk/utils.py @@ -32,9 +32,11 @@ class Element(dict): + """Represents an element in a list of elements in a rich message.""" __acceptable_keys = ["title", "item_url", "image_url", "subtitle", "buttons"] def __init__(self, *args, **kwargs): + """Initializes an element in a list of elements in a rich message.""" kwargs = { key: value for key, value in kwargs.items() if key in self.__acceptable_keys } @@ -43,6 +45,7 @@ def __init__(self, *args, **kwargs): class Button(dict): + """Represents a button in a rich message.""" pass @@ -257,12 +260,12 @@ def check_version_compatibility(rasa_version: Optional[Text]) -> None: rasa and rasa_sdk. Args: - rasa_version - A string containing the version of rasa that - is making the call to the action server. + rasa_version: A string containing the version of rasa that + is making the call to the action server. Raises: - Warning - The version of rasa version unknown or not compatible with - this version of rasa_sdk. + Warning: The version of rasa version unknown or not compatible with + this version of rasa_sdk. """ # Check for versions of Rasa that are too old to report their version number if rasa_version is None: @@ -386,3 +389,24 @@ def read_yaml_file(filename: Union[Text, Path]) -> Dict[Text, Any]: return read_yaml(read_file(filename, DEFAULT_ENCODING)) except (YAMLError, DuplicateKeyError) as e: raise YamlSyntaxException(filename, e) + + +def file_as_bytes(file_path: Text) -> bytes: + """Read in a file as a byte array. + + Args: + file_path: Path to the file to read. + + Returns: + The file content as a byte array. + + Raises: + FileNotFoundException: If the file does not exist. + """ + try: + with open(file_path, "rb") as f: + return f.read() + except FileNotFoundError: + raise FileNotFoundException( + f"Failed to read file, " f"'{os.path.abspath(file_path)}' does not exist." + ) From b2839a3a8932c59371ee16ddef9005c3448fd5df Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 11:52:32 +0200 Subject: [PATCH 13/19] Add action listing in gRPC --- proto/action_webhook.proto | 11 +++- proto/health.proto | 2 +- rasa_sdk/endpoint.py | 2 +- rasa_sdk/executor.py | 36 ++++++++++--- rasa_sdk/grpc_py/action_webhook_pb2.py | 46 ++++++++++------- rasa_sdk/grpc_py/action_webhook_pb2.pyi | 17 ++++++ rasa_sdk/grpc_py/action_webhook_pb2_grpc.py | 57 ++++++++++++++++----- rasa_sdk/grpc_py/health_pb2.py | 6 +-- rasa_sdk/grpc_py/health_pb2_grpc.py | 14 ++--- rasa_sdk/grpc_server.py | 57 +++++++++++++++++++-- 10 files changed, 190 insertions(+), 58 deletions(-) diff --git a/proto/action_webhook.proto b/proto/action_webhook.proto index 9cae517b1..5d4e9da91 100644 --- a/proto/action_webhook.proto +++ b/proto/action_webhook.proto @@ -3,8 +3,15 @@ syntax = "proto3"; package action_server_webhook; import "google/protobuf/struct.proto"; -service ActionServerWebhook { - rpc webhook (WebhookRequest) returns (WebhookResponse); +service ActionService { + rpc Webhook (WebhookRequest) returns (WebhookResponse); + rpc Actions (ActionsRequest) returns (ActionsResponse); +} + +message ActionsRequest {} + +message ActionsResponse { + map actions = 1; } message Tracker { diff --git a/proto/health.proto b/proto/health.proto index 4f81c56d4..09b3a62f2 100644 --- a/proto/health.proto +++ b/proto/health.proto @@ -6,6 +6,6 @@ message HealthCheckRequest {} message HealthCheckResponse {} -service Health { +service HealthService { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); } \ No newline at end of file diff --git a/rasa_sdk/endpoint.py b/rasa_sdk/endpoint.py index 5818026b1..1745787bb 100644 --- a/rasa_sdk/endpoint.py +++ b/rasa_sdk/endpoint.py @@ -172,7 +172,7 @@ async def actions(_) -> HTTPResponse: if auto_reload: executor.reload() - body = [{"name": k} for k in executor.actions.keys()] + body = [action_name_item.model_dump() for action_name_item in executor.list_actions()] # noqa: E501 return response.json(body, status=200) @app.exception(Exception) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index d3f246edd..2c1c4c2b4 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -1,3 +1,4 @@ +from __future__ import annotations import importlib import inspect import logging @@ -9,6 +10,8 @@ import sys import os +from pydantic import BaseModel, Field + from rasa_sdk.interfaces import ( Tracker, ActionNotFoundException, @@ -22,9 +25,10 @@ class CollectingDispatcher: - """Send messages back to user""" + """Send messages back to user.""" def __init__(self) -> None: + """Create a `CollectingDispatcher` object.""" self.messages: List[Dict[Text, Any]] = [] def utter_message( @@ -167,7 +171,9 @@ def utter_image_url(self, image: Text, **kwargs: Any) -> None: class ActionExecutor: + """Executes actions.""" def __init__(self) -> None: + """Initializes the `ActionExecutor`.""" self.actions: Dict[Text, Callable] = {} self._modules: Dict[Text, TimestampModule] = {} self._loaded: Set[Type[Action]] = set() @@ -181,7 +187,6 @@ def register_action(self, action: Union[Type[Action], Action]) -> None: action: Action to be registered. It can either be an instance of `Action` subclass class or an actual `Action` subclass. """ - if inspect.isclass(action): action = cast(Type[Action], action) if action.__module__.startswith("rasa."): @@ -274,8 +279,7 @@ def _import_module(self, name: Text) -> types.ModuleType: return module def register_package(self, package: Union[Text, types.ModuleType]) -> None: - """Register all the `Action` subclasses contained in a Python module or - package. + """Register all the `Action` subclasses contained in a Python module or package. If an `ImportError` is raised when loading the module or package, the action server is stopped with exit code 1. @@ -308,14 +312,14 @@ def _register_all_actions(self) -> None: self.register_action(action) def _find_modules_to_reload(self) -> Dict[Text, TimestampModule]: - """Finds all Python modules that should be reloaded by checking their - files' timestamps. + """Finds all Python modules that should be reloaded. + + Reloads modules by checking their files' timestamps. Returns: Dictionary containing file paths, new timestamps and Python modules that should be reloaded. """ - to_reload = {} for path, (timestamp, module) in self._modules.items(): @@ -418,10 +422,13 @@ def validate_events( return validated def is_domain_digest_valid(self, domain_digest: Optional[Text]) -> bool: - """Check if the domain_digest is valid + """Check if the domain_digest is valid. + If the domain_digest is empty or different from the one provided, it is invalid. + Args: domain_digest: latest value provided to compare the current value with. + Returns: True if the domain_digest is valid, False otherwise. """ @@ -431,15 +438,19 @@ def update_and_return_domain( self, payload: Dict[Text, Any], action_name: Text ) -> Optional[Dict[Text, Any]]: """Validate the digest, store the domain if available, and return the domain. + This method validates the domain digest from the payload. If the digest is invalid and no domain is provided, an exception is raised. If domain data is available, it stores the domain and digest. Finally, it returns the domain. + Args: payload: Request payload containing the domain data. action_name: Name of the action that should be executed. + Returns: The domain dictionary. + Raises: ActionMissingDomainException: Invalid digest and no domain data available. """ @@ -497,3 +508,12 @@ async def run(self, action_call: Dict[Text, Any]) -> Optional[Dict[Text, Any]]: logger.warning("Received an action call without an action.") return None + + def list_actions(self) -> List[ActionName]: + """List all registered action names.""" + return [ActionName(name=action_name) for action_name in self.actions.keys()] + + +class ActionName(BaseModel): + """Model for action name.""" + name: str = Field(alias="name") diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.py b/rasa_sdk/grpc_py/action_webhook_pb2.py index b8b7b1101..26f550a65 100644 --- a/rasa_sdk/grpc_py/action_webhook_pb2.py +++ b/rasa_sdk/grpc_py/action_webhook_pb2.py @@ -14,7 +14,7 @@ from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%rasa_sdk/grpc_py/action_webhook.proto\x12\x15\x61\x63tion_server_webhook\x1a\x1cgoogle/protobuf/struct.proto\"\xb8\x03\n\x07Tracker\x12\x11\n\tsender_id\x18\x01 \x01(\t\x12&\n\x05slots\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0elatest_message\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65vents\x18\x04 \x03(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06paused\x18\x05 \x01(\x08\x12\x1c\n\x0f\x66ollowup_action\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x43\n\x0b\x61\x63tive_loop\x18\x07 \x03(\x0b\x32..action_server_webhook.Tracker.ActiveLoopEntry\x12\x1f\n\x12latest_action_name\x18\x08 \x01(\tH\x01\x88\x01\x01\x12&\n\x05stack\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\x1a\x31\n\x0f\x41\x63tiveLoopEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x12\n\x10_followup_actionB\x15\n\x13_latest_action_name\"K\n\x06Intent\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x45ntity\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x41\x63tion\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x9d\x03\n\x06\x44omain\x12\'\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0esession_config\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07intents\x18\x03 \x03(\x0b\x32\x1d.action_server_webhook.Intent\x12/\n\x08\x65ntities\x18\x04 \x03(\x0b\x32\x1d.action_server_webhook.Entity\x12&\n\x05slots\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07\x61\x63tions\x18\x07 \x03(\x0b\x32\x1d.action_server_webhook.Action\x12&\n\x05\x66orms\x18\x08 \x01(\x0b\x32\x17.google.protobuf.Struct\x12,\n\x0b\x65\x32\x65_actions\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\"\xd7\x01\n\x0eWebhookRequest\x12\x13\n\x0bnext_action\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12/\n\x07tracker\x18\x03 \x01(\x0b\x32\x1e.action_server_webhook.Tracker\x12-\n\x06\x64omain\x18\x04 \x01(\x0b\x32\x1d.action_server_webhook.Domain\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x1a\n\rdomain_digest\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x10\n\x0e_domain_digest\"f\n\x0fWebhookResponse\x12\'\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Struct2o\n\x13\x41\x63tionServerWebhook\x12X\n\x07webhook\x12%.action_server_webhook.WebhookRequest\x1a&.action_server_webhook.WebhookResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%rasa_sdk/grpc_py/action_webhook.proto\x12\x15\x61\x63tion_server_webhook\x1a\x1cgoogle/protobuf/struct.proto\"\x10\n\x0e\x41\x63tionsRequest\"\x87\x01\n\x0f\x41\x63tionsResponse\x12\x44\n\x07\x61\x63tions\x18\x01 \x03(\x0b\x32\x33.action_server_webhook.ActionsResponse.ActionsEntry\x1a.\n\x0c\x41\x63tionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xb8\x03\n\x07Tracker\x12\x11\n\tsender_id\x18\x01 \x01(\t\x12&\n\x05slots\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0elatest_message\x18\x03 \x01(\x0b\x32\x17.google.protobuf.Struct\x12\'\n\x06\x65vents\x18\x04 \x03(\x0b\x32\x17.google.protobuf.Struct\x12\x0e\n\x06paused\x18\x05 \x01(\x08\x12\x1c\n\x0f\x66ollowup_action\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x43\n\x0b\x61\x63tive_loop\x18\x07 \x03(\x0b\x32..action_server_webhook.Tracker.ActiveLoopEntry\x12\x1f\n\x12latest_action_name\x18\x08 \x01(\tH\x01\x88\x01\x01\x12&\n\x05stack\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\x1a\x31\n\x0f\x41\x63tiveLoopEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x12\n\x10_followup_actionB\x15\n\x13_latest_action_name\"K\n\x06Intent\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x45ntity\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"K\n\x06\x41\x63tion\x12\x14\n\x0cstring_value\x18\x01 \x01(\t\x12+\n\ndict_value\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\"\x9d\x03\n\x06\x44omain\x12\'\n\x06\x63onfig\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0esession_config\x18\x02 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07intents\x18\x03 \x03(\x0b\x32\x1d.action_server_webhook.Intent\x12/\n\x08\x65ntities\x18\x04 \x03(\x0b\x32\x1d.action_server_webhook.Entity\x12&\n\x05slots\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x06 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\x07\x61\x63tions\x18\x07 \x03(\x0b\x32\x1d.action_server_webhook.Action\x12&\n\x05\x66orms\x18\x08 \x01(\x0b\x32\x17.google.protobuf.Struct\x12,\n\x0b\x65\x32\x65_actions\x18\t \x03(\x0b\x32\x17.google.protobuf.Struct\"\xd7\x01\n\x0eWebhookRequest\x12\x13\n\x0bnext_action\x18\x01 \x01(\t\x12\x11\n\tsender_id\x18\x02 \x01(\t\x12/\n\x07tracker\x18\x03 \x01(\x0b\x32\x1e.action_server_webhook.Tracker\x12-\n\x06\x64omain\x18\x04 \x01(\x0b\x32\x1d.action_server_webhook.Domain\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x1a\n\rdomain_digest\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x10\n\x0e_domain_digest\"f\n\x0fWebhookResponse\x12\'\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x17.google.protobuf.Struct\x12*\n\tresponses\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Struct2\xc3\x01\n\rActionService\x12X\n\x07Webhook\x12%.action_server_webhook.WebhookRequest\x1a&.action_server_webhook.WebhookResponse\x12X\n\x07\x41\x63tions\x12%.action_server_webhook.ActionsRequest\x1a&.action_server_webhook.ActionsResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,24 +22,32 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None + _ACTIONSRESPONSE_ACTIONSENTRY._options = None + _ACTIONSRESPONSE_ACTIONSENTRY._serialized_options = b'8\001' _TRACKER_ACTIVELOOPENTRY._options = None _TRACKER_ACTIVELOOPENTRY._serialized_options = b'8\001' - _globals['_TRACKER']._serialized_start=95 - _globals['_TRACKER']._serialized_end=535 - _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_start=443 - _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_end=492 - _globals['_INTENT']._serialized_start=537 - _globals['_INTENT']._serialized_end=612 - _globals['_ENTITY']._serialized_start=614 - _globals['_ENTITY']._serialized_end=689 - _globals['_ACTION']._serialized_start=691 - _globals['_ACTION']._serialized_end=766 - _globals['_DOMAIN']._serialized_start=769 - _globals['_DOMAIN']._serialized_end=1182 - _globals['_WEBHOOKREQUEST']._serialized_start=1185 - _globals['_WEBHOOKREQUEST']._serialized_end=1400 - _globals['_WEBHOOKRESPONSE']._serialized_start=1402 - _globals['_WEBHOOKRESPONSE']._serialized_end=1504 - _globals['_ACTIONSERVERWEBHOOK']._serialized_start=1506 - _globals['_ACTIONSERVERWEBHOOK']._serialized_end=1617 + _globals['_ACTIONSREQUEST']._serialized_start=94 + _globals['_ACTIONSREQUEST']._serialized_end=110 + _globals['_ACTIONSRESPONSE']._serialized_start=113 + _globals['_ACTIONSRESPONSE']._serialized_end=248 + _globals['_ACTIONSRESPONSE_ACTIONSENTRY']._serialized_start=202 + _globals['_ACTIONSRESPONSE_ACTIONSENTRY']._serialized_end=248 + _globals['_TRACKER']._serialized_start=251 + _globals['_TRACKER']._serialized_end=691 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_start=599 + _globals['_TRACKER_ACTIVELOOPENTRY']._serialized_end=648 + _globals['_INTENT']._serialized_start=693 + _globals['_INTENT']._serialized_end=768 + _globals['_ENTITY']._serialized_start=770 + _globals['_ENTITY']._serialized_end=845 + _globals['_ACTION']._serialized_start=847 + _globals['_ACTION']._serialized_end=922 + _globals['_DOMAIN']._serialized_start=925 + _globals['_DOMAIN']._serialized_end=1338 + _globals['_WEBHOOKREQUEST']._serialized_start=1341 + _globals['_WEBHOOKREQUEST']._serialized_end=1556 + _globals['_WEBHOOKRESPONSE']._serialized_start=1558 + _globals['_WEBHOOKRESPONSE']._serialized_end=1660 + _globals['_ACTIONSERVICE']._serialized_start=1663 + _globals['_ACTIONSERVICE']._serialized_end=1858 # @@protoc_insertion_point(module_scope) diff --git a/rasa_sdk/grpc_py/action_webhook_pb2.pyi b/rasa_sdk/grpc_py/action_webhook_pb2.pyi index 566596e3b..c8c43431f 100644 --- a/rasa_sdk/grpc_py/action_webhook_pb2.pyi +++ b/rasa_sdk/grpc_py/action_webhook_pb2.pyi @@ -6,6 +6,23 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor +class ActionsRequest(_message.Message): + __slots__ = [] + def __init__(self) -> None: ... + +class ActionsResponse(_message.Message): + __slots__ = ["actions"] + class ActionsEntry(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + ACTIONS_FIELD_NUMBER: _ClassVar[int] + actions: _containers.ScalarMap[str, str] + def __init__(self, actions: _Optional[_Mapping[str, str]] = ...) -> None: ... + class Tracker(_message.Message): __slots__ = ["sender_id", "slots", "latest_message", "events", "paused", "followup_action", "active_loop", "latest_action_name", "stack"] class ActiveLoopEntry(_message.Message): diff --git a/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py b/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py index 215a4062a..47a33414e 100644 --- a/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py +++ b/rasa_sdk/grpc_py/action_webhook_pb2_grpc.py @@ -5,7 +5,7 @@ from rasa_sdk.grpc_py import action_webhook_pb2 as rasa__sdk_dot_grpc__py_dot_action__webhook__pb2 -class ActionServerWebhookStub(object): +class ActionServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,42 +14,58 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.webhook = channel.unary_unary( - '/action_server_webhook.ActionServerWebhook/webhook', + self.Webhook = channel.unary_unary( + '/action_server_webhook.ActionService/Webhook', request_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.SerializeToString, response_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.FromString, ) + self.Actions = channel.unary_unary( + '/action_server_webhook.ActionService/Actions', + request_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsRequest.SerializeToString, + response_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsResponse.FromString, + ) -class ActionServerWebhookServicer(object): +class ActionServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def webhook(self, request, context): + def Webhook(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Actions(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') -def add_ActionServerWebhookServicer_to_server(servicer, server): +def add_ActionServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'webhook': grpc.unary_unary_rpc_method_handler( - servicer.webhook, + 'Webhook': grpc.unary_unary_rpc_method_handler( + servicer.Webhook, request_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.FromString, response_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.SerializeToString, ), + 'Actions': grpc.unary_unary_rpc_method_handler( + servicer.Actions, + request_deserializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsRequest.FromString, + response_serializer=rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( - 'action_server_webhook.ActionServerWebhook', rpc_method_handlers) + 'action_server_webhook.ActionService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class ActionServerWebhook(object): +class ActionService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def webhook(request, + def Webhook(request, target, options=(), channel_credentials=None, @@ -59,8 +75,25 @@ def webhook(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/action_server_webhook.ActionServerWebhook/webhook', + return grpc.experimental.unary_unary(request, target, '/action_server_webhook.ActionService/Webhook', rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookRequest.SerializeToString, rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.WebhookResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Actions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/action_server_webhook.ActionService/Actions', + rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsRequest.SerializeToString, + rasa__sdk_dot_grpc__py_dot_action__webhook__pb2.ActionsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/rasa_sdk/grpc_py/health_pb2.py b/rasa_sdk/grpc_py/health_pb2.py index 36205e83c..8f58d31d7 100644 --- a/rasa_sdk/grpc_py/health_pb2.py +++ b/rasa_sdk/grpc_py/health_pb2.py @@ -13,7 +13,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1drasa_sdk/grpc_py/health.proto\x12\x0egrpc.health.v1\"\x14\n\x12HealthCheckRequest\"\x15\n\x13HealthCheckResponse2Z\n\x06Health\x12P\n\x05\x43heck\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponseb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1drasa_sdk/grpc_py/health.proto\x12\x0egrpc.health.v1\"\x14\n\x12HealthCheckRequest\"\x15\n\x13HealthCheckResponse2a\n\rHealthService\x12P\n\x05\x43heck\x12\".grpc.health.v1.HealthCheckRequest\x1a#.grpc.health.v1.HealthCheckResponseb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -25,6 +25,6 @@ _globals['_HEALTHCHECKREQUEST']._serialized_end=69 _globals['_HEALTHCHECKRESPONSE']._serialized_start=71 _globals['_HEALTHCHECKRESPONSE']._serialized_end=92 - _globals['_HEALTH']._serialized_start=94 - _globals['_HEALTH']._serialized_end=184 + _globals['_HEALTHSERVICE']._serialized_start=94 + _globals['_HEALTHSERVICE']._serialized_end=191 # @@protoc_insertion_point(module_scope) diff --git a/rasa_sdk/grpc_py/health_pb2_grpc.py b/rasa_sdk/grpc_py/health_pb2_grpc.py index f20a40588..904f921bb 100644 --- a/rasa_sdk/grpc_py/health_pb2_grpc.py +++ b/rasa_sdk/grpc_py/health_pb2_grpc.py @@ -5,7 +5,7 @@ from rasa_sdk.grpc_py import health_pb2 as rasa__sdk_dot_grpc__py_dot_health__pb2 -class HealthStub(object): +class HealthServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -15,13 +15,13 @@ def __init__(self, channel): channel: A grpc.Channel. """ self.Check = channel.unary_unary( - '/grpc.health.v1.Health/Check', + '/grpc.health.v1.HealthService/Check', request_serializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckRequest.SerializeToString, response_deserializer=rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckResponse.FromString, ) -class HealthServicer(object): +class HealthServiceServicer(object): """Missing associated documentation comment in .proto file.""" def Check(self, request, context): @@ -31,7 +31,7 @@ def Check(self, request, context): raise NotImplementedError('Method not implemented!') -def add_HealthServicer_to_server(servicer, server): +def add_HealthServiceServicer_to_server(servicer, server): rpc_method_handlers = { 'Check': grpc.unary_unary_rpc_method_handler( servicer.Check, @@ -40,12 +40,12 @@ def add_HealthServicer_to_server(servicer, server): ), } generic_handler = grpc.method_handlers_generic_handler( - 'grpc.health.v1.Health', rpc_method_handlers) + 'grpc.health.v1.HealthService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class Health(object): +class HealthService(object): """Missing associated documentation comment in .proto file.""" @staticmethod @@ -59,7 +59,7 @@ def Check(request, wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/grpc.health.v1.Health/Check', + return grpc.experimental.unary_unary(request, target, '/grpc.health.v1.HealthService/Check', rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckRequest.SerializeToString, rasa__sdk_dot_grpc__py_dot_health__pb2.HealthCheckResponse.FromString, options, channel_credentials, diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 089d212ef..07f4242e0 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -23,8 +23,17 @@ ResourceNotFoundType, ActionExecutionFailed, ) -from rasa_sdk.grpc_py import action_webhook_pb2, action_webhook_pb2_grpc -from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest +from rasa_sdk.grpc_py import ( + action_webhook_pb2, + action_webhook_pb2_grpc, + health_pb2_grpc, +) +from rasa_sdk.grpc_py.action_webhook_pb2 import ( + ActionsResponse, + ActionsRequest, + WebhookRequest, +) +from rasa_sdk.grpc_py.health_pb2 import HealthCheckRequest, HealthCheckResponse from rasa_sdk.interfaces import ( ActionExecutionRejection, ActionNotFoundException, @@ -42,7 +51,27 @@ logger = logging.getLogger(__name__) -class GRPCActionServerWebhook(action_webhook_pb2_grpc.ActionServerWebhookServicer): +class GRPCActionServerHealthCheck(health_pb2_grpc.HealthServiceServicer): + """Runs health check RPC which is served through gRPC server.""" + def __init__(self) -> None: + """Initializes the HealthServicer.""" + pass + + def Check(self, request: HealthCheckRequest, context) -> HealthCheckResponse: + """Handle RPC request for the health check. + + Args: + request: The health check request. + context: The context of the request. + + Returns: + gRPC response. + """ + response = HealthCheckResponse() + return response + + +class GRPCActionServerWebhook(action_webhook_pb2_grpc.ActionServiceServicer): """Runs webhook RPC which is served through gRPC server.""" def __init__( @@ -59,7 +88,21 @@ def __init__( self.tracer_provider = tracer_provider self.executor = executor - async def webhook( + async def Actions(self, request: ActionsRequest, context) -> ActionsResponse: + """Handle RPC request for the actions. + + Args: + request: The actions request. + context: The context of the request. + + Returns: + gRPC response. + """ + actions = self.executor.list_actions() + response = ActionsResponse() + return ParseDict(actions, response) + + async def Webhook( self, request: WebhookRequest, context, @@ -169,10 +212,14 @@ async def run_grpc( executor.register_package(action_package_name) # tracer_provider = get_tracer_provider(endpoints) tracer_provider = None - action_webhook_pb2_grpc.add_ActionServerWebhookServicer_to_server( + action_webhook_pb2_grpc.add_ActionServiceServicer_to_server( GRPCActionServerWebhook(executor, tracer_provider), server ) + health_pb2_grpc.add_HealthServiceServicer_to_server( + GRPCActionServerHealthCheck(), server + ) + ca_cert = file_as_bytes(ssl_ca_file) if ssl_ca_file else None if ssl_certificate and ssl_keyfile: From 1e593efa2cdecc78f8a31c74bfae3f7292eefbee Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 12:12:08 +0200 Subject: [PATCH 14/19] Format code --- Makefile | 2 +- pyproject.toml | 4 +++- rasa_sdk/cli/arguments.py | 4 +--- rasa_sdk/endpoint.py | 5 ++++- rasa_sdk/executor.py | 2 ++ rasa_sdk/grpc_server.py | 8 +++++--- rasa_sdk/interfaces.py | 21 ++++++++------------- rasa_sdk/utils.py | 2 ++ 8 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 42bfecddf..48dd7f577 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ formatter: lint: poetry run ruff check rasa_sdk tests --ignore D - poetry run black --check rasa_sdk tests + poetry run black --exclude="rasa_sdk/grpc_py" --check rasa_sdk tests make lint-docstrings # Compare against `main` if no branch was provided diff --git a/pyproject.toml b/pyproject.toml index 5a00ed652..139296a36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.black] line-length = 88 target-version = [ "py37", "py38", "py39", "py310",] -exclude = "((.eggs | .git | .mypy_cache | .pytest_cache | build | dist))" +exclude = "((.eggs | .git | .mypy_cache | .pytest_cache | build | dist ))" [tool.poetry] name = "rasa-sdk" @@ -67,11 +67,13 @@ ignore_missing_imports = true show_error_codes = true warn_redundant_casts = true warn_unused_ignores = true +exclude = "rasa_sdk/grpc_py" [tool.ruff] ignore = [ "D100", "D104", "D105", "RUF005",] line-length = 88 select = [ "D", "E", "F", "W", "RUF",] +exclude = [ "rasa_sdk/grpc_py" ] [tool.poetry.dependencies] python = ">=3.8,<3.11" diff --git a/rasa_sdk/cli/arguments.py b/rasa_sdk/cli/arguments.py index 5a81f5e03..90e282e2c 100644 --- a/rasa_sdk/cli/arguments.py +++ b/rasa_sdk/cli/arguments.py @@ -82,7 +82,5 @@ def add_endpoint_arguments(parser: argparse.ArgumentParser) -> None: help="Configuration file for the assistant as a yml file.", ) parser.add_argument( - "--grpc", - help="Starts grpc server instead of http", - action="store_true" + "--grpc", help="Starts grpc server instead of http", action="store_true" ) diff --git a/rasa_sdk/endpoint.py b/rasa_sdk/endpoint.py index 1745787bb..0a0326042 100644 --- a/rasa_sdk/endpoint.py +++ b/rasa_sdk/endpoint.py @@ -172,7 +172,10 @@ async def actions(_) -> HTTPResponse: if auto_reload: executor.reload() - body = [action_name_item.model_dump() for action_name_item in executor.list_actions()] # noqa: E501 + body = [ + action_name_item.model_dump() + for action_name_item in executor.list_actions() + ] return response.json(body, status=200) @app.exception(Exception) diff --git a/rasa_sdk/executor.py b/rasa_sdk/executor.py index 2c1c4c2b4..24d4705ec 100644 --- a/rasa_sdk/executor.py +++ b/rasa_sdk/executor.py @@ -172,6 +172,7 @@ def utter_image_url(self, image: Text, **kwargs: Any) -> None: class ActionExecutor: """Executes actions.""" + def __init__(self) -> None: """Initializes the `ActionExecutor`.""" self.actions: Dict[Text, Callable] = {} @@ -516,4 +517,5 @@ def list_actions(self) -> List[ActionName]: class ActionName(BaseModel): """Model for action name.""" + name: str = Field(alias="name") diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 07f4242e0..20dc62948 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -45,7 +45,8 @@ ) from rasa_sdk.utils import ( check_version_compatibility, - number_of_sanic_workers, file_as_bytes, + number_of_sanic_workers, + file_as_bytes, ) logger = logging.getLogger(__name__) @@ -53,6 +54,7 @@ class GRPCActionServerHealthCheck(health_pb2_grpc.HealthServiceServicer): """Runs health check RPC which is served through gRPC server.""" + def __init__(self) -> None: """Initializes the HealthServicer.""" pass @@ -232,8 +234,8 @@ async def run_grpc( f"[::]:{port}", server_credentials=grpc.ssl_server_credentials( private_key_certificate_chain_pairs=[(private_key, certificate_chain)], - root_certificates = ca_cert, - require_client_auth = True if ca_cert else False, + root_certificates=ca_cert, + require_client_auth=True if ca_cert else False, ), ) else: diff --git a/rasa_sdk/interfaces.py b/rasa_sdk/interfaces.py index e21462e46..21791f416 100644 --- a/rasa_sdk/interfaces.py +++ b/rasa_sdk/interfaces.py @@ -23,7 +23,6 @@ class Tracker: @classmethod def from_dict(cls, state: "TrackerState") -> "Tracker": """Create a tracker from dump.""" - return Tracker( state["sender_id"], state.get("slots", {}), @@ -49,7 +48,6 @@ def __init__( stack: Optional[List[Dict[Text, Any]]] = None, ) -> None: """Initialize the tracker.""" - # list of previously seen events self.events = events # id of the source of the messages @@ -72,6 +70,7 @@ def __init__( @property def active_form(self) -> Dict[Text, Any]: + """Get the currently active form.""" warnings.warn( "Use of `active_form` is deprecated. Please use `active_loop insteaad.", DeprecationWarning, @@ -80,7 +79,6 @@ def active_form(self) -> Dict[Text, Any]: def current_state(self) -> Dict[Text, Any]: """Return the current tracker state as an object.""" - if len(self.events) > 0: latest_event_time = self.events[-1].get("timestamp") else: @@ -100,12 +98,11 @@ def current_state(self) -> Dict[Text, Any]: } def current_slot_values(self) -> Dict[Text, Any]: - """Return the currently set values of the slots""" + """Return the currently set values of the slots.""" return self.slots def get_slot(self, key) -> Optional[Any]: """Retrieves the value of a slot.""" - if key in self.slots: return self.slots[key] else: @@ -133,7 +130,6 @@ def get_latest_entity_values( Returns: List of entity values. """ - entities = self.latest_message.get("entities", []) return ( x.get("value") @@ -144,8 +140,7 @@ def get_latest_entity_values( ) def get_latest_input_channel(self) -> Optional[Text]: - """Get the name of the input_channel of the latest UserUttered event""" - + """Get the name of the input_channel of the latest UserUttered event.""" for e in reversed(self.events): if e.get("event") == "user": return e.get("input_channel") @@ -229,7 +224,8 @@ def applied_events(self) -> List[Dict[Text, Any]]: def undo_till_previous(event_type: Text, done_events: List[Dict[Text, Any]]): """Removes events from `done_events` until the first - occurrence `event_type` is found which is also removed.""" + occurrence `event_type` is found which is also removed. + """ # list gets modified - hence we need to copy events! for e in reversed(done_events[:]): del done_events[-1] @@ -262,7 +258,6 @@ def slots_to_validate(self) -> Dict[Text, Any]: Returns: A mapping of extracted slot candidates and their values. """ - slots: Dict[Text, Any] = {} count: int = 0 @@ -331,7 +326,6 @@ class Action: def name(self) -> Text: """Unique identifier of this simple action.""" - raise NotImplementedError("An action must implement a name") async def run( @@ -356,7 +350,6 @@ async def run( A dictionary of `rasa_sdk.events.Event` instances that is returned through the endpoint """ - raise NotImplementedError("An action must implement its run method") def __str__(self) -> Text: @@ -365,7 +358,9 @@ def __str__(self) -> Text: class ActionExecutionRejection(Exception): """Raising this exception will allow other policies - to predict another action""" + to predict another action + . + """ def __init__(self, action_name: Text, message: Optional[Text] = None) -> None: self.action_name = action_name diff --git a/rasa_sdk/utils.py b/rasa_sdk/utils.py index c058e033b..b1404b48e 100644 --- a/rasa_sdk/utils.py +++ b/rasa_sdk/utils.py @@ -33,6 +33,7 @@ class Element(dict): """Represents an element in a list of elements in a rich message.""" + __acceptable_keys = ["title", "item_url", "image_url", "subtitle", "buttons"] def __init__(self, *args, **kwargs): @@ -46,6 +47,7 @@ def __init__(self, *args, **kwargs): class Button(dict): """Represents a button in a rich message.""" + pass From 110620eee0e0b690d37d3032ad939c38953e7441 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 17:58:48 +0200 Subject: [PATCH 15/19] Add missing newlines --- Makefile | 2 +- proto/health.proto | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 48dd7f577..878d2b0df 100644 --- a/Makefile +++ b/Makefile @@ -68,4 +68,4 @@ generate-grpc: --grpc_python_out=. \ --pyi_out=. \ proto/action_webhook.proto \ - proto/health.proto \ No newline at end of file + proto/health.proto diff --git a/proto/health.proto b/proto/health.proto index 09b3a62f2..d6d4f8a14 100644 --- a/proto/health.proto +++ b/proto/health.proto @@ -8,4 +8,4 @@ message HealthCheckResponse {} service HealthService { rpc Check(HealthCheckRequest) returns (HealthCheckResponse); -} \ No newline at end of file +} From d4b112d3842f560dd1219ae9e4352a370216a917 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 18:04:09 +0200 Subject: [PATCH 16/19] Add suport for reloading custom actions in grpc mode --- rasa_sdk/__main__.py | 1 + rasa_sdk/grpc_server.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/rasa_sdk/__main__.py b/rasa_sdk/__main__.py index c642f35ec..7cbfd6a2b 100644 --- a/rasa_sdk/__main__.py +++ b/rasa_sdk/__main__.py @@ -30,6 +30,7 @@ def main_from_args(args): args.ssl_certificate, args.ssl_keyfile, args.ssl_ca_file, + args.auto_reload, args.endpoints, ) ) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 20dc62948..187aa6090 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -79,15 +79,18 @@ class GRPCActionServerWebhook(action_webhook_pb2_grpc.ActionServiceServicer): def __init__( self, executor: ActionExecutor, + auto_reload: bool = False, tracer_provider: Optional[TracerProvider] = None, ) -> None: """Initializes the ActionServerWebhook. Args: tracer_provider: The tracer provider. + auto_reload: Enable auto-reloading of modules containing Action subclasses. executor: The action executor. """ self.tracer_provider = tracer_provider + self.auto_reload = auto_reload self.executor = executor async def Actions(self, request: ActionsRequest, context) -> ActionsResponse: @@ -100,6 +103,9 @@ async def Actions(self, request: ActionsRequest, context) -> ActionsResponse: Returns: gRPC response. """ + if self.auto_reload: + self.executor.reload() + actions = self.executor.list_actions() response = ActionsResponse() return ParseDict(actions, response) @@ -123,6 +129,8 @@ async def Webhook( ) with tracer.start_as_current_span(span_name, context=tracer_context): check_version_compatibility(request.version) + if self.auto_reload: + self.executor.reload() try: action_call = MessageToDict(request, preserving_proto_field_name=True) result = await self.executor.run(action_call) @@ -195,6 +203,7 @@ async def run_grpc( ssl_certificate: Optional[str] = None, ssl_keyfile: Optional[str] = None, ssl_ca_file: Optional[str] = None, + auto_reload: bool = False, endpoints: str = DEFAULT_ENDPOINTS_PATH, ): """Start a gRPC server to handle incoming action requests. @@ -205,6 +214,7 @@ async def run_grpc( ssl_certificate: File path to the SSL certificate. ssl_keyfile: File path to the SSL key file. ssl_ca_file: File path to the SSL CA certificate file. + auto_reload: Enable auto-reloading of modules containing Action subclasses. endpoints: Path to the endpoints file. """ workers = number_of_sanic_workers() @@ -215,7 +225,7 @@ async def run_grpc( # tracer_provider = get_tracer_provider(endpoints) tracer_provider = None action_webhook_pb2_grpc.add_ActionServiceServicer_to_server( - GRPCActionServerWebhook(executor, tracer_provider), server + GRPCActionServerWebhook(executor, auto_reload, tracer_provider), server ) health_pb2_grpc.add_HealthServiceServicer_to_server( From 0431530c2cc201bab42c21bc2a4f46bf62baa716 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 18:10:32 +0200 Subject: [PATCH 17/19] Add changelog for gRPC --- changelog/1109.feature.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/1109.feature.md diff --git a/changelog/1109.feature.md b/changelog/1109.feature.md new file mode 100644 index 000000000..64c709bca --- /dev/null +++ b/changelog/1109.feature.md @@ -0,0 +1,6 @@ +Rasa SDK now supports gRPC protocol. +This allows users to use gRPC to invoke custom actions. +Users can use secure (TLS) and insecure connections to communicate over gRPC. +To start action server with gRPC use `--grpc` flag. +For SSL support, users can provide `--ssl-keyfile`, `--ssl-certificate` and `--ssl-ca-file`. +Support for `--ssl-password` is not available yet due to a limitation in the gRPC Python library. From 6b78b2b3ac38d3fe22b2da0eb3ee50b154d4d531 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Tue, 18 Jun 2024 18:31:18 +0200 Subject: [PATCH 18/19] Set type for gRPC context to grpc.aio.ServicerContext --- rasa_sdk/grpc_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 187aa6090..3b7f8d3ec 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -113,7 +113,7 @@ async def Actions(self, request: ActionsRequest, context) -> ActionsResponse: async def Webhook( self, request: WebhookRequest, - context, + context: grpc.aio.ServicerContext, ) -> action_webhook_pb2.WebhookResponse: """Handle RPC request for the webhook. From c6ff6f6a6c8585cfbc115c9c69e09fa57456d708 Mon Sep 17 00:00:00 2001 From: Radovan Zivkovic Date: Wed, 19 Jun 2024 10:53:32 +0200 Subject: [PATCH 19/19] Start gRPC server with gzip compression enabled --- rasa_sdk/grpc_server.py | 21 ++++++++++++++------- rasa_sdk/tracing/utils.py | 5 ++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/rasa_sdk/grpc_server.py b/rasa_sdk/grpc_server.py index 3b7f8d3ec..f3f875d8a 100644 --- a/rasa_sdk/grpc_server.py +++ b/rasa_sdk/grpc_server.py @@ -11,6 +11,7 @@ from concurrent import futures from grpc import aio from google.protobuf.json_format import MessageToDict, ParseDict +from opentelemetry import trace from rasa_sdk.constants import ( DEFAULT_SERVER_PORT, @@ -40,7 +41,7 @@ ActionMissingDomainException, ) from rasa_sdk.tracing.utils import ( - get_tracer_and_context, + get_tracer_provider, TracerProvider, ) from rasa_sdk.utils import ( @@ -124,10 +125,14 @@ async def Webhook( Returns: gRPC response. """ - tracer, tracer_context, span_name = get_tracer_and_context( - self.tracer_provider, request + span_name = "GRPCActionServerWebhook.Webhook" + tracer = ( + self.tracer_provider.get_tracer(span_name) + if self.tracer_provider + else trace.get_tracer(span_name) ) - with tracer.start_as_current_span(span_name, context=tracer_context): + + with tracer.start_as_current_span(span_name): check_version_compatibility(request.version) if self.auto_reload: self.executor.reload() @@ -218,12 +223,14 @@ async def run_grpc( endpoints: Path to the endpoints file. """ workers = number_of_sanic_workers() - server = aio.server(futures.ThreadPoolExecutor(max_workers=workers)) + server = aio.server( + futures.ThreadPoolExecutor(max_workers=workers), + compression=grpc.Compression.Gzip, + ) initialise_interrupts(server) executor = ActionExecutor() executor.register_package(action_package_name) - # tracer_provider = get_tracer_provider(endpoints) - tracer_provider = None + tracer_provider = get_tracer_provider(endpoints) action_webhook_pb2_grpc.add_ActionServiceServicer_to_server( GRPCActionServerWebhook(executor, auto_reload, tracer_provider), server ) diff --git a/rasa_sdk/tracing/utils.py b/rasa_sdk/tracing/utils.py index bbf56374c..18c848439 100644 --- a/rasa_sdk/tracing/utils.py +++ b/rasa_sdk/tracing/utils.py @@ -1,4 +1,3 @@ -from rasa_sdk.grpc_py.action_webhook_pb2 import WebhookRequest from rasa_sdk.tracing import config from opentelemetry import trace from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator @@ -18,12 +17,12 @@ def get_tracer_provider(endpoints_file: str) -> Optional[TracerProvider]: def get_tracer_and_context( - tracer_provider: Optional[TracerProvider], request: Union[Request, WebhookRequest] + tracer_provider: Optional[TracerProvider], request: Union[Request] ) -> Tuple[Any, Any, Text]: """Gets tracer and context.""" span_name = "create_app.webhook" - if tracer_provider is None or isinstance(request, WebhookRequest): + if tracer_provider is None: tracer = trace.get_tracer(span_name) context = None else: