├── .github ├── trigger.txt └── workflows │ ├── cicd.yml │ ├── cleanup-branch.yml │ └── cleanup-nightly.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── actions ├── __init__.py ├── actions.py ├── custom_forms.py ├── custom_forms_config.yml ├── handoff.py ├── handoff_config.yml ├── parsing.py ├── profile_db.py └── requirements-actions.txt ├── chatroom_handoff.html ├── config.yml ├── credentials.yml ├── data ├── nlu │ └── nlu.yml ├── rules │ ├── rules.yml │ └── rules_handoff.yml └── stories │ ├── stories.yml │ ├── stories_switch_from_pay_credit_card.yml │ ├── stories_switch_from_search_transactions.yml │ └── stories_switch_from_transfer_money.yml ├── deploy ├── gcr-auth.json └── values.yml ├── domain.yml ├── endpoints.yml ├── handoff.gif ├── images ├── cicd.png └── financial-demo-eks-test-vpc.png ├── pyproject.toml ├── requirements-dev.txt ├── requirements.txt ├── run_rasa_action_server_with_ide.py ├── run_rasa_shell_with_ide.py ├── run_rasa_test_with_ide.py ├── scripts ├── delete_all_test_clusters.py ├── patch_gcr_auth_json.py ├── smoketest.py └── wait_for_external_ip.sh ├── setup.cfg └── tests ├── __init__.py ├── conftest.py ├── data ├── all_slots_filled_tracker.json ├── empty_tracker.json ├── pay_cc_confirmed.json └── pay_cc_not_confirmed.json ├── db_test.py ├── test_actions.py └── test_stories.yml /.github/trigger.txt: -------------------------------------------------------------------------------- 1 | # A dummy file to trigger the workflow without making any actual change. 2 | # Just change something arbitrary in this text, like a space . -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | # DISABLED until we change the workflow to reflect the actual live deployment 2 | # name: financial-demo cicd 3 | 4 | # on: 5 | # workflow_dispatch: 6 | # push: 7 | # paths: 8 | # - 'data/**' 9 | # - 'config.yml' 10 | # - 'domain.yml' 11 | # - 'actions/**' 12 | # - 'requirements.txt' 13 | # - 'Dockerfile' 14 | # - '.github/trigger.txt' 15 | 16 | # env: 17 | # # Keep these values in sync with the values in the Makefile 18 | # AWS_REGION: us-west-2 19 | # AWS_ECR_URI: 024629701212.dkr.ecr.us-west-2.amazonaws.com 20 | # AWS_ECR_REPOSITORY: financial-demo 21 | # AWS_S3_BUCKET_NAME: rasa-financial-demo 22 | 23 | # # Notes: 24 | # # (-) rasa & rasa-sdk versions are extracted from `requirements.txt` 25 | # # (-) TODO: extract this from Makefile 26 | # RASA_ENTERPRISE_VERSION: "1.1.0" 27 | 28 | # jobs: 29 | # params: 30 | # name: params 31 | # runs-on: ubuntu-latest 32 | # # Map step outputs to job outputs, for use by downstream jobs 33 | # outputs: 34 | # git_branch: ${{ steps.git.outputs.git_branch }} 35 | # do_deploy_to_prod_cluster: ${{ steps.git.outputs.do_deploy_to_prod_cluster }} 36 | 37 | # rasa_enterprise_version: ${{ steps.versions.outputs.rasa_enterprise_version }} 38 | # rasa_version: ${{ steps.versions.outputs.rasa_version }} 39 | # rasa_sdk_version: ${{ steps.versions.outputs.rasa_sdk_version }} 40 | 41 | # do_training: ${{ steps.rasa_model.outputs.do_training }} 42 | 43 | # aws_region: ${{ steps.aws.outputs.aws_region }} 44 | # aws_s3: ${{ steps.aws.outputs.aws_s3 }} 45 | 46 | # do_create_test_cluster: ${{ steps.aws.outputs.do_create_test_cluster }} 47 | 48 | # steps: 49 | # - name: git 50 | # id: git 51 | # run: | 52 | # echo $GITHUB_REF 53 | # git_branch=$(echo ${GITHUB_REF##*/}) 54 | 55 | # echo "::set-output name=git_branch::$git_branch" 56 | 57 | # if [[ $git_branch == "main" ]] 58 | # then 59 | # echo "::set-output name=do_deploy_to_prod_cluster::true" 60 | # else 61 | # echo "::set-output name=do_deploy_to_prod_cluster::false" 62 | # fi 63 | 64 | # - name: checkout 65 | # uses: actions/checkout@v2 66 | 67 | # - name: files 68 | # id: files 69 | # if: github.event_name == 'push' 70 | # uses: jitterbit/get-changed-files@v1 71 | 72 | # - name: Configure AWS Credentials 73 | # uses: aws-actions/configure-aws-credentials@v1 74 | # with: 75 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 76 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 77 | # aws-region: ${{ env.AWS_REGION }} 78 | 79 | # - name: versions 80 | # id: versions 81 | # run: | 82 | # rasa_version=$( cat requirements.txt | grep 'rasa\[spacy\]' | cut -d'=' -f 3 )-spacy-en 83 | # rasa_sdk_version=$( cat requirements.txt | grep 'rasa-sdk' | cut -d'=' -f 3 ) 84 | 85 | # echo "::set-output name=rasa_enterprise_version::${{ env.RASA_ENTERPRISE_VERSION }}" 86 | # echo "::set-output name=rasa_version::$rasa_version" 87 | # echo "::set-output name=rasa_sdk_version::$rasa_sdk_version" 88 | 89 | # - name: rasa_model 90 | # id: rasa_model 91 | # run: | 92 | # github_event_name=$(echo ${GITHUB_EVENT_NAME}) 93 | # echo "github_event_name: $github_event_name" 94 | 95 | # aws_s3_rasa_model_exists=$( make aws-s3-rasa-model-exists ) 96 | # echo "aws_s3_rasa_model_exists: $aws_s3_rasa_model_exists" 97 | 98 | # if [[ $github_event_name == 'workflow_dispatch' ]] || \ 99 | # [[ $aws_s3_rasa_model_exists == 'false' ]] || \ 100 | # [[ "${{ steps.files.outputs.all }}" == *"data/"* ]] || \ 101 | # [[ "${{ steps.files.outputs.all }}" == *"config.yml"* ]] || \ 102 | # [[ "${{ steps.files.outputs.all }}" == *"domain.yml"* ]] || \ 103 | # [[ "${{ steps.files.outputs.all }}" == *"requirements.txt"* ]] 104 | # then 105 | # echo "::set-output name=do_training::true" 106 | # else 107 | # echo "::set-output name=do_training::false" 108 | # fi 109 | 110 | # - name: aws 111 | # id: aws 112 | # run: | 113 | # echo "::set-output name=aws_region::${{ env.AWS_REGION }}" 114 | # echo "::set-output name=aws_s3::${{ env.AWS_S3_BUCKET_NAME }}" 115 | 116 | # if [[ $(make aws-eks-cluster-exists) = True ]] 117 | # then 118 | # echo "::set-output name=do_create_test_cluster::false" 119 | # else 120 | # echo "::set-output name=do_create_test_cluster::true" 121 | # fi 122 | 123 | 124 | 125 | # params_summary: 126 | # name: params_summary 127 | # runs-on: ubuntu-latest 128 | # needs: [params] 129 | # steps: 130 | # - name: params_summary 131 | # run: | 132 | # echo git_branch: ${{ needs.params.outputs.git_branch }} 133 | # echo do_deploy_to_prod_cluster: ${{ needs.params.outputs.do_deploy_to_prod_cluster }} 134 | 135 | # echo rasa_enterprise_version : ${{ needs.params.outputs.rasa_enterprise_version }} 136 | # echo rasa_version : ${{ needs.params.outputs.rasa_version }} 137 | # echo rasa_sdk_version : ${{ needs.params.outputs.rasa_sdk_version }} 138 | 139 | # echo do_training: ${{ needs.params.outputs.do_training }} 140 | 141 | # echo aws_region: ${{ needs.params.outputs.aws_region }} 142 | # echo aws_s3: ${{ needs.params.outputs.aws_s3 }} 143 | 144 | # echo do_create_test_cluster: ${{ needs.params.outputs.do_create_test_cluster }} 145 | 146 | # # - name: Dump GitHub context 147 | # # env: 148 | # # GITHUB_CONTEXT: ${{ toJSON(github) }} 149 | # # run: | 150 | # # echo "$GITHUB_CONTEXT" 151 | # # exit 1 152 | 153 | 154 | # action_server: 155 | # name: action_server (to AWS ECR) 156 | # runs-on: ubuntu-latest 157 | # needs: [params, params_summary] 158 | # steps: 159 | # - name: checkout 160 | # uses: actions/checkout@v2 161 | 162 | # - name: Set up Python 3.7 163 | # uses: actions/setup-python@v2 164 | # with: 165 | # python-version: 3.7 166 | 167 | # - name: Cache pip 168 | # # see: https://docs.github.com/en/actions/guides/building-and-testing-python#caching-dependencies 169 | # uses: actions/cache@v2 170 | # with: 171 | # path: ~/.cache/pip 172 | # key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }} 173 | # restore-keys: | 174 | # ${{ runner.os }}-pip- 175 | # ${{ runner.os }}- 176 | 177 | 178 | # - name: Configure AWS Credentials 179 | # uses: aws-actions/configure-aws-credentials@v1 180 | # with: 181 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 182 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 183 | # aws-region: ${{ env.AWS_REGION }} 184 | 185 | # - name: Do it all 186 | # run: | 187 | # python -m pip install -U pip 188 | # pip install -r requirements-dev.txt 189 | # make lint 190 | # make types 191 | # make test 192 | # make aws-ecr-docker-login 193 | # make docker-pull 194 | # make docker-build 195 | # make docker-push 196 | 197 | # rasa_model: 198 | # name: rasa_model (to AWS S3) 199 | # runs-on: ubuntu-latest 200 | # needs: [params, params_summary] 201 | # services: 202 | # # Label used to access the service container 203 | # duckling: 204 | # image: rasa/duckling 205 | # ports: 206 | # # Maps port 8000 on service container to port 8000 on host VM 207 | # - 8000:8000 208 | # steps: 209 | # - name: checkout 210 | # if: needs.params.outputs.do_training == 'true' 211 | # uses: actions/checkout@v2 212 | 213 | # - name: Install dependencies 214 | # if: needs.params.outputs.do_training == 'true' 215 | # run: | 216 | # python -m pip install -U pip 217 | # pip install -r requirements.txt 218 | # python -m spacy download en_core_web_md 219 | # python -m spacy link en_core_web_md en 220 | 221 | # - name: Configure AWS Credentials 222 | # if: needs.params.outputs.do_training == 'true' 223 | # uses: aws-actions/configure-aws-credentials@v1 224 | # with: 225 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 226 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 227 | # aws-region: ${{ needs.params.outputs.aws_region }} 228 | 229 | # - name: Do it all 230 | # if: needs.params.outputs.do_training == 'true' 231 | # run: | 232 | # make rasa-train 233 | # make rasa-test 234 | # make aws-s3-upload-rasa-model 235 | 236 | # aws_eks_create_test_cluster: 237 | # name: aws_eks_create_test_cluster 238 | # runs-on: ubuntu-latest 239 | # needs: [params, params_summary] 240 | # steps: 241 | # - name: checkout 242 | # if: needs.params.outputs.do_create_test_cluster == 'true' 243 | # uses: actions/checkout@v2 244 | 245 | # - name: Configure AWS Credentials 246 | # if: needs.params.outputs.do_create_test_cluster == 'true' 247 | # uses: aws-actions/configure-aws-credentials@v1 248 | # with: 249 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 250 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 251 | # aws-region: ${{ needs.params.outputs.aws_region }} 252 | 253 | # - name: Do it all 254 | # if: needs.params.outputs.do_create_test_cluster == 'true' 255 | # run: | 256 | # make install-eksctl 257 | # make install-kubectl 258 | # make aws-eks-cluster-create 259 | 260 | # deploy_to_test_cluster: 261 | # name: deploy_to_test_cluster 262 | # runs-on: ubuntu-latest 263 | # needs: [params, aws_eks_create_test_cluster, rasa_model, action_server] 264 | # steps: 265 | # - name: checkout 266 | # uses: actions/checkout@v2 267 | 268 | # - name: Configure AWS Credentials 269 | # uses: aws-actions/configure-aws-credentials@v1 270 | # with: 271 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 272 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 273 | # aws-region: ${{ needs.params.outputs.aws_region }} 274 | 275 | # - name: install CLIs & dependencies 276 | # run: | 277 | # make install-eksctl 278 | # make install-kubectl 279 | # make install-helm 280 | # make install-jp 281 | 282 | # - name: configure kubectl 283 | # run: | 284 | # make aws-eks-cluster-update-kubeconfig 285 | 286 | # - name: create namespace if not exists 287 | # run: | 288 | # make aws-eks-namespace-create 289 | 290 | # # Enable cluster to pull the private rasa enterprise image 291 | # - name: create/refresh gcr-pull-secret 292 | # env: 293 | # GCR_AUTH_JSON_PRIVATE_KEY_ID: ${{ secrets.GCR_AUTH_JSON_PRIVATE_KEY_ID }} 294 | # GCR_AUTH_JSON_PRIVATE_KEY: ${{ secrets.GCR_AUTH_JSON_PRIVATE_KEY }} 295 | # GCR_AUTH_JSON_CLIENT_EMAIL: ${{ secrets.GCR_AUTH_JSON_CLIENT_EMAIL }} 296 | # GCR_AUTH_JSON_CLIENT_ID: ${{ secrets.GCR_AUTH_JSON_CLIENT_ID }} 297 | # run: | 298 | # make pull-secret-gcr-create 299 | 300 | # # Enable cluster to pull the private action server image 301 | # - name: create/refresh ecr-pull-secret 302 | # run: | 303 | # make pull-secret-ecr-create 304 | 305 | # - name: Install or Upgrade Rasa Enterprise 306 | # env: 307 | # GLOBAL_POSTGRESQL_POSTGRESQLPASSWORD: ${{ secrets.GLOBAL_POSTGRESQL_POSTGRESQLPASSWORD }} 308 | # GLOBAL_REDIS_PASSWORD: ${{ secrets.GLOBAL_REDIS_PASSWORD }} 309 | # RABBITMQ_RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_RABBITMQ_PASSWORD }} 310 | # RASAX_DEBUG_MODE: true 311 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 312 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 313 | # RASAX_JWTSECRET: ${{ secrets.RASAX_JWTSECRET }} 314 | # RASAX_PASSWORDSALT: ${{ secrets.RASAX_PASSWORDSALT }} 315 | # RASAX_TOKEN: ${{ secrets.RASAX_TOKEN }} 316 | # RASA_TOKEN: ${{ secrets.RASA_TOKEN }} 317 | # run: | 318 | # make rasa-enterprise-install 319 | 320 | # - name: Check Rasa Enterprise Health 321 | # run: | 322 | # # Need to give rasa-prod container some time... 323 | # sleep 30 324 | # make rasa-enterprise-check-health 325 | 326 | # - name: Deploy rasa model 327 | # env: 328 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 329 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 330 | 331 | # run: | 332 | # make aws-s3-download-rasa-model 333 | # make rasa-enterprise-model-delete 334 | # make rasa-enterprise-model-upload 335 | # sleep 2 336 | # make rasa-enterprise-model-tag 337 | # # Give rasa-prod some time to unpack & load the model 338 | # sleep 60 339 | 340 | # - name: Smoketest 341 | # env: 342 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 343 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 344 | # run: | 345 | # make rasa-enterprise-smoketest 346 | 347 | 348 | # deploy_to_prod_cluster: 349 | # name: deploy_to_prod_cluster 350 | # runs-on: ubuntu-latest 351 | # needs: [params, deploy_to_test_cluster] 352 | # if: ${{ needs.params.outputs.do_deploy_to_prod_cluster == 'true' }} 353 | # steps: 354 | # - name: checkout 355 | # uses: actions/checkout@v2 356 | 357 | # - name: Configure AWS Credentials 358 | # uses: aws-actions/configure-aws-credentials@v1 359 | # with: 360 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 361 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 362 | # aws-region: ${{ needs.params.outputs.aws_region }} 363 | 364 | # - name: install CLIs & dependencies 365 | # run: | 366 | # make install-eksctl 367 | # make install-kubectl 368 | # make install-helm 369 | # make install-jp 370 | 371 | # - name: configure kubectl 372 | # run: | 373 | # make aws-eks-cluster-update-kubeconfig AWS_EKS_CLUSTER_NAME=financial-demo-production 374 | 375 | # - name: create namespace if not exists 376 | # run: | 377 | # make aws-eks-namespace-create 378 | 379 | # # Enable cluster to pull the private rasa enterprise image 380 | # - name: create/refresh gcr-pull-secret 381 | # env: 382 | # GCR_AUTH_JSON_PRIVATE_KEY_ID: ${{ secrets.GCR_AUTH_JSON_PRIVATE_KEY_ID }} 383 | # GCR_AUTH_JSON_PRIVATE_KEY: ${{ secrets.GCR_AUTH_JSON_PRIVATE_KEY }} 384 | # GCR_AUTH_JSON_CLIENT_EMAIL: ${{ secrets.GCR_AUTH_JSON_CLIENT_EMAIL }} 385 | # GCR_AUTH_JSON_CLIENT_ID: ${{ secrets.GCR_AUTH_JSON_CLIENT_ID }} 386 | # run: | 387 | # make pull-secret-gcr-create 388 | 389 | # # Enable cluster to pull the private action server image 390 | # - name: create/refresh ecr-pull-secret 391 | # run: | 392 | # make pull-secret-ecr-create 393 | 394 | # - name: Install or Upgrade Rasa Enterprise 395 | # env: 396 | # GLOBAL_POSTGRESQL_POSTGRESQLPASSWORD: ${{ secrets.GLOBAL_POSTGRESQL_POSTGRESQLPASSWORD }} 397 | # GLOBAL_REDIS_PASSWORD: ${{ secrets.GLOBAL_REDIS_PASSWORD }} 398 | # RABBITMQ_RABBITMQ_PASSWORD: ${{ secrets.RABBITMQ_RABBITMQ_PASSWORD }} 399 | # RASAX_DEBUG_MODE: false 400 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 401 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 402 | # RASAX_JWTSECRET: ${{ secrets.RASAX_JWTSECRET }} 403 | # RASAX_PASSWORDSALT: ${{ secrets.RASAX_PASSWORDSALT }} 404 | # RASAX_TOKEN: ${{ secrets.RASAX_TOKEN }} 405 | # RASA_TOKEN: ${{ secrets.RASA_TOKEN }} 406 | # run: | 407 | # make rasa-enterprise-install 408 | 409 | # - name: Check Rasa Enterprise Health 410 | # run: | 411 | # # Need to give rasa-prod container some time... 412 | # sleep 30 413 | # make rasa-enterprise-check-health 414 | 415 | # - name: Deploy rasa model 416 | # env: 417 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 418 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 419 | 420 | # run: | 421 | # make aws-s3-download-rasa-model 422 | # make rasa-enterprise-model-delete 423 | # make rasa-enterprise-model-upload 424 | # sleep 2 425 | # make rasa-enterprise-model-tag 426 | # # Give rasa-prod some time to unpack & load the model 427 | # sleep 60 428 | 429 | # - name: Smoketest 430 | # env: 431 | # RASAX_INITIALUSER_USERNAME: ${{ secrets.RASAX_INITIALUSER_USERNAME }} 432 | # RASAX_INITIALUSER_PASSWORD: ${{ secrets.RASAX_INITIALUSER_PASSWORD }} 433 | # run: | 434 | # make rasa-enterprise-smoketest 435 | -------------------------------------------------------------------------------- /.github/workflows/cleanup-branch.yml: -------------------------------------------------------------------------------- 1 | # # Cleans up infrastructure & artifacts. Manually & when branch gets deleted 2 | 3 | # # Note: the workflow_dispatch & schedule events will only trigger if the workflow 4 | # # file is on the default (main) branch. 5 | 6 | # # See: 7 | # # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch 8 | # # https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow 9 | # # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule 10 | # name: financial-demo cleanup-branch 11 | 12 | # on: 13 | # workflow_dispatch: 14 | # delete: 15 | 16 | # env: 17 | # # Keep these values in sync with the values in the Makefile 18 | # AWS_REGION: us-west-2 19 | # AWS_ECR_URI: 024629701212.dkr.ecr.us-west-2.amazonaws.com 20 | # AWS_ECR_REPOSITORY: financial-demo 21 | # AWS_S3_BUCKET_NAME: rasa-financial-demo 22 | 23 | # jobs: 24 | # cleanup_deleted_branch: 25 | # if: github.event.ref_type == 'branch' 26 | # name: cleanup_deleted_branch 27 | # runs-on: ubuntu-latest 28 | # steps: 29 | # - name: checkout 30 | # uses: actions/checkout@v2 31 | 32 | # - name: Set up Python 3.7 33 | # uses: actions/setup-python@v2 34 | # with: 35 | # python-version: 3.7 36 | 37 | # - name: Cache pip 38 | # # see: https://docs.github.com/en/actions/guides/building-and-testing-python#caching-dependencies 39 | # uses: actions/cache@v2 40 | # with: 41 | # path: ~/.cache/pip 42 | # key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }} 43 | # restore-keys: | 44 | # ${{ runner.os }}-pip- 45 | # ${{ runner.os }}- 46 | 47 | 48 | # - name: Configure AWS Credentials 49 | # uses: aws-actions/configure-aws-credentials@v1 50 | # with: 51 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 52 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 53 | # aws-region: ${{ env.AWS_REGION }} 54 | 55 | # - name: install CLIs & dependencies 56 | # run: | 57 | # make install-eksctl 58 | # make install-kubectl 59 | # make install-helm 60 | # make install-jp 61 | 62 | # - name: Cleanup ECR 63 | # run: | 64 | # echo "Delete action server image of branch ${{ github.event.ref }}" 65 | # make aws-ecr-docker-login 66 | # make aws-ecr-delete-image GIT_BRANCH_NAME=${{ github.event.ref }} 67 | 68 | # - name: Cleanup S3 69 | # run: | 70 | # echo "Delete rasa model of branch ${{ github.event.ref }}" 71 | # make aws-s3-delete-rasa-model GIT_BRANCH_NAME=${{ github.event.ref }} 72 | 73 | # - name: Cleanup EKS & all Persistent Volume Claims / Persistent Volumes 74 | # run: | 75 | # echo "Delete test cluster of branch ${{ github.event.ref }} and PVCs" 76 | # make aws-eks-cluster-update-kubeconfig GIT_BRANCH_NAME=${{ github.event.ref }} 77 | # make rasa-enterprise-uninstall 78 | # make rasa-enterprise-delete-pvc-all 79 | # make aws-eks-cluster-delete GIT_BRANCH_NAME=${{ github.event.ref }} 80 | -------------------------------------------------------------------------------- /.github/workflows/cleanup-nightly.yml: -------------------------------------------------------------------------------- 1 | # # Cleans up EKS test clusters. Manually & every night at 3:30 UTC 2 | 3 | # # Note: the workflow_dispatch & schedule events will only trigger if the workflow 4 | # # file is on the default (main) branch. 5 | 6 | # # See: 7 | # # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#workflow_dispatch 8 | # # https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow 9 | # # https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule 10 | # name: financial-demo cleanup-nightly 11 | 12 | # on: 13 | # workflow_dispatch: 14 | # schedule: 15 | # - cron: '30 3 * * *' 16 | 17 | 18 | # env: 19 | # # Keep these values in sync with the values in the Makefile 20 | # AWS_REGION: us-west-2 21 | 22 | # jobs: 23 | # cleanup_nightly: 24 | # name: cleanup_nightly 25 | # runs-on: ubuntu-latest 26 | # steps: 27 | # - name: checkout 28 | # uses: actions/checkout@v2 29 | 30 | # - name: Set up Python 3.7 31 | # uses: actions/setup-python@v2 32 | # with: 33 | # python-version: 3.7 34 | 35 | # - name: Cache pip 36 | # # see: https://docs.github.com/en/actions/guides/building-and-testing-python#caching-dependencies 37 | # uses: actions/cache@v2 38 | # with: 39 | # path: ~/.cache/pip 40 | # key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }} 41 | # restore-keys: | 42 | # ${{ runner.os }}-pip- 43 | # ${{ runner.os }}- 44 | 45 | 46 | # - name: Configure AWS Credentials 47 | # uses: aws-actions/configure-aws-credentials@v1 48 | # with: 49 | # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 50 | # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 51 | # aws-region: ${{ env.AWS_REGION }} 52 | 53 | # - name: install CLIs & dependencies 54 | # run: | 55 | # make install-eksctl 56 | # make install-kubectl 57 | # make install-helm 58 | # make install-jp 59 | 60 | # - name: Cleanup EKS 61 | # run: | 62 | # echo "Delete all test clusters" 63 | # make aws-eks-cluster-delete-all-test-clusters 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # IDE Stuff 10 | .vscode/ 11 | .history 12 | *.DS_Store 13 | *.wpr 14 | *.wpu 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # SageMath parsed files 89 | *.sage.py 90 | 91 | # Environments 92 | #.env 93 | .venv 94 | env/ 95 | venv/ 96 | ENV/ 97 | env.bak/ 98 | venv.bak/ 99 | 100 | # Spyder project settings 101 | .spyderproject 102 | .spyproject 103 | 104 | # Rope project settings 105 | .ropeproject 106 | 107 | # mkdocs documentation 108 | /site 109 | 110 | # mypy 111 | .mypy_cache/ 112 | 113 | # pytype 114 | .pytype/ 115 | 116 | # Rasa stuff 117 | models/ 118 | *.db* 119 | *.dot 120 | terms/ 121 | results/ 122 | 123 | # Secret & local files 124 | secret/ 125 | run_rasa* 126 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/ambv/black 3 | rev: stable 4 | hooks: 5 | - id: black 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rasa/rasa-sdk:3.1.0 2 | 3 | COPY actions /app/actions 4 | 5 | USER root 6 | RUN pip install --no-cache-dir -r /app/actions/requirements-actions.txt 7 | 8 | USER 1001 9 | CMD ["start", "--actions", "actions"] 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /actions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/financial-demo/7627cebef1571d55855b886cf4ac20459af9566c/actions/__init__.py -------------------------------------------------------------------------------- /actions/custom_forms.py: -------------------------------------------------------------------------------- 1 | """Customization to deal nicely with repeated slot validation failures.""" 2 | import abc 3 | from typing import Dict, Text, Any, List 4 | import logging 5 | import pathlib 6 | import ruamel.yaml 7 | from rasa_sdk import utils 8 | from rasa_sdk.forms import FormValidationAction 9 | from rasa_sdk.events import ( 10 | SlotSet, 11 | EventType, 12 | LoopInterrupted, 13 | ActionExecutionRejected, 14 | ) 15 | from rasa_sdk import Tracker 16 | from rasa_sdk.executor import CollectingDispatcher 17 | 18 | 19 | logger = logging.getLogger(__name__) 20 | 21 | # these slots are used to store information needed to nicely deal with 22 | # repeated slot validation failures 23 | RVF_SLOT = "repeated_validation_failures" 24 | CF_SLOT = "AA_CONTINUE_FORM" 25 | 26 | 27 | here = pathlib.Path(__file__).parent.absolute() 28 | custom_forms_config = ( 29 | ruamel.yaml.safe_load(open(f"{here}/custom_forms_config.yml", "r")) or {} 30 | ).get("custom_forms", {}) 31 | 32 | MAX_VALIDATION_FAILURES = custom_forms_config.get("max_validation_failures", 2) 33 | 34 | 35 | class CustomFormValidationAction(FormValidationAction, metaclass=abc.ABCMeta): 36 | """Validates if slot values are valid and handles repeated validation failures. 37 | 38 | To use, you add the following to your bot: 39 | 40 | (-) Include these two intents: 41 | - affirm 42 | - deny 43 | 44 | (-) In domain.yml define these slots: 45 | 46 | slots: 47 | repeated_validation_failures: 48 | type: any 49 | AA_CONTINUE_FORM: 50 | type: any 51 | 52 | (-) In domain.yml, for each form, declare 'AA_CONTINUE_FORM' as first required slot. 53 | 54 | For example: 55 | 56 | forms: 57 | cc_payment_form: 58 | AA_CONTINUE_FORM: 59 | - type: from_intent 60 | intent: affirm 61 | value: yes 62 | - type: from_intent 63 | intent: deny 64 | value: no 65 | - type: from_text 66 | intent: 67 | - inform 68 | - cc_payment_form 69 | 70 | (-) In domain.yml, for each form, define the 'utter_{form}_AA_CONTINUE_FORM' response, 71 | using /affirm & /deny buttons. 72 | 73 | For example: 74 | 75 | utter_ask_cc_payment_form_AA_CONTINUE_FORM: 76 | - buttons: 77 | - payload: /affirm 78 | title: Yes 79 | - payload: /deny 80 | title: No, cancel the transaction 81 | text: Would you like to continue scheduling the credit card payment? 82 | 83 | (-) In the custom action Class that validates slots, subclass from 84 | 'CustomFormValidationAction' instead of from 'FormValidationAction'. 85 | Optionally, add an 'explain_{slot}' method for every slot that the bot 86 | should explain in some detail if the user is repeatedly provides answer that 87 | cannot be validated. 88 | 89 | For example: 90 | 91 | class ValidatePayCCForm(CustomFormValidationAction): 92 | async def explain_credit_card( 93 | self, 94 | value: Text, 95 | dispatcher: CollectingDispatcher, 96 | tracker: Tracker, 97 | domain: Dict[Text, Any], 98 | ) -> Dict[Text, Any]: 99 | 100 | # Bot utters a message that explains the slot 101 | dispatcher.utter_message( ) 102 | 103 | # optionally, you can set slots by returning a dict 104 | return {} 105 | """ 106 | 107 | # Avoids registering this class as a custom action 108 | @abc.abstractmethod 109 | def name(self) -> Text: 110 | """Unique identifier of the CustomFormValidationAction""" 111 | 112 | raise NotImplementedError("A CustomFormValidationAction must implement a name") 113 | 114 | async def validate( 115 | self, 116 | dispatcher: CollectingDispatcher, 117 | tracker: Tracker, 118 | domain: Dict, 119 | ) -> List[EventType]: 120 | """Validates slots by calling a validation function for each slot. 121 | 122 | Calls an explain function for the requested slot when validation fails 123 | MAX_VALIDATION_FAILURES in a row, and sets 'AA_CONTINUE_FORM' slot to None, which 124 | triggers the bot to utter the 'utter_ask_{form}_AA_CONTINUE_FORM' template. 125 | 126 | Args: 127 | dispatcher: the dispatcher which is used to send messages back to the user. 128 | tracker: the conversation tracker for the current user. 129 | domain: the bot's domain. 130 | Returns: 131 | `SlotSet` events for every validated slot. 132 | """ 133 | events = [] 134 | 135 | if not tracker.get_slot(CF_SLOT): 136 | events.append(SlotSet(CF_SLOT, "yes")) 137 | 138 | events.extend(await super().validate(dispatcher, tracker, domain)) 139 | 140 | events.extend( 141 | await self.repeated_validation_failures(dispatcher, tracker, domain, events) 142 | ) 143 | 144 | return events 145 | 146 | async def repeated_validation_failures( 147 | self, 148 | dispatcher: CollectingDispatcher, 149 | tracker: Tracker, 150 | domain: Dict, 151 | events: List[EventType], 152 | ) -> List[EventType]: 153 | """Updates the slot repeated_validation_failures, and sets required form slot 154 | `AA_CONTINUE_FORM` to None when the threshold is reached. 155 | 156 | This will trigger utter_ask_{form}_AA_CONTINUE_FORM, asking the user if they want 157 | to continue with this form or not. 158 | """ 159 | rvf_events: List[EventType] = [] 160 | requested_slot = tracker.get_slot("requested_slot") 161 | 162 | # Only do this while form is asking for a certain slot 163 | if not requested_slot: 164 | return rvf_events 165 | 166 | # if the requested slot was not extracted, interupt the form 167 | interrupt_form = False 168 | if not events: 169 | interrupt_form = True 170 | else: 171 | for event in events: 172 | if event["event"] == "slot" and event["name"] == "requested_slot": 173 | interrupt_form = True 174 | 175 | if interrupt_form: 176 | # Sending LoopInterrupted will prevent rasa.core from asking for the slot 177 | rvf_events.append(LoopInterrupted(is_interrupted=True)) 178 | 179 | # Sending ActionExecutionRejected will allow rasa.core to predict 180 | # something else before continuing with the form 181 | rvf_events.append(ActionExecutionRejected(action_name=self.form_name())) 182 | 183 | return rvf_events 184 | 185 | # Skip if validate_{slot} turned off the form by setting requested_slot to None 186 | for event in events: 187 | if ( 188 | event["event"] == "slot" 189 | and event["name"] == "requested_slot" 190 | and not event["value"] 191 | ): 192 | rvf_events.append(SlotSet(RVF_SLOT, 0)) 193 | return rvf_events 194 | 195 | rvf = tracker.get_slot(RVF_SLOT) 196 | if rvf: 197 | rvf = int(rvf) 198 | else: 199 | # initialize counter to 0 200 | rvf = 0 201 | 202 | # check if validation of the requested_slot failed 203 | validation_failed = True 204 | for event in events: 205 | if ( 206 | event["event"] == "slot" 207 | and event["name"] == requested_slot 208 | and event["value"] 209 | ): 210 | validation_failed = False 211 | break 212 | 213 | # keep track of repeated validation failures 214 | if validation_failed: 215 | rvf += 1 216 | else: 217 | rvf = 0 218 | 219 | if rvf >= MAX_VALIDATION_FAILURES: 220 | rvf_events.extend( 221 | await self.explain_requested_slot(dispatcher, tracker, domain) 222 | ) 223 | # reset counter 224 | rvf = 0 225 | 226 | # Triggers 'utter_ask_{form}_AA_CONTINUE_FORM' 227 | rvf_events.append(SlotSet(CF_SLOT, None)) 228 | 229 | rvf_events.append(SlotSet(RVF_SLOT, rvf)) 230 | return rvf_events 231 | 232 | async def explain_requested_slot( 233 | self, 234 | dispatcher: CollectingDispatcher, 235 | tracker: Tracker, 236 | domain: Dict[Text, Any], 237 | ) -> List[EventType]: 238 | """Explains requested slot by calling an explain function for the slot. 239 | 240 | Args: 241 | dispatcher: the dispatcher which is used to 242 | send messages back to the user. 243 | tracker: the conversation tracker for the current user. 244 | domain: the bot's domain. 245 | Returns: 246 | `SlotSet` events for the explained slot (Optional). 247 | """ 248 | slot_name = tracker.get_slot("requested_slot") 249 | if not slot_name: 250 | return [] 251 | 252 | slot_value = tracker.get_slot(slot_name) 253 | 254 | method_name = f"explain_{slot_name.replace('-','_')}" 255 | explain_method = getattr(self, method_name, None) 256 | 257 | if not explain_method: 258 | logger.debug( 259 | f"Skipping explanation for `{slot_name}`: there is no explanation " 260 | "method specified." 261 | ) 262 | return [] 263 | 264 | slots = {} 265 | explanation_output = await utils.call_potential_coroutine( 266 | explain_method(slot_value, dispatcher, tracker, domain) 267 | ) 268 | 269 | if explanation_output: 270 | slots.update(explanation_output) 271 | 272 | return [SlotSet(slot, value) for slot, value in slots.items()] 273 | 274 | async def validate_AA_CONTINUE_FORM( 275 | self, 276 | value: Text, 277 | dispatcher: CollectingDispatcher, 278 | tracker: Tracker, 279 | domain: Dict[Text, Any], 280 | ) -> Dict[Text, Any]: 281 | """Validates value of 'AA_CONTINUE_FORM' slot""" 282 | if value == "yes": 283 | return {CF_SLOT: value} 284 | 285 | if value == "no": 286 | # This will activate rule 'Submit ---_form' to cancel the operation 287 | return { 288 | "requested_slot": None, 289 | "zz_confirm_form": "no", 290 | CF_SLOT: value, 291 | } 292 | 293 | # The user's answer was not valid. Just re-set it to None. 294 | return {CF_SLOT: None} 295 | -------------------------------------------------------------------------------- /actions/custom_forms_config.yml: -------------------------------------------------------------------------------- 1 | custom_forms: 2 | # When validation of required slots fails this many times: 3 | # (-) Bot will explain the slot if user explain_{slot} method exists 4 | # (-) Bot will ask to continue with the form or not 5 | max_validation_failures: 2 6 | -------------------------------------------------------------------------------- /actions/handoff.py: -------------------------------------------------------------------------------- 1 | from rasa_sdk import Tracker, Action 2 | from rasa_sdk.executor import CollectingDispatcher 3 | 4 | import ruamel.yaml 5 | import pathlib 6 | from typing import Dict, Text, Any, List 7 | from rasa_sdk.events import EventType 8 | 9 | here = pathlib.Path(__file__).parent.absolute() 10 | handoff_config = ( 11 | ruamel.yaml.safe_load(open(f"{here}/handoff_config.yml", "r")) or {} 12 | ).get("handoff_hosts", {}) 13 | 14 | 15 | class ActionHandoffOptions(Action): 16 | def name(self) -> Text: 17 | return "action_handoff_options" 18 | 19 | async def run( 20 | self, 21 | dispatcher: CollectingDispatcher, 22 | tracker: Tracker, 23 | domain: Dict[Text, Any], 24 | ) -> List[EventType]: 25 | 26 | if not any([config.get("url") for bot, config in handoff_config.items()]): 27 | dispatcher.utter_message(template="utter_no_handoff") 28 | else: 29 | buttons = [ 30 | { 31 | "title": config.get("title"), 32 | "payload": f'/trigger_handoff{{"handoff_to":"{bot}"}}', 33 | } 34 | for bot, config in handoff_config.items() 35 | ] 36 | dispatcher.utter_message( 37 | text=( 38 | "I can't transfer you to a human, " 39 | "but I can transfer you to one of these bots" 40 | ), 41 | buttons=buttons, 42 | ) 43 | return [] 44 | 45 | 46 | class ActionHandoff(Action): 47 | def name(self) -> Text: 48 | return "action_handoff" 49 | 50 | async def run( 51 | self, 52 | dispatcher: CollectingDispatcher, 53 | tracker: Tracker, 54 | domain: Dict[Text, Any], 55 | ) -> List[EventType]: 56 | 57 | dispatcher.utter_message(template="utter_handoff") 58 | handoff_to = tracker.get_slot("handoff_to") 59 | 60 | handoff_bot = handoff_config.get(handoff_to, {}) 61 | url = handoff_bot.get("url") 62 | 63 | if url: 64 | if tracker.get_latest_input_channel() == "rest": 65 | dispatcher.utter_message( 66 | json_message={ 67 | "handoff_host": url, 68 | "title": handoff_bot.get("title"), 69 | } 70 | ) 71 | else: 72 | dispatcher.utter_message( 73 | template="utter_wouldve_handed_off", handoffhost=url 74 | ) 75 | else: 76 | dispatcher.utter_message(template="utter_no_handoff") 77 | 78 | return [] 79 | -------------------------------------------------------------------------------- /actions/handoff_config.yml: -------------------------------------------------------------------------------- 1 | handoff_hosts: 2 | helpdesk_assistant: 3 | title: "Helpdesk Assistant" 4 | url: "http://localhost:5005" 5 | ## you can add more handoff hosts to this list e.g. 6 | # moodbot: 7 | # title: "MoodBot" 8 | # url: "http://localhost:5007" 9 | 10 | -------------------------------------------------------------------------------- /actions/parsing.py: -------------------------------------------------------------------------------- 1 | from dateutil import relativedelta, parser 2 | from typing import Dict, Text, Any, Optional 3 | from rasa_sdk import Tracker 4 | 5 | 6 | def close_interval_duckling_time( 7 | timeinfo: Dict[Text, Any] 8 | ) -> Optional[Dict[Text, Any]]: 9 | grain = timeinfo.get("to", timeinfo.get("from", {})).get("grain") 10 | start = timeinfo.get("from", {}).get("value") 11 | end = timeinfo.get("to", {}).get("value") 12 | if (start or end) and not (start and end): 13 | deltaargs = {f"{grain}s": 1} 14 | delta = relativedelta.relativedelta(**deltaargs) 15 | if start: 16 | parsedstart = parser.isoparse(start) 17 | parsedend = parsedstart + delta 18 | end = parsedend.isoformat() 19 | elif end: 20 | parsedend = parser.isoparse(end) 21 | parsedstart = parsedend - delta 22 | start = parsedstart.isoformat() 23 | return { 24 | "start_time": start, 25 | "start_time_formatted": format_isotime_by_grain(start, grain), 26 | "end_time": end, 27 | "end_time_formatted": format_isotime_by_grain(end, grain), 28 | "grain": grain, 29 | } 30 | 31 | 32 | def make_interval_from_value_duckling_time( 33 | timeinfo: Dict[Text, Any] 34 | ) -> Dict[Text, Any]: 35 | grain = timeinfo.get("grain") 36 | start = timeinfo.get("value") 37 | parsedstart = parser.isoparse(start) 38 | deltaargs = {f"{grain}s": 1} 39 | delta = relativedelta.relativedelta(**deltaargs) 40 | parsedend = parsedstart + delta 41 | end = parsedend.isoformat() 42 | return { 43 | "start_time": start, 44 | "start_time_formatted": format_isotime_by_grain(start, grain), 45 | "end_time": end, 46 | "end_time_formatted": format_isotime_by_grain(end, grain), 47 | "grain": grain, 48 | } 49 | 50 | 51 | def parse_duckling_time_as_interval( 52 | timeentity: Dict[Text, Any] 53 | ) -> Optional[Dict[Text, Any]]: 54 | timeinfo = timeentity.get("additional_info", {}) 55 | if timeinfo.get("type") == "interval": 56 | return close_interval_duckling_time(timeinfo) 57 | elif timeinfo.get("type") == "value": 58 | return make_interval_from_value_duckling_time(timeinfo) 59 | 60 | 61 | def format_isotime_by_grain(isotime, grain=None): 62 | value = parser.isoparse(isotime) 63 | grain_format = { 64 | "second": "%I:%M:%S%p, %A %b %d, %Y", 65 | "day": "%A %b %d, %Y", 66 | "week": "%A %b %d, %Y", 67 | "month": "%b %Y", 68 | "year": "%Y", 69 | } 70 | timeformat = grain_format.get(grain, "%I:%M%p, %A %b %d, %Y") 71 | time_formatted = value.strftime(timeformat) 72 | return time_formatted 73 | 74 | 75 | def parse_duckling_time(timeentity: Dict[Text, Any]) -> Optional[Dict[Text, Any]]: 76 | try: 77 | timeinfo = timeentity.get("additional_info", {}) 78 | except AttributeError: 79 | return {"time": None} 80 | if timeinfo.get("type") == "value": 81 | value = timeinfo.get("value") 82 | grain = timeinfo.get("grain") 83 | parsedtime = { 84 | "time": value, 85 | "time_formatted": format_isotime_by_grain(value, grain), 86 | "grain": grain, 87 | } 88 | return parsedtime 89 | 90 | 91 | def get_entity_details( 92 | tracker: Tracker, entity_type: Text 93 | ) -> Optional[Dict[Text, Any]]: 94 | all_entities = tracker.latest_message.get("entities", []) 95 | entities = [e for e in all_entities if e.get("entity") == entity_type] 96 | if entities: 97 | return entities[0] 98 | 99 | 100 | def parse_duckling_currency(entity: Dict[Text, Any]) -> Optional[Dict[Text, Any]]: 101 | if entity.get("entity") == "amount-of-money": 102 | amount = entity.get("additional_info", {}).get("value") 103 | currency = entity.get("additional_info", {}).get("unit") 104 | return {"amount-of-money": f"{amount:.2f}", "currency": currency} 105 | elif entity.get("entity") == "number": 106 | amount = entity.get("value") 107 | return {"amount-of-money": f"{amount:.2f}", "currency": "$"} 108 | -------------------------------------------------------------------------------- /actions/profile_db.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlalchemy as sa 3 | from sqlalchemy import Column, Integer, String, DateTime, REAL 4 | from sqlalchemy.orm import Session, sessionmaker 5 | from sqlalchemy.ext.declarative import declarative_base 6 | from sqlalchemy.engine.base import Engine 7 | from typing import Dict, Text, List, Union, Optional 8 | 9 | from random import choice, randrange, sample, randint 10 | from numpy import arange 11 | from datetime import datetime, timedelta 12 | import pytz 13 | 14 | utc = pytz.UTC 15 | 16 | GENERAL_ACCOUNTS = { 17 | "recipient": [ 18 | "katy parrow", 19 | "evan oslo", 20 | "william baker", 21 | "karen lancaster", 22 | "kyle gardner", 23 | "john jacob", 24 | "percy donald", 25 | "lisa macintyre", 26 | ], 27 | "vendor": ["target", "starbucks", "amazon"], 28 | "depositor": ["interest", "employer"], 29 | } 30 | 31 | ACCOUNT_NUMBER_LENGTH = 12 32 | CREDIT_CARD_NUMBER_LENGTH = 14 33 | 34 | 35 | Base = declarative_base() 36 | 37 | 38 | class Account(Base): 39 | """Accounts table. 40 | `session_id` is only meaningful for accounts generated by conversation sessions, 41 | when it is equal to `tracker.sender_id`. 42 | Since `id` autoincrements, it is used to generate unique account numbers by 43 | adding leading zeros to it. 44 | """ 45 | 46 | __tablename__ = "account" 47 | id = Column(Integer, primary_key=True) 48 | session_id = Column(String(255)) 49 | account_holder_name = Column(String(255)) 50 | currency = Column(String(255)) 51 | 52 | 53 | class CreditCard(Base): 54 | """Credit cards table. `account_id` is an `Account.id`""" 55 | 56 | __tablename__ = "creditcards" 57 | id = Column(Integer, primary_key=True) 58 | credit_card_name = Column(String(255)) 59 | minimum_balance = Column(REAL) 60 | current_balance = Column(REAL) 61 | account_id = Column(Integer) 62 | 63 | 64 | class Transaction(Base): 65 | """Transactions table. `to/from_acount_number` are `Account.id`s with leading zeros""" 66 | 67 | __tablename__ = "transactions" 68 | id = Column(Integer, primary_key=True) 69 | timestamp = Column(DateTime) 70 | amount = Column(REAL) 71 | from_account_number = Column(String(14)) 72 | to_account_number = Column(String(14)) 73 | 74 | 75 | class RecipientRelationship(Base): 76 | """Valid recipients table. `account_id` and `recipient_account_id` are `Account.id`'s""" 77 | 78 | __tablename__ = "recipient_relationships" 79 | id = Column(Integer, primary_key=True) 80 | account_id = Column(Integer) 81 | recipient_account_id = Column(Integer) 82 | recipient_nickname = Column(String(255)) 83 | 84 | 85 | def create_database(database_engine: Engine, database_name: Text): 86 | """Try to connect to the database. Create it if it does not exist""" 87 | try: 88 | database_engine.connect() 89 | except sa.exc.OperationalError: 90 | default_db_url = f"sqlite:///{database_name}.db" 91 | default_engine = sa.create_engine(default_db_url) 92 | conn = default_engine.connect() 93 | conn.execute("commit") 94 | conn.execute(f"CREATE DATABASE {database_name}") 95 | conn.close() 96 | 97 | 98 | class ProfileDB: 99 | def __init__(self, db_engine: Engine): 100 | self.engine = db_engine 101 | self.create_tables() 102 | self.session = self.get_session() 103 | 104 | def get_session(self) -> Session: 105 | return sessionmaker(bind=self.engine, autoflush=True)() 106 | 107 | def create_tables(self): 108 | CreditCard.__table__.create(self.engine, checkfirst=True) 109 | Transaction.__table__.create(self.engine, checkfirst=True) 110 | RecipientRelationship.__table__.create(self.engine, checkfirst=True) 111 | Account.__table__.create(self.engine, checkfirst=True) 112 | 113 | def get_account(self, id: int): 114 | """Get an `Account` object based on an `Account.id`""" 115 | return self.session.query(Account).filter(Account.id == id).first() 116 | 117 | def get_account_from_session_id(self, session_id: Text): 118 | """Get an `Account` object based on a `Account.session_id`""" 119 | # if the action server restarts in the middle of a conversation, the db will need to be repopulated outside of an action_session_start 120 | if not self.check_session_id_exists(session_id): 121 | self.populate_profile_db(session_id) 122 | account = ( 123 | self.session.query(Account).filter(Account.session_id == session_id).first() 124 | ) 125 | return account 126 | 127 | @staticmethod 128 | def get_account_number(account: Union[CreditCard, Account]): 129 | """Get a bank or credit card account number by adding the appropriate number of leading zeros to an `Account.id`""" 130 | if type(account) is CreditCard: 131 | return f"%0.{CREDIT_CARD_NUMBER_LENGTH}d" % account.id 132 | else: 133 | return f"%0.{ACCOUNT_NUMBER_LENGTH}d" % account.id 134 | 135 | def get_account_from_number(self, account_number: Text): 136 | """Get a bank or credit card account based on an account number""" 137 | if len(account_number) == CREDIT_CARD_NUMBER_LENGTH: 138 | return ( 139 | self.session.query(CreditCard) 140 | .filter(CreditCard.id == int(account_number)) 141 | .first() 142 | ) 143 | else: 144 | return ( 145 | self.session.query(Account) 146 | .filter(Account.id == int(account_number)) 147 | .first() 148 | ) 149 | 150 | def get_recipient_from_name(self, session_id: Text, recipient_name: Text): 151 | """Get a recipient based on the nickname. 152 | Take the first one if there are multiple that match. 153 | """ 154 | account = self.get_account_from_session_id(session_id) 155 | recipient = ( 156 | self.session.query(RecipientRelationship) 157 | .filter(RecipientRelationship.account_id == account.id) 158 | .filter(RecipientRelationship.recipient_nickname == recipient_name.lower()) 159 | .first() 160 | ) 161 | recipient_account = self.get_account(recipient.recipient_account_id) 162 | return recipient_account 163 | 164 | def list_known_recipients(self, session_id: Text): 165 | """List recipient nicknames available to an account holder""" 166 | recipients = ( 167 | self.session.query(RecipientRelationship.recipient_nickname) 168 | .filter( 169 | RecipientRelationship.account_id 170 | == self.get_account_from_session_id(session_id).id 171 | ) 172 | .all() 173 | ) 174 | return [recipient.recipient_nickname for recipient in recipients] 175 | 176 | def check_session_id_exists(self, session_id: Text): 177 | """Check if an account for `session_id` already exists""" 178 | return self.session.query( 179 | self.session.query(Account.session_id) 180 | .filter(Account.session_id == session_id) 181 | .exists() 182 | ).scalar() 183 | 184 | def get_account_balance(self, session_id: Text): 185 | """Get the account balance for an account""" 186 | account_number = self.get_account_number( 187 | self.get_account_from_session_id(session_id) 188 | ) 189 | spent = float( 190 | self.session.query(sa.func.sum(Transaction.amount)) 191 | .filter(Transaction.from_account_number == account_number) 192 | .all()[0][0] 193 | ) 194 | earned = float( 195 | self.session.query(sa.func.sum(Transaction.amount)) 196 | .filter(Transaction.to_account_number == account_number) 197 | .all()[0][0] 198 | ) 199 | return earned - spent 200 | 201 | def get_currency(self, session_id: Text): 202 | """Get the currency for an account""" 203 | return ( 204 | self.session.query(Account.currency) 205 | .filter(Account.session_id == session_id) 206 | .first()[0] 207 | ) 208 | 209 | def search_transactions( 210 | self, 211 | session_id: Text, 212 | start_time: Optional[datetime] = None, 213 | end_time: Optional[datetime] = None, 214 | deposit: bool = False, 215 | vendor: Optional[Text] = None, 216 | ): 217 | """Find all transactions for an account between `start_time` and `end_time`. 218 | Looks for spend transactions by default, set `deposit` to `True` to search earnings. 219 | Looks for transactions with anybody by default, set `vendor` to search by vendor 220 | """ 221 | account = self.get_account_from_session_id(session_id) 222 | account_number = self.get_account_number(account) 223 | if deposit: 224 | transactions = self.session.query(Transaction).filter( 225 | Transaction.to_account_number == account_number 226 | ) 227 | elif vendor: 228 | to_account = ( 229 | self.session.query(Account.id) 230 | .filter(Account.session_id.startswith("vendor_")) 231 | .filter(Account.account_holder_name == vendor.lower()) 232 | .first() 233 | ) 234 | to_account_number = self.get_account_number(to_account) 235 | transactions = ( 236 | self.session.query(Transaction) 237 | .filter(Transaction.from_account_number == account_number) 238 | .filter(Transaction.to_account_number == to_account_number) 239 | ) 240 | else: 241 | transactions = self.session.query(Transaction).filter( 242 | Transaction.from_account_number == account_number 243 | ) 244 | if start_time: 245 | transactions = transactions.filter(Transaction.timestamp >= start_time) 246 | if end_time: 247 | transactions = transactions.filter(Transaction.timestamp <= end_time) 248 | 249 | return transactions 250 | 251 | def list_credit_cards(self, session_id: Text): 252 | """List valid credit cards for an acccount""" 253 | account = self.get_account_from_session_id(session_id) 254 | cards = ( 255 | self.session.query(CreditCard) 256 | .filter(CreditCard.account_id == account.id) 257 | .all() 258 | ) 259 | return [card.credit_card_name for card in cards] 260 | 261 | def get_credit_card(self, session_id: Text, credit_card_name: Text): 262 | """Get a `CreditCard` object based on the card's name and the `session_id`""" 263 | account = self.get_account_from_session_id(session_id) 264 | return ( 265 | self.session.query(CreditCard) 266 | .filter(CreditCard.account_id == account.id) 267 | .filter(CreditCard.credit_card_name == credit_card_name.lower()) 268 | .first() 269 | ) 270 | 271 | def get_credit_card_balance( 272 | self, 273 | session_id: Text, 274 | credit_card_name: Text, 275 | balance_type: Text = "current_balance", 276 | ): 277 | """Get the balance for a credit card based on its name and the balance type""" 278 | balance_type = "_".join(balance_type.split()) 279 | card = self.get_credit_card(session_id, credit_card_name) 280 | return getattr(card, balance_type) 281 | 282 | @staticmethod 283 | def list_balance_types(): 284 | """List valid balance types for credit cards""" 285 | return [ 286 | " ".join(name.split("_")) 287 | for name in CreditCard.__table__.columns.keys() 288 | if name.endswith("balance") 289 | ] 290 | 291 | def list_vendors(self): 292 | """List valid vendors""" 293 | vendors = ( 294 | self.session.query(Account.account_holder_name) 295 | .filter(Account.session_id.startswith("vendor_")) 296 | .all() 297 | ) 298 | return [vendor.account_holder_name for vendor in vendors] 299 | 300 | def pay_off_credit_card( 301 | self, session_id: Text, credit_card_name: Text, amount: float 302 | ): 303 | """Do a transaction to move the specified amount from an account to a credit card""" 304 | account = self.get_account_from_session_id(session_id) 305 | account_number = self.get_account_number(account) 306 | credit_card = ( 307 | self.session.query(CreditCard) 308 | .filter(CreditCard.account_id == account.id) 309 | .filter(CreditCard.credit_card_name == credit_card_name.lower()) 310 | .first() 311 | ) 312 | self.transact( 313 | account_number, 314 | self.get_account_number(credit_card), 315 | amount, 316 | ) 317 | credit_card.current_balance -= amount 318 | if amount < credit_card.minimum_balance: 319 | credit_card.minimum_balance -= amount 320 | else: 321 | credit_card.minimum_balance = 0 322 | self.session.commit() 323 | 324 | def add_session_account(self, session_id: Text, name: Optional[Text] = ""): 325 | """Add a new account for a new session_id. Assumes no such account exists yet.""" 326 | self.session.add( 327 | Account(session_id=session_id, account_holder_name=name, currency="$") 328 | ) 329 | 330 | def add_credit_cards(self, session_id: Text): 331 | """Populate the creditcard table for a given session_id""" 332 | credit_card_names = ["iron bank", "credit all", "emblem", "justice bank"] 333 | credit_cards = [ 334 | CreditCard( 335 | credit_card_name=cardname, 336 | minimum_balance=choice([20, 30, 40]), 337 | current_balance=choice( 338 | [round(amount, 2) for amount in list(arange(20, 500, 0.01))] 339 | ), 340 | account_id=self.get_account_from_session_id(session_id).id, 341 | ) 342 | for cardname in credit_card_names 343 | ] 344 | self.session.add_all(credit_cards) 345 | 346 | def check_general_accounts_populated( 347 | self, general_account_names: Dict[Text, List[Text]] 348 | ): 349 | """Check whether tables have been populated with global values for vendors, recipients, and depositors""" 350 | account_names = set( 351 | [ 352 | name 353 | for list_names in general_account_names.values() 354 | for name in list_names 355 | ] 356 | ) 357 | existing_accounts = self.session.query(Account.account_holder_name).filter( 358 | Account.account_holder_name.in_(account_names) 359 | ) 360 | existing_account_names = set( 361 | [account.account_holder_name for account in existing_accounts.all()] 362 | ) 363 | return account_names == existing_account_names 364 | 365 | def add_general_accounts(self, general_account_names: Dict[Text, List[Text]]): 366 | """Populate tables with global values for vendors, recipients, and depositors""" 367 | general_accounts = [ 368 | Account(session_id=f"{prefix}_{id}", account_holder_name=name) 369 | for prefix, names in general_account_names.items() 370 | for id, name in enumerate(names) 371 | ] 372 | 373 | for account in general_accounts: 374 | self.session.merge(account) 375 | self.session.commit() 376 | 377 | def add_recipients(self, session_id: Text): 378 | """Populate recipients table""" 379 | account = self.get_account_from_session_id(session_id) 380 | recipients = ( 381 | self.session.query(Account.account_holder_name, Account.id) 382 | .filter(Account.session_id.startswith("recipient_")) 383 | .all() 384 | ) 385 | session_recipients = sample(recipients, choice(list(range(3, len(recipients))))) 386 | relationships = [ 387 | RecipientRelationship( 388 | account_id=account.id, 389 | recipient_account_id=recipient.id, 390 | recipient_nickname=recipient.account_holder_name, 391 | ) 392 | for recipient in session_recipients 393 | ] 394 | self.session.add_all(relationships) 395 | 396 | def add_transactions(self, session_id: Text): 397 | """Populate transactions table for a session ID with random transactions""" 398 | account_number = self.get_account_number( 399 | self.get_account_from_session_id(session_id) 400 | ) 401 | vendors = ( 402 | self.session.query(Account) 403 | .filter(Account.session_id.startswith("vendor_")) 404 | .all() 405 | ) 406 | depositors = ( 407 | self.session.query(Account) 408 | .filter(Account.session_id.startswith("depositor_")) 409 | .all() 410 | ) 411 | 412 | start_date = utc.localize(datetime(2019, 1, 1)) 413 | end_date = utc.localize(datetime.now()) 414 | number_of_days = (end_date - start_date).days 415 | 416 | for vendor in vendors: 417 | rand_spend_amounts = sample( 418 | [round(amount, 2) for amount in list(arange(5, 50, 0.01))], 419 | number_of_days // 2, 420 | ) 421 | 422 | rand_dates = [ 423 | (start_date + timedelta(days=randrange(number_of_days))) 424 | for x in range(0, len(rand_spend_amounts)) 425 | ] 426 | 427 | spend_transactions = [ 428 | Transaction( 429 | from_account_number=account_number, 430 | to_account_number=self.get_account_number(vendor), 431 | amount=amount, 432 | timestamp=date, 433 | ) 434 | for amount, date in zip(rand_spend_amounts, rand_dates) 435 | ] 436 | 437 | self.session.add_all(spend_transactions) 438 | 439 | for depositor in depositors: 440 | if depositor.account_holder_name == "interest": 441 | rand_deposit_amounts = sample( 442 | [round(amount, 2) for amount in list(arange(5, 20, 0.01))], 443 | number_of_days // 30, 444 | ) 445 | else: 446 | rand_deposit_amounts = sample( 447 | [round(amount, 2) for amount in list(arange(1000, 2000, 0.01))], 448 | number_of_days // 14, 449 | ) 450 | 451 | rand_dates = [ 452 | (start_date + timedelta(days=randrange(number_of_days))) 453 | for x in range(0, len(rand_deposit_amounts)) 454 | ] 455 | 456 | deposit_transactions = [ 457 | Transaction( 458 | from_account_number=self.get_account_number(depositor), 459 | to_account_number=account_number, 460 | amount=amount, 461 | timestamp=date, 462 | ) 463 | for amount, date in zip(rand_deposit_amounts, rand_dates) 464 | ] 465 | 466 | self.session.add_all(deposit_transactions) 467 | 468 | def populate_profile_db(self, session_id: Text): 469 | """Initialize the database for a conversation session. 470 | Will populate all tables with sample values. 471 | If general accounts have already been populated, it will only 472 | add account-holder-specific values to tables. 473 | """ 474 | if not self.check_general_accounts_populated(GENERAL_ACCOUNTS): 475 | self.add_general_accounts(GENERAL_ACCOUNTS) 476 | if not self.check_session_id_exists(session_id): 477 | self.add_session_account(session_id) 478 | self.add_recipients(session_id) 479 | self.add_transactions(session_id) 480 | self.add_credit_cards(session_id) 481 | 482 | self.session.commit() 483 | 484 | def transact( 485 | self, from_account_number: Text, to_account_number: Text, amount: float 486 | ): 487 | """Add a transation to the transaction table""" 488 | timestamp = datetime.now() 489 | transaction = Transaction( 490 | from_account_number=from_account_number, 491 | to_account_number=to_account_number, 492 | amount=amount, 493 | timestamp=timestamp, 494 | ) 495 | self.session.add(transaction) 496 | self.session.commit() 497 | -------------------------------------------------------------------------------- /actions/requirements-actions.txt: -------------------------------------------------------------------------------- 1 | python-dateutil==2.8.1 2 | numpy~=1.19.2 3 | pytz~=2019.3 4 | ruamel.yaml 5 | sqlalchemy~=1.3.22 6 | -------------------------------------------------------------------------------- /chatroom_handoff.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | language: en 3 | pipeline: 4 | - name: WhitespaceTokenizer 5 | - name: RegexFeaturizer 6 | - name: LexicalSyntacticFeaturizer 7 | - name: CountVectorsFeaturizer 8 | - name: CountVectorsFeaturizer 9 | analyzer: "char_wb" 10 | min_ngram: 1 11 | max_ngram: 4 12 | - name: DIETClassifier 13 | epochs: 100 14 | - name: FallbackClassifier 15 | threshold: 0.7 16 | - name: DucklingEntityExtractor 17 | url: http://localhost:8000 18 | dimensions: 19 | - amount-of-money 20 | - time 21 | - number 22 | - name: SpacyNLP 23 | model: "en_core_web_md" 24 | case_sensitive: false 25 | - name: "SpacyEntityExtractor" 26 | # Note: It is not possible to use the SpacyTokenizer + SpacyFeaturizer in 27 | # combination with the WhitespaceTokenizer, and as a result the 28 | # PERSON extraction by Spacy is not very robust. 29 | # Because of this, the nlu training data is annotated as well, and the 30 | # DIETClassifier will also extract PERSON entities . 31 | dimensions: ["PERSON"] 32 | - name: EntitySynonymMapper 33 | policies: 34 | - name: AugmentedMemoizationPolicy 35 | - name: TEDPolicy 36 | epochs: 40 37 | - name: RulePolicy 38 | core_fallback_threshold: 0.4 39 | core_fallback_action_name: "action_default_fallback" 40 | enable_fallback_prediction: True 41 | -------------------------------------------------------------------------------- /credentials.yml: -------------------------------------------------------------------------------- 1 | # This file contains the credentials for the voice & chat platforms 2 | # which your bot is using. 3 | # https://rasa.com/docs/rasa/user-guide/messaging-and-voice-channels/ 4 | 5 | rest: 6 | # # you don't need to provide anything here - this channel doesn't 7 | # # require any credentials 8 | 9 | 10 | #facebook: 11 | # verify: "" 12 | # secret: "" 13 | # page-access-token: "" 14 | 15 | #slack: 16 | # slack_token: "" 17 | # slack_channel: "" 18 | # proxy: "" 19 | 20 | #socketio: 21 | # user_message_evt: 22 | # bot_message_evt: 23 | # session_persistence: 24 | 25 | #mattermost: 26 | # url: "https:///api/v4" 27 | # token: "" 28 | # webhook_url: "" 29 | 30 | # This entry is needed if you are using Rasa X. The entry represents credentials 31 | # for the Rasa X "channel", i.e. Talk to your bot and Share with guest testers. 32 | rasa: 33 | url: "http://localhost:5002/api" 34 | -------------------------------------------------------------------------------- /data/nlu/nlu.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | nlu: 3 | - intent: check_human 4 | examples: | 5 | - are you a bot? 6 | - am i speaking to an ai? 7 | - are you a human or a bot? 8 | - are you real? 9 | - are you virtual? 10 | - are you an ai? 11 | - aren't you a bot 12 | - are you a chatbot 13 | - are you a rasa bot? 14 | - are you a real bot? 15 | - are you a robot 16 | - are you ai 17 | - are you artificial 18 | - are you artificial intelligence 19 | - are you real 20 | - are you really a bot 21 | - are you robot 22 | - are you sure that you're a bot? 23 | - bot? 24 | - intent: affirm 25 | examples: | 26 | - indeed 27 | - correct 28 | - that sounds good 29 | - yes 30 | - yess please 31 | - of course 32 | - yup 33 | - yeah 34 | - yes please 35 | - yes plz 36 | - Sure 37 | - Ok 38 | - sweet 39 | - cool, 40 | - yes... 41 | - y 42 | - ok 43 | - ye 44 | - okay 45 | - That would be great 46 | - YES 47 | - YUP 48 | - Yea 49 | - Yeah 50 | - Yeah sure 51 | - Yep 52 | - Yep that's fine 53 | - Yep! 54 | - Yepp 55 | - Yes 56 | - Yes I do 57 | - Yes please 58 | - Yes please! 59 | - Yes, I accept 60 | - intent: ask_transfer_charge 61 | examples: | 62 | - Will I be charged for transferring money 63 | - do transfers cost something? 64 | - is there a transfer charge? 65 | - Is there a charge 66 | - will i be charged for a transaction? 67 | - do xfers cost something? 68 | - is there a transfer fee 69 | - is there a xfer fee 70 | - how much is the transfer fee 71 | - whats the cost of transferring money 72 | - what am I charged upon money transfer 73 | - are you gonna bill me for this exchange 74 | - whats the xfer price for this account 75 | - okay but how much to move this money? 76 | - how much is the fee to shift my money around 77 | - if I send money to someone will I be charged extra for that? 78 | - is there a charge when I send money to someone 79 | - what is the fee for sending money to someone else 80 | - I sent money to my friend will I be charged extra for that 81 | - what is the percentage fee for transactions to another person 82 | - are there any transaction fees or charges I should know about? 83 | - tell me about transfer fees 84 | - intent: check_balance 85 | examples: | 86 | - How much money is on my account? 87 | - How much money is on my [bank](account_type) account? 88 | - what's my [balance](amount-of-money)? 89 | - what's my [current balance](amount-of-money)? 90 | - What's left on that account? 91 | - How much do I have on that account? 92 | - What's the [balance](amount-of-money) on that account? 93 | - How much money is left on that account? 94 | - what is my account [balance](amount-of-money) 95 | - what's my account [balance](amount-of-money)? 96 | - what's my account [balance](amount-of-money) 97 | - whats my account [balance](amount-of-money) 98 | - what is my [bank](account_type) account [balance](amount-of-money) 99 | - what's my [bank](account_type) account [balance](amount-of-money)? 100 | - what's my [bank](account_type) account [balance](amount-of-money) 101 | - whats my [bank](account_type) account [balance](amount-of-money) 102 | - What's my [credit card](account_type) [balance](amount-of-money) 103 | - What's my [credit](account_type) [balance](amount-of-money) 104 | - How much money do I owe on my [credit cards](account_type) 105 | - Show me my [credit card](account_type) [balance](amount-of-money) 106 | - What [credit](account_type) accounts do I have 107 | - Show me my [credit](account_type) accounts 108 | - Whats the [balance](amount-of-money) on my [credit](account_type) account 109 | - What's my [credit](account_type) account 110 | - What's my [emblem](credit_card) [card](account_type) [balance](amount-of-money) 111 | - Show me my [iron bank](credit_card) [balance](amount-of-money) 112 | - What's my [justice bank](credit_card) [balance](amount-of-money) 113 | - What's the [balance](amount-of-money) on the [justice bank](credit_card) account 114 | - what's my [emblm](credit_card) [credit card](account_type) [balance](amount-of-money)? 115 | - what's my [credit card](account_type) [balance](amount-of-money)? 116 | - what's my [credit card](account_type) account [balance](amount-of-money)? 117 | - What is my [emblem](credit_card) [card](account_type) [balance](amount-of-money)? 118 | - whats my [credit card](account_type) [balance](amount-of-money) 119 | - What is my [emblem](credit_card) [balance](amount-of-money)? 120 | - whats my [emblm](credit_card) [card](account_type) [balance](amount-of-money)? 121 | - what's my [embelm](credit_card)'s [card](account_type) [balance](amount-of-money)? 122 | - what's my [emblm](credit_card) account [balance](amount-of-money)? 123 | - How much money have i spent lately> 124 | - I should check my [credit card]{"entity": "account_type", "value": "credit"} [balance](amount-of-money) [first](account_type) 125 | - check my [credit card]{"entity": "account_type", "value": "credit"} [balance](amount-of-money) 126 | - check my [credit card]{"entity": "account_type", "value": "credit"} [balance](amount-of-money) [first](account_type) 127 | - Can you tell me my account [balance](amount-of-money)? 128 | - Hello, I would like to know how much money is in much money is in my account 129 | - hi, whats my account bal please 130 | - so.... what's my [balance](amount-of-money)? 131 | - whats my [current balance](amount-of-money) 132 | - What's my [balance](amount-of-money)? 133 | - How much money do I have? 134 | - Yeah, I like coffee 👀 But how much money do I have? 135 | - Yeah, I know 😀 Since it's so nice: How much money do I have? 136 | - what places have I spent money? 137 | - Wait, I'll have to check my balance first. Can you do that please? 138 | - Hi, I need to know how much money is in my [checking](account_type) account 139 | - Balance please 140 | - intent: check_earnings 141 | examples: | 142 | - How much money went into my account last month? 143 | - How much money did I make last year? 144 | - What did I earn last month? 145 | - How much did I make last month? 146 | - How much money did I make last week? 147 | - how much was deposited in my account last month? 148 | - how much was deposited in my account in the last two weeks? 149 | - check deposits for last week 150 | - total deposits over last year 151 | - how much was deposited in January 152 | - how much money have I earned? 153 | - what did I earn? 154 | - How much have i earned? 155 | - how much did I make 156 | - am I rich yet? 157 | - am I a millionaire 158 | - how much cash is rolling through my account 159 | - how much money is in the money making machine account? 160 | - what did I make in the last year? 161 | - show me how much I've earned in the last week 162 | - can i afford to go on vacation 163 | - rent is due soon how much do I have in my account 164 | - intent: deny 165 | examples: | 166 | - not really 167 | - no 168 | - I don't think so 169 | - never 170 | - no way 171 | - nope 172 | - no thanks 173 | - I dunno 174 | - Nothing! 175 | - nevermind 176 | - n 177 | - i decline 178 | - i don not like this 179 | - i don't think so 180 | - i don't want either of those 181 | - i don't want to 182 | - i don't want to give you my email 183 | - i dont want to 184 | - i dont want to accept :P lol 185 | - i guess it means - no 186 | - i'm afraid not 187 | - i'm not sure 188 | - intent: goodbye 189 | examples: | 190 | - see you later 191 | - goodbye 192 | - i'm done 193 | - quit 194 | - stop 195 | - bye 196 | - Adios 197 | - BYEE 198 | - GOODBYE 199 | - Thanks a lot. See ya later 200 | - have a good day. time to go, bye 201 | - bye bye 202 | - ok i gtg 203 | - bye bye bot 204 | - bye for now 205 | - bye udo 206 | - bye was nice talking to you 207 | - bye! 208 | - byee 209 | - catch you later 210 | - ciao 211 | - cya 212 | - farewell 213 | - good bye 214 | - good bye rasa bot! 215 | - good night 216 | - goodbye 217 | - goodbye. 218 | - goodnight 219 | - gotta go 220 | - intent: greet 221 | examples: | 222 | - yo 223 | - good morning 224 | - hi 225 | - hey there 226 | - hey 227 | - good evening 228 | - hello 229 | - Hello? 230 | - HEY 231 | - hello are you still there 232 | - hallo 233 | - HI 234 | - Hey 235 | - Hi 236 | - hi! 237 | - hello there 238 | - hi there 239 | - Hello 240 | - hello its ella 241 | - Hola. 242 | - welcome 243 | - howdy 244 | - good day 245 | - intent: inform 246 | examples: | 247 | - $10 248 | - 5000 249 | - 500 250 | - $100 251 | - $500 252 | - 100 dollars 253 | - 1000 dollars 254 | - tomorrow 255 | - next week 256 | - yesterday 257 | - for today 258 | - last week 259 | - the past month 260 | - the past two days 261 | - the last two weeks 262 | - at [amazon](vendor_name) 263 | - [starbucks](vendor_name) 264 | - at [target](vendor_name) 265 | - at [starbucks](vendor_name) 266 | - [target](vendor_name) 267 | - [Amazon](vendor_name) 268 | - [Starbucks](vendor_name) 269 | - [Target](vendor_name) 270 | - I want to pay the [current balance](amount-of-money) 271 | - [current balance](amount-of-money) 272 | - [full](amount-of-money) 273 | - in [full](amount-of-money) 274 | - the [full balance](amount-of-money) 275 | - [minimum balance](amount-of-money) 276 | - I want to pay the [minimum balance](amount-of-money) 277 | - the [current balance](amount-of-money) 278 | - the [minimum balance](amount-of-money) 279 | - my [minimum balance](amount-of-money) 280 | - my [current balance](amount-of-money) 281 | - [emblem](credit_card) [card](account_type) 282 | - all [cards](account_type) 283 | - [iron bank](credit_card) [card](account_type) 284 | - [justice bank](credit_card) [card](account_type) 285 | - [emblem](credit_card) 286 | - [iron bank](credit_card) 287 | - [justice bank](credit_card) 288 | - [emblem](credit_card) account 289 | - [iron bank](credit_card) account 290 | - [justice bank](credit_card) account 291 | - [emblem](credit_card) [credit card](account_type) 292 | - [iron bank](credit_card) [credit card](account_type) 293 | - [justice bank](credit_card) [credit card](account_type) 294 | - my [emblem](credit_card) [credit card](account_type) 295 | - my [iron bank](credit_card) [credit card](account_type) 296 | - my [justice bank](credit_card) [credit card](account_type) 297 | - my [emblem](credit_card) [card](account_type) 298 | - my [iron bank](credit_card) [card](account_type) 299 | - my [justice bank](credit_card) [card](account_type) 300 | - towards my [emblem](credit_card) [card](account_type) 301 | - towards my [iron bank](credit_card) [card](account_type) 302 | - towards my [justice bank](credit_card) [card](account_type) 303 | - for sunday 304 | - for friday 305 | - for tomorrow 306 | - for saturday 307 | - I want to pay the [minimum balance](amount-of-money) on my [emblem](credit_card) [credit card](account_type) today 308 | - today 309 | - Please schedule it for the first of next month 310 | - a hundred dollars 311 | - [mastercard](credit_card) 312 | - [visa](credit_card) 313 | - [american express](credit_card) 314 | - my [rasa](credit_card) account 315 | - my [credit card]{"entity": "account_type", "value": "credit"} account ending in 4321 316 | - My [Visa](credit_card) account 317 | - A friend. 318 | - [Alexandra](PERSON)? 319 | - today please 320 | - [Iron Bank](credit_card) 321 | - My [visa](credit_card) 322 | - [Bob](PERSON) 323 | - [bill](PERSON) 324 | - [anusha](PERSON) 325 | - [John](PERSON) 326 | - [gary](PERSON) 327 | - [Claire](PERSON) 328 | - [Imani](PERSON) 329 | - [darnell](PERSON) 330 | - [Guangchen](PERSON) 331 | - [cheng](PERSON) 332 | - to my [brother](PERSON) 333 | - to my [sister](PERSON) 334 | - my [dad](PERSON) 335 | - William Baker 336 | - Evan Oslo 337 | - Lisa Macintyre 338 | - Karen Lancaster 339 | - Percy Donald 340 | - Kyle Gardner 341 | - Katy Parrow 342 | - Percy please 343 | - Laura Schilling 344 | - In that case, let's go with Percy 345 | - My chase 346 | - Jane 347 | - Melinda 348 | - Sara 349 | - my wife's 350 | - Andreas 351 | - htthis weekend 352 | - all of them 353 | - Darren Smith 354 | - Percy 355 | - percy donald 356 | - danIelle smilanich 357 | - [Gringtos](credit_card) 358 | - intent: pay_cc 359 | examples: | 360 | - I would like to pay the [minimum balance](amount-of-money) on my [embelm](credit_card) [card](account_type) 361 | - I want to pay my [credit card](account_type) 362 | - I want to pay my [card](account_type) 363 | - Pay my [card](account_type) 364 | - I want to pay my [justice bank](credit_card) bill 365 | - Pay my [discover](credit_card) 366 | - I want to pay my [iron bank](credit_card) bill 367 | - Pay my [visa](credit_card) 368 | - I want to pay my [visa](credit_card) bill 369 | - Pay my [mastercard](credit_card) 370 | - I want to pay my [MasterCard](credit_card) bill 371 | - I want to pay my [credit card](account_type) bill 372 | - I want to pay the [current balance](amount-of-money) on my [credit card](account_type) 373 | - can you help me pay the [current balance](amount-of-money) on my [credit card](account_type) 374 | - i want to pay off my [credit card](account_type) 375 | - I want to pay off my [credit card](account_type) 376 | - i want to pay my [credit card](account_type) 377 | - i want to pay off my [emblem](credit_card) [credit card](account_type) 378 | - I want to pay my [current balance](amount-of-money) on my [embelm](credit_card) [credit card](account_type) 379 | - i want to pay my [current balance](amount-of-money) on my [emblem](credit_card) [credit card](account_type) 380 | - I want to pay $500 on my [emblem](credit_card) [credit card](account_type) on Sunday 381 | - I want to pay my [current balance](amount-of-money) on my [emblem](credit_card) [credit card](account_type) today 382 | - i need to pay off my [emblm](credit_card) [credit card](account_type) 383 | - Please schedule a payment towards my [credit card](account_type) for April 12th 384 | - Can I schedule a payment towards my [credit card](account_type) for tomorrow? 385 | - Pay off my [minimum balance](amount-of-money) please 386 | - i would like to pay $1200 to my [credit card]{"entity": "account_type", "value": "credit"} 387 | - I would also like to pay my [visa](credit_card) bill 388 | - Pay off my [credit card]{"entity": "account_type", "value": "credit"}, please 389 | - I guess it is. Since it's so much, let's pay off my [credit](account_type) 390 | - Let's pay that [credit card]{"entity": "account_type", "value": "credit"} bill 391 | - I want to pay 100 dollars toward my [Emblem](credit_card) [card](account_type), tomorrow 392 | - pay 100 towards [emblem](credit_card) [card](account_type), tomorrow 393 | - id like to pay my bill 394 | - could I pay off my [credit card]{"entity": "account_type", "value": "credit"}? 395 | - pay off my [credit card]{"entity": "account_type", "value": "credit"} 396 | - i wanna pay my [credit card]{"entity": "account_type", "value": "credit"} 397 | - pay a bill 398 | - can I pay my [card]{"entity": "account_type", "value": "credit"}? 399 | - intent: search_transactions 400 | examples: | 401 | - how much did I spend at [Target](vendor_name) this week? 402 | - what is my typical spending at [Amazon](vendor_name)? 403 | - I want to check my spending history 404 | - how much did I spend at [Starbucks](vendor_name) last week? 405 | - I need to check my spending history 406 | - I want to check my spending 407 | - I want to check my spending history at [starbucks](vendor_name) 408 | - i want to check my spending history 409 | - i want to see my transaction history 410 | - I want to search my past transactions 411 | - can I search my transaction history 412 | - can I look at past transactions 413 | - I want to search my transactions with [amazon](vendor_name) 414 | - can i seem my transactions with [target](vendor_name) 415 | - how much did i spend at [starbucks](vendor_name) last month? 416 | - what did i spend last month? 417 | - How much did I spend last month? 418 | - What did I spend at [Legoland](vendor_name) last month? 419 | - What did I spend at [Legoland](vendor_name) last year? 420 | - how much did i spend at [starbucks](vendor_name) last year 421 | - Yes! How much did I spend on [Starbucks](vendor_name) last month? 422 | - How much did I spend at Burger King last month? 423 | - what places have I spent money? 424 | - What did I spend at [Starbucks](vendor_name) in may? 425 | - How much money did I lose to [starbucks](vendor_name) in may? 426 | - Oh how much did I actually spend at [target](vendor_name) last month? I'd love to know. 427 | - Can you tell me how much I spent at [Ikea](vendor_name) this month? 428 | - Can you tell me how much I spent at coffeeshops this month? 429 | - Can you tell me how much I spent on coffee this month? 430 | - What did I spend at [Costco](vendor_name) last month? 431 | - what did I spend at [target](vendor_name) last month 432 | - where did I spend the most money last month? 433 | - intent: thankyou 434 | examples: | 435 | - thank you goodbye 436 | - okay thank you goodbye 437 | - thank you bye 438 | - um okay thank you good bye 439 | - thank you 440 | - and thats all thank you and good bye 441 | - okay thank you 442 | - thanks 443 | - thanks goodbye 444 | - thank you and good bye 445 | - Thanks! 446 | - Awesome, you are a good bot! 447 | - great! 448 | - I see, thanks 449 | - Oh thats good! 450 | - it's good :-) 451 | - thnaks 452 | - good job bot 453 | - great 454 | - thanks for the help 455 | - thanks you 456 | - thanks! 457 | - thankyou 458 | - thnks 459 | - thx 460 | - thanks for your information 461 | - thanks f 462 | - intent: transfer_money 463 | examples: | 464 | - I want to pay [John](PERSON) 465 | - I want to transfer $100 to [Bob](PERSON) 466 | - can i transfer money to [Sally](PERSON) 467 | - I want to transfer money 468 | - I want to pay someone 469 | - can I pay someone 470 | - I need to transfer money to a friend 471 | - can I transfer money to my [mom](PERSON) 472 | - I want to pay [Bert](PERSON) 473 | - pay my friend 474 | - transfer money 475 | - I want to transfer $100 to [Tommy](PERSON) 476 | - i want to transfer $400 to [Akela](PERSON) 477 | - I want to pay [Paula Handy](PERSON) 478 | - pay Katy Parrow $40 please 479 | - transfer $60 to [Jason Jacob](PERSON) 480 | - pay [Evan Oslo](PERSON) 481 | - pay [Lisa](PERSON) please 482 | - can I transfer money to [Kyle Gardner](PERSON)? 483 | - Pay [Karen](PERSON) 60 Euros 484 | - pay [Mona](PERSON) $60 485 | - transfer $60 to [Sally](PERSON) 486 | - need to transfer money 487 | - II want to transfer to [Kelly](PERSON) 488 | - Pay [Percy](PERSON) $50 489 | - Pay $50 to [Pat](PERSON) 490 | - Ok pay [Emma](PERSON) 491 | - pay [Emanual](PERSON) 492 | - i want to transfer $100 to [Jane smith](PERSON) 493 | - i want to transfer $100 to my [daughter](PERSON) 494 | - i want to transfer money please 495 | - i mean i want to transfer money 496 | - I need to pay a friend. 497 | - transfer money to [ethan oswald](PERSON) 498 | - transfer money to [sara](PERSON) 499 | - I need to transfer money to my grandma [Judith](PERSON). 500 | - i want to transfer money to [Latisha](PERSON) 501 | - I need to make an urgent transfer to [Bob](PERSON), can you do it? 502 | - Ahh, okay so I want to make a transfer to [Bob](PERSON). 503 | - Can I transfer money to a friend? 504 | - I need to transfer money to [Jeff](PERSON), my son 505 | - I need to send money to my kid 506 | - I need to transfer money 507 | - send $50 to [john jacob](PERSON) 508 | - I need to transfer money to my co-worker who paid for lunch 509 | - Hi I like to send money 510 | - I'd like to send 50$ to [John](PERSON) 511 | - Hi I like to send money to [John](PERSON) 512 | - Please transfer 50 dollars to [Andreas](PERSON) 513 | - I would like to transfer money to [Philipp](PERSON) 514 | - I would like to transfer 50$ to [Joschka](PERSON) 515 | - intent: check_recipients 516 | examples: | 517 | - Who can I send money to? 518 | - Who are my known recipients 519 | - Show me my list of recipients 520 | - Show the recipient list 521 | - Show me people I can send money to 522 | - Who is a valid payee 523 | - who can I pay? 524 | - who can I transfer money? 525 | - who's in my recipient list? 526 | - who can I transfer money to? 527 | - Who is on your list? 528 | - who is in my list of kown recipients? 529 | - Who all can I transfer money to? 530 | - Who can I transfer money to? 531 | - Okay. Who can I transfer money to then? 532 | - can I see the list of known recipients? 533 | - tell me who I can share my money with 534 | - whats my recipient list 535 | - can I send money to anyone? 536 | - who exactly am I able to send money to 537 | - if I want to transfer money can i send it to anyone 538 | - show me my money sending list 539 | - intent: help 540 | examples: | 541 | - help 542 | - what can you do? 543 | - what can I ask you? 544 | - what do you do? 545 | - what can you help me with? 546 | - help me 547 | - help, what do you do 548 | - how do I use this 549 | - how can you help me 550 | - What are the things that you can do? 551 | - What else can you help me with? 552 | - can you do anything else? 553 | - can you help me? 554 | - come back 555 | - cool! can I do something else here? 556 | - hello what can you do for me 557 | - help 558 | - help me 559 | - help please 560 | - help pls 561 | - help? 562 | - hep me 563 | - intent: human_handoff 564 | examples: | 565 | - handoff 566 | - operator 567 | - I want a human 568 | - can I speak to an agent 569 | - real agent please 570 | - real human 571 | - chat with a live agent 572 | - give me a person please 573 | - i want to talk to a human 574 | - transfer to a human 575 | - Please give me to a human 576 | - can I talk to human? 577 | - can I talk to human 578 | - talk to human 579 | - i want human :( 580 | - can i talk to human 581 | - i want to talk to a human 582 | - i want to speak to human 583 | - can i talk to a real person? 584 | - connect me to a real person 585 | - I need a real person 586 | - can i took with a real person 587 | - let me speak to a real person 588 | - let me speak to a real person please 589 | - i want to talk to a real person 590 | - intent: out_of_scope 591 | examples: | 592 | - what is the square root of 5 593 | - I want to know the weather 594 | - what is the meaning of life. 595 | - Fridge Isn't Running 596 | - my tv isn't working 597 | - I want a pizza 598 | - my washing machine isn't working 599 | - what year is it 600 | - order a pizza 601 | - I want to order a pizza 602 | - what is the weather today 603 | - what is the weather 604 | - what the weather today ? 605 | - hows the weather 606 | - tell me a joke 607 | - Can I get a hamburger? 608 | - Can YouTube talk? 609 | - Can you call me back ? 610 | - Can you give me your datacenter's password 611 | - Can you give me your datacenter's password? 612 | - Can you make sandwiches? 613 | - Can you please send me an uber 614 | - ofif fsdfafsfs 615 | - synonym: credit 616 | examples: | 617 | - credit card 618 | - credit cards 619 | - credit account 620 | - credit accounts 621 | - card 622 | - cards 623 | - credit card account 624 | - synonym: emblem 625 | examples: | 626 | - emblm 627 | - embelm 628 | - Emblem 629 | - synonym: current balance 630 | examples: | 631 | - balance 632 | - full 633 | - full balance 634 | -------------------------------------------------------------------------------- /data/rules/rules.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | rules: 4 | 5 | - rule: Confirm that the bot is not a human 6 | steps: 7 | - intent: check_human 8 | - action: utter_bot 9 | 10 | - rule: Ask the user to rephrase whenever they send a message with low NLU confidence 11 | steps: 12 | - intent: nlu_fallback 13 | - action: utter_default 14 | 15 | - rule: answer out of scope 16 | steps: 17 | - intent: out_of_scope 18 | - action: utter_out_of_scope 19 | 20 | - rule: greet 21 | steps: 22 | - intent: greet 23 | - action: utter_greet 24 | - action: utter_help 25 | 26 | - rule: say goodbye 27 | steps: 28 | - intent: goodbye 29 | - action: utter_goodbye 30 | 31 | - rule: help 32 | steps: 33 | - intent: help 34 | - action: utter_help 35 | 36 | - rule: thankyou 37 | steps: 38 | - intent: thankyou 39 | - action: utter_noworries 40 | 41 | - rule: is there a transfer charge 42 | steps: 43 | - intent: ask_transfer_charge 44 | - action: action_show_transfer_charge 45 | 46 | - rule: Show list of known recipients 47 | steps: 48 | - intent: check_recipients 49 | - action: action_show_recipients 50 | 51 | - rule: Show balance (bank account or credit card, based on account_type) 52 | steps: 53 | - intent: check_balance 54 | - action: action_show_balance 55 | 56 | - rule: Activate cc_payment_form when no other form is active 57 | condition: 58 | # this condition allows stories to handle form switching 59 | - active_loop: null 60 | steps: 61 | - intent: pay_cc 62 | - action: cc_payment_form 63 | - active_loop: cc_payment_form 64 | 65 | - rule: Activate transfer_money_form when no other form is active 66 | condition: 67 | # this condition allows stories to handle form switching 68 | - active_loop: null 69 | steps: 70 | - intent: transfer_money 71 | - action: transfer_money_form 72 | - active_loop: transfer_money_form 73 | 74 | - rule: Activate transaction_search_form when no other form is active 75 | condition: 76 | # this condition allows stories to handle form switching 77 | - active_loop: null 78 | steps: 79 | - or: 80 | - intent: search_transactions 81 | - intent: check_earnings 82 | - action: transaction_search_form 83 | - active_loop: transaction_search_form 84 | 85 | - rule: Submit cc_payment_form while not switched from previous form 86 | condition: 87 | - active_loop: cc_payment_form 88 | - slot_was_set: 89 | - previous_form_name: null 90 | steps: 91 | - action: cc_payment_form 92 | - active_loop: null 93 | - slot_was_set: 94 | - requested_slot: null 95 | - action: action_pay_cc 96 | 97 | - rule: Submit transfer_money_form while not switched from previous form 98 | condition: 99 | - active_loop: transfer_money_form 100 | - slot_was_set: 101 | - previous_form_name: null 102 | steps: 103 | - action: transfer_money_form 104 | - active_loop: null 105 | - slot_was_set: 106 | - requested_slot: null 107 | - action: action_transfer_money 108 | 109 | - rule: Submit transaction_search_form while not switched from previous form 110 | condition: 111 | - active_loop: transaction_search_form 112 | - slot_was_set: 113 | - previous_form_name: null 114 | steps: 115 | - action: transaction_search_form 116 | - active_loop: null 117 | - slot_was_set: 118 | - requested_slot: null 119 | - action: action_transaction_search 120 | -------------------------------------------------------------------------------- /data/rules/rules_handoff.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | rules: 4 | 5 | - rule: Handoff intent from other bot's handoff triggers greeting 6 | steps: 7 | - intent: handoff 8 | - action: utter_greet 9 | 10 | - rule: Provide handoff options 11 | steps: 12 | - intent: human_handoff 13 | - action: utter_ask_handoff 14 | - action: action_handoff_options 15 | 16 | - rule: Execute handoff 17 | steps: 18 | - action: action_handoff_options 19 | - intent: trigger_handoff 20 | - action: action_handoff 21 | 22 | - rule: Deny handoff 23 | steps: 24 | - action: action_handoff_options 25 | - intent: deny 26 | - action: utter_ask_whatelse 27 | -------------------------------------------------------------------------------- /data/stories/stories.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | stories: 4 | 5 | - story: say ok when user affirms after utter_help 6 | steps: 7 | - action: utter_help 8 | - intent: affirm 9 | - action: utter_ok 10 | 11 | - story: say ok when user affirms after action_show_balance 12 | steps: 13 | - action: action_show_balance 14 | - intent: affirm 15 | - action: utter_ok 16 | 17 | - story: say ok when user affirms after action_show_recipients 18 | steps: 19 | - action: action_show_recipients 20 | - intent: affirm 21 | - action: utter_ok 22 | 23 | - story: say ok when user affirms after action_show_transfer_charge 24 | steps: 25 | - action: action_show_transfer_charge 26 | - intent: affirm 27 | - action: utter_ok 28 | -------------------------------------------------------------------------------- /data/stories/stories_switch_from_pay_credit_card.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | stories: 4 | 5 | - story: pay credit card + switch to search transactions or transfer money, deny 6 | steps: 7 | - intent: pay_cc 8 | - action: cc_payment_form 9 | - active_loop: cc_payment_form 10 | - or: 11 | - intent: search_transactions 12 | - intent: check_earnings 13 | - intent: transfer_money 14 | - action: action_switch_forms_ask 15 | - slot_was_set: 16 | - next_form_name: another form 17 | - intent: deny 18 | - action: action_switch_forms_deny 19 | - slot_was_set: 20 | - next_form_name: null 21 | - action: cc_payment_form 22 | - active_loop: cc_payment_form 23 | 24 | - story: pay credit card + switch to search transactions, affirm + switch back, deny 25 | steps: 26 | - intent: pay_cc 27 | - action: cc_payment_form 28 | - active_loop: cc_payment_form 29 | - or: 30 | - intent: search_transactions 31 | - intent: check_earnings 32 | - action: action_switch_forms_ask 33 | - slot_was_set: 34 | - next_form_name: transaction_search_form 35 | - intent: affirm 36 | - action: action_switch_forms_affirm 37 | - slot_was_set: 38 | - next_form_name: null 39 | - slot_was_set: 40 | - previous_form_name: cc_payment_form 41 | - action: transaction_search_form 42 | - active_loop: transaction_search_form 43 | - active_loop: null 44 | - slot_was_set: 45 | - requested_slot: null 46 | - action: action_transaction_search 47 | - action: action_switch_back_ask 48 | - slot_was_set: 49 | - previous_form_name: null 50 | - intent: deny 51 | - action: utter_ask_whatelse 52 | 53 | - story: pay credit card + switch to transfer money, affirm + switch back, deny 54 | steps: 55 | - intent: pay_cc 56 | - action: cc_payment_form 57 | - active_loop: cc_payment_form 58 | - intent: transfer_money 59 | - action: action_switch_forms_ask 60 | - slot_was_set: 61 | - next_form_name: transfer_money_form 62 | - intent: affirm 63 | - action: action_switch_forms_affirm 64 | - slot_was_set: 65 | - next_form_name: null 66 | - slot_was_set: 67 | - previous_form_name: cc_payment_form 68 | - action: transfer_money_form 69 | - active_loop: transfer_money_form 70 | - active_loop: null 71 | - slot_was_set: 72 | - requested_slot: null 73 | - action: action_transfer_money 74 | - action: action_switch_back_ask 75 | - slot_was_set: 76 | - previous_form_name: null 77 | - intent: deny 78 | - action: utter_ask_whatelse 79 | 80 | - story: pay credit card + switch to search transactions, affirm + switch back, affirm 81 | steps: 82 | - intent: pay_cc 83 | - action: cc_payment_form 84 | - active_loop: cc_payment_form 85 | - or: 86 | - intent: search_transactions 87 | - intent: check_earnings 88 | - action: action_switch_forms_ask 89 | - slot_was_set: 90 | - next_form_name: transaction_search_form 91 | - intent: affirm 92 | - action: action_switch_forms_affirm 93 | - slot_was_set: 94 | - next_form_name: null 95 | - slot_was_set: 96 | - previous_form_name: cc_payment_form 97 | - action: transaction_search_form 98 | - active_loop: transaction_search_form 99 | - active_loop: null 100 | - slot_was_set: 101 | - requested_slot: null 102 | - action: action_transaction_search 103 | - action: action_switch_back_ask 104 | - slot_was_set: 105 | - previous_form_name: null 106 | - intent: affirm 107 | - action: cc_payment_form 108 | - active_loop: cc_payment_form 109 | 110 | - story: pay credit card + switch to transfer money, affirm + switch back, affirm 111 | steps: 112 | - intent: pay_cc 113 | - action: cc_payment_form 114 | - active_loop: cc_payment_form 115 | - intent: transfer_money 116 | - action: action_switch_forms_ask 117 | - slot_was_set: 118 | - next_form_name: transfer_money_form 119 | - intent: affirm 120 | - action: action_switch_forms_affirm 121 | - slot_was_set: 122 | - next_form_name: null 123 | - slot_was_set: 124 | - previous_form_name: cc_payment_form 125 | - action: transfer_money_form 126 | - active_loop: transfer_money_form 127 | - active_loop: null 128 | - slot_was_set: 129 | - requested_slot: null 130 | - action: action_transfer_money 131 | - action: action_switch_back_ask 132 | - slot_was_set: 133 | - previous_form_name: null 134 | - intent: affirm 135 | - action: cc_payment_form 136 | - active_loop: cc_payment_form 137 | -------------------------------------------------------------------------------- /data/stories/stories_switch_from_search_transactions.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | stories: 4 | 5 | - story: search transactions + switch to transfer money or pay credit card, deny 6 | steps: 7 | - or: 8 | - intent: search_transactions 9 | - intent: check_earnings 10 | - action: transaction_search_form 11 | - active_loop: transaction_search_form 12 | - or: 13 | - intent: transfer_money 14 | - intent: pay_cc 15 | - action: action_switch_forms_ask 16 | - slot_was_set: 17 | - next_form_name: another form 18 | - intent: deny 19 | - action: action_switch_forms_deny 20 | - slot_was_set: 21 | - next_form_name: null 22 | - action: transaction_search_form 23 | - active_loop: transaction_search_form 24 | 25 | - story: search transactions + switch to transfer money, affirm + switch back, deny 26 | steps: 27 | - or: 28 | - intent: search_transactions 29 | - intent: check_earnings 30 | - action: transaction_search_form 31 | - active_loop: transaction_search_form 32 | - intent: transfer_money 33 | - action: action_switch_forms_ask 34 | - slot_was_set: 35 | - next_form_name: transfer_money_form 36 | - intent: affirm 37 | - action: action_switch_forms_affirm 38 | - slot_was_set: 39 | - next_form_name: null 40 | - slot_was_set: 41 | - previous_form_name: transaction_search_form 42 | - action: transfer_money_form 43 | - active_loop: transfer_money_form 44 | - active_loop: null 45 | - slot_was_set: 46 | - requested_slot: null 47 | - action: action_transfer_money 48 | - action: action_switch_back_ask 49 | - slot_was_set: 50 | - previous_form_name: null 51 | - intent: deny 52 | - action: utter_ask_whatelse 53 | 54 | - story: search transactions + switch to pay credit card, affirm + switch back, deny 55 | steps: 56 | - or: 57 | - intent: search_transactions 58 | - intent: check_earnings 59 | - action: transaction_search_form 60 | - active_loop: transaction_search_form 61 | - intent: pay_cc 62 | - action: action_switch_forms_ask 63 | - slot_was_set: 64 | - next_form_name: cc_payment_form 65 | - intent: affirm 66 | - action: action_switch_forms_affirm 67 | - slot_was_set: 68 | - next_form_name: null 69 | - slot_was_set: 70 | - previous_form_name: transaction_search_form 71 | - action: cc_payment_form 72 | - active_loop: cc_payment_form 73 | - active_loop: null 74 | - slot_was_set: 75 | - requested_slot: null 76 | - action: action_pay_cc 77 | - action: action_switch_back_ask 78 | - slot_was_set: 79 | - previous_form_name: null 80 | - intent: deny 81 | - action: utter_ask_whatelse 82 | 83 | - story: search transactions + switch to transfer money, affirm + switch back, affirm 84 | steps: 85 | - or: 86 | - intent: search_transactions 87 | - intent: check_earnings 88 | - action: transaction_search_form 89 | - active_loop: transaction_search_form 90 | - intent: transfer_money 91 | - action: action_switch_forms_ask 92 | - slot_was_set: 93 | - next_form_name: transfer_money_form 94 | - intent: affirm 95 | - action: action_switch_forms_affirm 96 | - slot_was_set: 97 | - next_form_name: null 98 | - slot_was_set: 99 | - previous_form_name: transaction_search_form 100 | - action: transfer_money_form 101 | - active_loop: transfer_money_form 102 | - active_loop: null 103 | - slot_was_set: 104 | - requested_slot: null 105 | - action: action_transfer_money 106 | - action: action_switch_back_ask 107 | - slot_was_set: 108 | - previous_form_name: null 109 | - intent: affirm 110 | - action: transaction_search_form 111 | - active_loop: transaction_search_form 112 | 113 | - story: search transactions + switch to pay credit card, affirm + switch back, affirm 114 | steps: 115 | - or: 116 | - intent: search_transactions 117 | - intent: check_earnings 118 | - action: transaction_search_form 119 | - active_loop: transaction_search_form 120 | - intent: pay_cc 121 | - action: action_switch_forms_ask 122 | - slot_was_set: 123 | - next_form_name: cc_payment_form 124 | - intent: affirm 125 | - action: action_switch_forms_affirm 126 | - slot_was_set: 127 | - next_form_name: null 128 | - slot_was_set: 129 | - previous_form_name: transaction_search_form 130 | - action: cc_payment_form 131 | - active_loop: cc_payment_form 132 | - active_loop: null 133 | - slot_was_set: 134 | - requested_slot: null 135 | - action: action_pay_cc 136 | - action: action_switch_back_ask 137 | - slot_was_set: 138 | - previous_form_name: null 139 | - intent: affirm 140 | - action: transaction_search_form 141 | - active_loop: transaction_search_form 142 | -------------------------------------------------------------------------------- /data/stories/stories_switch_from_transfer_money.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | 3 | stories: 4 | 5 | - story: transfer money + switch to search transactions or pay credit card, deny 6 | steps: 7 | - intent: transfer_money 8 | - action: transfer_money_form 9 | - active_loop: transfer_money_form 10 | - or: 11 | - intent: search_transactions 12 | - intent: check_earnings 13 | - intent: pay_cc 14 | - action: action_switch_forms_ask 15 | - slot_was_set: 16 | - next_form_name: another form 17 | - intent: deny 18 | - action: action_switch_forms_deny 19 | - slot_was_set: 20 | - next_form_name: null 21 | - action: transfer_money_form 22 | - active_loop: transfer_money_form 23 | 24 | - story: transfer money + switch to search transactions, affirm + switch back, deny 25 | steps: 26 | - intent: transfer_money 27 | - action: transfer_money_form 28 | - active_loop: transfer_money_form 29 | - or: 30 | - intent: search_transactions 31 | - intent: check_earnings 32 | - action: action_switch_forms_ask 33 | - slot_was_set: 34 | - next_form_name: transaction_search_form 35 | - intent: affirm 36 | - action: action_switch_forms_affirm 37 | - slot_was_set: 38 | - next_form_name: null 39 | - slot_was_set: 40 | - previous_form_name: transfer_money_form 41 | - action: transaction_search_form 42 | - active_loop: transaction_search_form 43 | - active_loop: null 44 | - slot_was_set: 45 | - requested_slot: null 46 | - action: action_transaction_search 47 | - action: action_switch_back_ask 48 | - slot_was_set: 49 | - previous_form_name: null 50 | - intent: deny 51 | - action: utter_ask_whatelse 52 | 53 | - story: transfer money + switch to pay credit card, affirm + switch back, deny 54 | steps: 55 | - intent: transfer_money 56 | - action: transfer_money_form 57 | - active_loop: transfer_money_form 58 | - intent: pay_cc 59 | - action: action_switch_forms_ask 60 | - slot_was_set: 61 | - next_form_name: cc_payment_form 62 | - intent: affirm 63 | - action: action_switch_forms_affirm 64 | - slot_was_set: 65 | - next_form_name: null 66 | - slot_was_set: 67 | - previous_form_name: transfer_money_form 68 | - action: cc_payment_form 69 | - active_loop: cc_payment_form 70 | - active_loop: null 71 | - slot_was_set: 72 | - requested_slot: null 73 | - action: action_pay_cc 74 | - action: action_switch_back_ask 75 | - slot_was_set: 76 | - previous_form_name: null 77 | - intent: deny 78 | - action: utter_ask_whatelse 79 | 80 | - story: transfer money+ switch to search transactions, affirm + switch back, affirm 81 | steps: 82 | - intent: transfer_money 83 | - action: transfer_money_form 84 | - active_loop: transfer_money_form 85 | - or: 86 | - intent: search_transactions 87 | - intent: check_earnings 88 | - action: action_switch_forms_ask 89 | - slot_was_set: 90 | - next_form_name: transaction_search_form 91 | - intent: affirm 92 | - action: action_switch_forms_affirm 93 | - slot_was_set: 94 | - next_form_name: null 95 | - slot_was_set: 96 | - previous_form_name: transfer_money_form 97 | - action: transaction_search_form 98 | - active_loop: transaction_search_form 99 | - active_loop: null 100 | - slot_was_set: 101 | - requested_slot: null 102 | - action: action_transaction_search 103 | - action: action_switch_back_ask 104 | - slot_was_set: 105 | - previous_form_name: null 106 | - intent: affirm 107 | - action: transfer_money_form 108 | - active_loop: transfer_money_form 109 | 110 | - story: transfer money+ switch to pay credit card, affirm + switch back, affirm 111 | steps: 112 | - intent: transfer_money 113 | - action: transfer_money_form 114 | - active_loop: transfer_money_form 115 | - intent: pay_cc 116 | - action: action_switch_forms_ask 117 | - slot_was_set: 118 | - next_form_name: cc_payment_form 119 | - intent: affirm 120 | - action: action_switch_forms_affirm 121 | - slot_was_set: 122 | - next_form_name: null 123 | - slot_was_set: 124 | - previous_form_name: transfer_money_form 125 | - action: cc_payment_form 126 | - active_loop: cc_payment_form 127 | - active_loop: null 128 | - slot_was_set: 129 | - requested_slot: null 130 | - action: action_pay_cc 131 | - action: action_switch_back_ask 132 | - slot_was_set: 133 | - previous_form_name: null 134 | - intent: affirm 135 | - action: transfer_money_form 136 | - active_loop: transfer_money_form 137 | -------------------------------------------------------------------------------- /deploy/gcr-auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "rasa-platform", 4 | "private_key_id": "set_as_environment_variable-GCR_AUTH_JSON_PRIVATE_KEY_ID", 5 | "private_key": "set_as_environment_variable-GCR_AUTH_JSON_PRIVATE_KEY", 6 | "client_email": "set_as_environment_variable-GCR_AUTH_JSON_CLIENT_EMAIL", 7 | "client_id": "set_as_environment_variable-GCR_AUTH_JSON_CLIENT_ID", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://accounts.google.com/o/oauth2/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/rasa-testing%40rasa-platform.iam.gserviceaccount.com" 12 | } -------------------------------------------------------------------------------- /deploy/values.yml: -------------------------------------------------------------------------------- 1 | # debugMode enables / disables the debug mode for Rasa and Rasa X 2 | debugMode: "set_as_environment_variable-RASAX_DEBUG_MODE" 3 | # nginx ingress controller 4 | nginx: 5 | service: 6 | port: 80 7 | # docker pull secret for private images 8 | images: 9 | imagePullSecrets: 10 | - name: "gcr-pull-secret" 11 | - name: "ecr-pull-secret" 12 | # action server image 13 | app: 14 | name: "set_as_environment_variable-APP_NAME" 15 | tag: "set_as_environment_variable-APP_TAG" 16 | # rasax specific settings 17 | rasax: 18 | tag: "set_as_environment_variable-RASAX_TAG" 19 | # name of the Rasa Enterprise image to use 20 | name: "gcr.io/rasa-platform/rasa-x-ee" 21 | # initialUser is the user which is created upon the initial start of Rasa Enterprise 22 | initialUser: 23 | # username specifies a name of this user 24 | username: "set_as_environment_variable-RASAX_INITIALUSER_USERNAME" 25 | # password for the Rasa Enterprise user 26 | password: "set_as_environment_variable-RASAX_INITIALUSER_PASSWORD" 27 | # passwordSalt Rasa X uses to salt the user passwords 28 | passwordSalt: "set_as_environment_variable-RASAX_PASSWORDSALT" 29 | # token Rasa X accepts as authentication token from other Rasa services 30 | token: "set_as_environment_variable-RASAX_TOKEN" 31 | # jwtSecret which is used to sign the jwtTokens of the users 32 | jwtSecret: "set_as_environment_variable-RASAX_JWTSECRET" 33 | # rasa: Settings common for all Rasa containers 34 | rasa: 35 | tag: "set_as_environment_variable-RASA_TAG" 36 | # token Rasa accepts as authentication token from other Rasa services 37 | token: "set_as_environment_variable-RASA_TOKEN" 38 | livenessProbe: 39 | # initialProbeDelay for the `livenessProbe` 40 | initialProbeDelay: 60 41 | # RabbitMQ specific settings 42 | rabbitmq: 43 | # rabbitmq settings of the subchart 44 | rabbitmq: 45 | # password which is used for the authentication 46 | password: "set_as_environment_variable-RABBITMQ_RABBITMQ_PASSWORD" 47 | # global settings of the used subcharts 48 | global: 49 | # postgresql: global settings of the postgresql subchart 50 | postgresql: 51 | # postgresqlPassword is the password which is used when the postgresqlUsername equals "postgres" 52 | postgresqlPassword: "set_as_environment_variable-GLOBAL_POSTGRESQL_POSTGRESQLPASSWORD" 53 | # redis: global settings of the redis subchart 54 | redis: 55 | # password to use in case there no external secret was provided 56 | password: "set_as_environment_variable-GLOBAL_REDIS_PASSWORD" -------------------------------------------------------------------------------- /domain.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | session_config: 3 | session_expiration_time: 0 4 | carry_over_slots_to_new_session: true 5 | intents: 6 | - check_human 7 | - transfer_money: 8 | use_entities: [] 9 | - inform 10 | - pay_cc: 11 | use_entities: [] 12 | - greet 13 | - goodbye 14 | - affirm 15 | - deny 16 | - thankyou 17 | - ask_transfer_charge 18 | - search_transactions: 19 | use_entities: [] 20 | - check_balance: 21 | use_entities: 22 | - credit_card 23 | - account_type 24 | - check_earnings: 25 | use_entities: [] 26 | - check_recipients 27 | - out_of_scope 28 | - session_start 29 | - restart 30 | - trigger_handoff 31 | - handoff 32 | - human_handoff 33 | - help 34 | - nlu_fallback 35 | entities: 36 | - amount-of-money 37 | - credit_card 38 | - payment_date 39 | - vendor_name 40 | - time 41 | - PERSON 42 | - number 43 | - account_type 44 | - handoff_to 45 | - search_type 46 | slots: 47 | AA_CONTINUE_FORM: 48 | type: any 49 | influence_conversation: false 50 | mappings: 51 | - intent: affirm 52 | type: from_intent 53 | value: yes 54 | conditions: 55 | - active_loop: cc_payment_form 56 | requested_slot: AA_CONTINUE_FORM 57 | - active_loop: transfer_money_form 58 | requested_slot: AA_CONTINUE_FORM 59 | - active_loop: transaction_search_form 60 | requested_slot: AA_CONTINUE_FORM 61 | - intent: deny 62 | type: from_intent 63 | value: no 64 | conditions: 65 | - active_loop: cc_payment_form 66 | requested_slot: AA_CONTINUE_FORM 67 | - active_loop: transfer_money_form 68 | requested_slot: AA_CONTINUE_FORM 69 | - active_loop: transaction_search_form 70 | requested_slot: AA_CONTINUE_FORM 71 | - intent: 72 | - inform 73 | - cc_payment_form 74 | type: from_text 75 | conditions: 76 | - active_loop: cc_payment_form 77 | requested_slot: AA_CONTINUE_FORM 78 | - intent: 79 | - inform 80 | - transfer_money_form 81 | type: from_text 82 | conditions: 83 | - active_loop: transfer_money_form 84 | requested_slot: AA_CONTINUE_FORM 85 | - intent: 86 | - inform 87 | - transaction_search_form 88 | type: from_text 89 | conditions: 90 | - active_loop: transaction_search_form 91 | requested_slot: AA_CONTINUE_FORM 92 | PERSON: 93 | type: any 94 | influence_conversation: false 95 | mappings: 96 | - entity: PERSON 97 | type: from_entity 98 | conditions: 99 | - active_loop: transfer_money_form 100 | requested_slot: PERSON 101 | - intent: 102 | - inform 103 | - transfer_money_form 104 | type: from_text 105 | conditions: 106 | - active_loop: transfer_money_form 107 | requested_slot: PERSON 108 | - type: from_entity 109 | entity: PERSON 110 | account_type: 111 | type: any 112 | influence_conversation: false 113 | mappings: 114 | - type: from_entity 115 | entity: account_type 116 | amount-of-money: 117 | type: any 118 | influence_conversation: false 119 | mappings: 120 | - entity: amount-of-money 121 | not_intent: 122 | - check_balance 123 | - check_earnings 124 | type: from_entity 125 | conditions: 126 | - active_loop: cc_payment_form 127 | requested_slot: amount-of-money 128 | - active_loop: transfer_money_form 129 | requested_slot: amount-of-money 130 | - entity: number 131 | not_intent: 132 | - check_balance 133 | - check_earnings 134 | type: from_entity 135 | conditions: 136 | - active_loop: cc_payment_form 137 | requested_slot: amount-of-money 138 | - active_loop: transfer_money_form 139 | requested_slot: amount-of-money 140 | - intent: 141 | - inform 142 | - cc_payment_form 143 | type: from_text 144 | conditions: 145 | - active_loop: cc_payment_form 146 | requested_slot: amount-of-money 147 | - intent: 148 | - inform 149 | - transfer_money_form 150 | type: from_text 151 | conditions: 152 | - active_loop: transfer_money_form 153 | requested_slot: amount-of-money 154 | - type: from_entity 155 | entity: amount-of-money 156 | amount_transferred: 157 | type: any 158 | initial_value: 0 159 | influence_conversation: false 160 | mappings: 161 | - type: custom 162 | credit_card: 163 | type: any 164 | influence_conversation: false 165 | mappings: 166 | - entity: credit_card 167 | type: from_entity 168 | conditions: 169 | - active_loop: cc_payment_form 170 | requested_slot: credit_card 171 | - intent: 172 | - inform 173 | - cc_payment_form 174 | type: from_text 175 | conditions: 176 | - active_loop: cc_payment_form 177 | requested_slot: credit_card 178 | - type: from_entity 179 | entity: credit_card 180 | currency: 181 | type: any 182 | influence_conversation: false 183 | initial_value: $ 184 | mappings: 185 | - type: custom 186 | end_time: 187 | type: any 188 | influence_conversation: false 189 | mappings: 190 | - type: custom 191 | end_time_formatted: 192 | type: any 193 | influence_conversation: false 194 | mappings: 195 | - type: custom 196 | grain: 197 | type: any 198 | influence_conversation: false 199 | mappings: 200 | - type: custom 201 | handoff_to: 202 | type: any 203 | influence_conversation: false 204 | mappings: 205 | - type: from_entity 206 | entity: handoff_to 207 | next_form_name: 208 | type: text 209 | influence_conversation: true 210 | mappings: 211 | - type: custom 212 | number: 213 | type: any 214 | influence_conversation: false 215 | mappings: 216 | - type: from_entity 217 | entity: number 218 | payment_amount_type: 219 | type: any 220 | initial_value: '' 221 | influence_conversation: false 222 | mappings: 223 | - type: custom 224 | previous_form_name: 225 | type: text 226 | influence_conversation: true 227 | mappings: 228 | - type: custom 229 | repeated_validation_failures: 230 | type: any 231 | influence_conversation: false 232 | mappings: 233 | - type: custom 234 | requested_slot: 235 | type: any 236 | influence_conversation: false 237 | mappings: 238 | - type: custom 239 | search_type: 240 | type: any 241 | influence_conversation: false 242 | mappings: 243 | - intent: search_transactions 244 | type: from_trigger_intent 245 | value: spend 246 | - intent: check_earnings 247 | type: from_trigger_intent 248 | value: deposit 249 | - type: from_entity 250 | entity: search_type 251 | conditions: 252 | - active_loop: transaction_search_form 253 | - type: from_entity 254 | entity: search_type 255 | start_time: 256 | type: any 257 | influence_conversation: false 258 | mappings: 259 | - type: custom 260 | start_time_formatted: 261 | type: any 262 | influence_conversation: false 263 | mappings: 264 | - type: custom 265 | time: 266 | type: any 267 | influence_conversation: false 268 | mappings: 269 | - entity: time 270 | type: from_entity 271 | conditions: 272 | - active_loop: cc_payment_form 273 | requested_slot: time 274 | - active_loop: transaction_search_form 275 | requested_slot: time 276 | - intent: 277 | - inform 278 | - cc_payment_form 279 | type: from_text 280 | conditions: 281 | - active_loop: cc_payment_form 282 | requested_slot: time 283 | - intent: 284 | - inform 285 | - transaction_search_form 286 | type: from_text 287 | conditions: 288 | - active_loop: transaction_search_form 289 | requested_slot: time 290 | - type: from_entity 291 | entity: time 292 | time_formatted: 293 | type: any 294 | influence_conversation: false 295 | mappings: 296 | - type: custom 297 | vendor_name: 298 | type: any 299 | influence_conversation: false 300 | mappings: 301 | - type: from_entity 302 | entity: vendor_name 303 | zz_confirm_form: 304 | type: any 305 | influence_conversation: false 306 | mappings: 307 | - intent: affirm 308 | type: from_intent 309 | value: yes 310 | conditions: 311 | - active_loop: cc_payment_form 312 | requested_slot: zz_confirm_form 313 | - active_loop: transfer_money_form 314 | requested_slot: zz_confirm_form 315 | - active_loop: transaction_search_form 316 | requested_slot: zz_confirm_form 317 | - intent: deny 318 | type: from_intent 319 | value: no 320 | conditions: 321 | - active_loop: cc_payment_form 322 | requested_slot: zz_confirm_form 323 | - active_loop: transfer_money_form 324 | requested_slot: zz_confirm_form 325 | - active_loop: transaction_search_form 326 | requested_slot: zz_confirm_form 327 | - intent: 328 | - inform 329 | - cc_payment_form 330 | type: from_text 331 | conditions: 332 | - active_loop: cc_payment_form 333 | requested_slot: zz_confirm_form 334 | - intent: 335 | - inform 336 | - transfer_money_form 337 | type: from_text 338 | conditions: 339 | - active_loop: transfer_money_form 340 | requested_slot: zz_confirm_form 341 | - intent: 342 | - inform 343 | - transaction_search_form 344 | type: from_text 345 | conditions: 346 | - active_loop: transaction_search_form 347 | requested_slot: zz_confirm_form 348 | responses: 349 | utter_out_of_scope: 350 | - text: Sorry, I'm not sure how to respond to that. Type "help" for assistance. 351 | utter_ask_transfer_money_form_amount-of-money: 352 | - text: How much money do you want to transfer? 353 | utter_ask_transfer_money_form_PERSON: 354 | - text: Who do you want to transfer money to? 355 | utter_goodbye: 356 | - text: Bye 357 | utter_noworries: 358 | - text: You're welcome :) 359 | utter_transfer_complete: 360 | - text: Successfully transferred {currency}{amount-of-money} to {PERSON}. 361 | utter_transfer_charge: 362 | - text: You are entitled to six transfers within a statement cycle before being charged. For subsequent transfers you will be charged {currency}10 per transaction. 363 | utter_ask_cc_payment_form_amount-of-money: 364 | - text: How much do you want to pay? 365 | utter_ask_cc_payment_form_credit_card: 366 | - text: Towards which credit card account do you want to make a payment? 367 | utter_ask_cc_payment_form_time: 368 | - text: For which date would you like to schedule the payment? 369 | utter_ask_transaction_search_form_vendor_name: 370 | - text: For which vendor do you want to see transactions? e.g Starbucks, Target, Amazon 371 | utter_ask_transaction_search_form_time: 372 | - text: In which timeframe would you like to search for transactions? 373 | utter_ask_transaction_search_form_search_type: 374 | - text: Would you like to search incoming or outgoing transactions? 375 | buttons: 376 | - title: Incoming (earnings) 377 | payload: /inform{"search_type":"deposit"}' 378 | - title: Outgoing (spending) 379 | payload: /inform{"search_type":"spend"}' 380 | utter_no_payment_amount: 381 | - text: Sorry, I don't understand that payment amount. 382 | utter_no_paymentdate: 383 | - text: Sorry, that is not a valid payment date. 384 | utter_no_creditcard: 385 | - text: Sorry, that is not a valid credit card account to make payments towards. 386 | utter_no_vendor_name: 387 | - text: Sorry, that's not a recognized vendor. 388 | utter_no_transactdate: 389 | - text: Sorry, that's not a recognized time frame. 390 | utter_cc_pay_scheduled: 391 | - text: Payment of {currency}{amount-of-money}{payment_amount_type} towards your {credit_card} account scheduled to be paid at {time_formatted}. 392 | utter_searching_spend_transactions: 393 | - text: Searching transactions{vendor_name} between {start_time_formatted} and {end_time_formatted}... 394 | utter_found_spend_transactions: 395 | - text: I found {numtransacts} transactions{vendor_name} totalling {currency}{total}. 396 | utter_searching_deposit_transactions: 397 | - text: Searching deposits made to your account between {start_time_formatted} and {end_time_formatted}... 398 | utter_found_deposit_transactions: 399 | - text: I found {numtransacts} deposits made to your account totalling {currency}{total} 400 | utter_ask_rephrase: 401 | - text: I didn't quite understand that. Can you rephrase? 402 | utter_ok: 403 | - text: 👍 404 | utter_ask_continue: 405 | - text: Would you like to continue? 406 | utter_default: 407 | - text: I didn't quite understand that. Could you rephrase? 408 | utter_ask_cc_payment_form_AA_CONTINUE_FORM: 409 | - buttons: 410 | - payload: /affirm 411 | title: Yes 412 | - payload: /deny 413 | title: No, cancel the transaction 414 | text: Would you like to continue scheduling the credit card payment? 415 | utter_ask_transfer_money_form_AA_CONTINUE_FORM: 416 | - buttons: 417 | - payload: /affirm 418 | title: Yes 419 | - payload: /deny 420 | title: No, cancel the transfer 421 | text: Would you like to continue scheduling the money transfer? 422 | utter_ask_transaction_search_form_AA_CONTINUE_FORM: 423 | - buttons: 424 | - payload: /affirm 425 | title: Yes 426 | - payload: /deny 427 | title: No, cancel the search 428 | text: Would you like to continue the transaction search? 429 | utter_ask_cc_payment_form_zz_confirm_form: 430 | - buttons: 431 | - payload: /affirm 432 | title: Yes 433 | - payload: /deny 434 | title: No, cancel the transaction 435 | text: Would you like to schedule a payment of {currency}{amount-of-money}{payment_amount_type} towards your {credit_card} account for {time_formatted}? 436 | utter_ask_transfer_money_form_zz_confirm_form: 437 | - buttons: 438 | - payload: /affirm 439 | title: Yes 440 | - payload: /deny 441 | title: No, cancel the transaction 442 | text: Would you like to transfer {currency}{amount-of-money} to {PERSON}? 443 | utter_cc_pay_cancelled: 444 | - text: Credit card account payment cancelled. 445 | utter_transfer_cancelled: 446 | - text: Transfer cancelled. 447 | utter_transaction_search_cancelled: 448 | - text: Transaction search cancelled. 449 | utter_account_balance: 450 | - text: Your bank account balance is {currency}{init_account_balance}. 451 | utter_changed_account_balance: 452 | - text: Your bank account balance was {currency}{init_account_balance} and is now {currency}{account_balance} after transfers and payments. 453 | utter_unknown_recipient: 454 | - text: Sorry, {PERSON} is not in your list of known recipients. 455 | utter_insufficient_funds: 456 | - text: Sorry, you don't have enough money to do that! 457 | utter_insufficient_funds_specific: 458 | - text: The {payment_amount_type} on your {credit_card} credit card is {amount-of-money}, so you have insufficient funds to pay it off. 459 | utter_credit_card_balance: 460 | - text: The current balance for your {credit_card} account is {currency}{credit_card_balance}. 461 | utter_nothing_due: 462 | - text: Your don't owe any money on your {credit_card} credit card bill. 463 | utter_recipients: 464 | - text: These are your known recpients to whom you can send money:{formatted_recipients} 465 | utter_greet: 466 | - text: Hi! I'm your Financial Assistant! 467 | utter_ask_handoff: 468 | - text: It looks like you want to be transferred to a human agent. 469 | utter_handoff: 470 | - text: Alright, I'll try to transfer you. 471 | utter_wouldve_handed_off: 472 | - text: If you were talking to me via chatroom, I would have handed you off to {handoffhost}. 473 | utter_no_handoff: 474 | - text: Since you haven't configured a host to hand off to, I can't send you anywhere! 475 | utter_ask_whatelse: 476 | - text: What else can I help you with? 477 | utter_bot: 478 | - text: I'm a virtual assistant made with Rasa. 479 | utter_help: 480 | - text: "I can help you with your financial accounts. \nYou can ask me things like: \n- What's my account balance? \n- Pay off my credit card \n- What did I spend at Target last month? \n- I need to transfer money" 481 | actions: 482 | - action_session_start 483 | - action_restart 484 | - action_show_balance 485 | - action_show_recipients 486 | - action_show_transfer_charge 487 | - action_handoff 488 | - action_handoff_options 489 | - action_pay_cc 490 | - action_transfer_money 491 | - action_transaction_search 492 | - action_ask_transaction_search_form_zz_confirm_form 493 | - action_switch_forms_ask 494 | - action_switch_forms_deny 495 | - action_switch_forms_affirm 496 | - action_switch_back_ask 497 | - validate_cc_payment_form 498 | - validate_transfer_money_form 499 | - validate_transaction_search_form 500 | forms: 501 | cc_payment_form: 502 | ignored_intents: [] 503 | required_slots: 504 | - AA_CONTINUE_FORM 505 | - credit_card 506 | - amount-of-money 507 | - time 508 | - zz_confirm_form 509 | transfer_money_form: 510 | ignored_intents: [] 511 | required_slots: 512 | - AA_CONTINUE_FORM 513 | - PERSON 514 | - amount-of-money 515 | - zz_confirm_form 516 | transaction_search_form: 517 | ignored_intents: [] 518 | required_slots: 519 | - AA_CONTINUE_FORM 520 | - search_type 521 | - time 522 | - zz_confirm_form 523 | -------------------------------------------------------------------------------- /endpoints.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | # This file contains the different endpoints your bot can use. 3 | 4 | # Server where the models are pulled from. 5 | # https://rasa.com/docs/rasa/user-guide/running-the-server/#fetching-models-from-a-server/ 6 | 7 | #models: 8 | # url: http://my-server.com/models/default_core@latest 9 | # wait_time_between_pulls: 10 # [optional](default: 100) 10 | 11 | # Server which runs your custom actions. 12 | # https://rasa.com/docs/rasa/core/actions/#custom-actions/ 13 | 14 | action_endpoint: 15 | url: "http://localhost:5056/webhook" 16 | 17 | # Tracker store which is used to store the conversations. 18 | # By default the conversations are stored in memory. 19 | # https://rasa.com/docs/rasa/api/tracker-stores/ 20 | 21 | #tracker_store: 22 | # type: redis 23 | # url: 24 | # port: 25 | # db: 26 | # password: 27 | # use_ssl: 28 | 29 | #tracker_store: 30 | # type: mongod 31 | # url: 32 | # db: 33 | # username: 34 | # password: 35 | 36 | # Event broker which all conversation events should be streamed to. 37 | # https://rasa.com/docs/rasa/api/event-brokers/ 38 | 39 | #event_broker: 40 | # url: localhost 41 | # username: username 42 | # password: password 43 | # queue: queue 44 | -------------------------------------------------------------------------------- /handoff.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/financial-demo/7627cebef1571d55855b886cf4ac20459af9566c/handoff.gif -------------------------------------------------------------------------------- /images/cicd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/financial-demo/7627cebef1571d55855b886cf4ac20459af9566c/images/cicd.png -------------------------------------------------------------------------------- /images/financial-demo-eks-test-vpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/financial-demo/7627cebef1571d55855b886cf4ac20459af9566c/images/financial-demo-eks-test-vpc.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 88 3 | target-version = [ "py36", "py37", "py38",] 4 | exclude = "((.eggs | .git | .mypy_cache | .pytest_cache | build | dist))" 5 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | # action to create report PR 3 | pytablewriter 4 | 5 | # python lint/format/types 6 | black~=20.8b1 7 | flake8~=3.8.4 8 | pytype==2019.7.11 9 | pre-commit 10 | 11 | # python test 12 | pytest-cov~=2.10.1 13 | coveralls~=3.0.1 14 | pytest~=5.4.3 15 | pytest-sanic~=1.6.2 16 | pytest-asyncio~=0.14.0 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -r actions/requirements-actions.txt 2 | 3 | rasa[spacy]==3.1.0 4 | 5 | # Sync with `Dockerfile` 6 | rasa-sdk==3.1.0 7 | -------------------------------------------------------------------------------- /run_rasa_action_server_with_ide.py: -------------------------------------------------------------------------------- 1 | """This script allows use of an IDE (Wing, Pycharm, ...) to debug custom actions: 2 | 3 | (-) Place this script in same location as your actions.py 4 | (-) Open & run it from within your IDE 5 | (-) Now you can put breakpoints in your actions.py, but also explore the internals of the 6 | rasa_sdk action server itself. 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | # insert path of this script in syspath so actions.py will be found 13 | sys.path.insert(1, os.path.dirname(os.path.abspath(__file__))) 14 | 15 | # 16 | # This is exactly like issuing the command: 17 | # $ rasa run actions 18 | # 19 | sys.argv.append('run') 20 | sys.argv.append('actions') 21 | sys.argv.append('--actions') 22 | sys.argv.append('actions') 23 | sys.argv.append('--port') 24 | sys.argv.append('5056') 25 | sys.argv.append('--debug') 26 | 27 | if __name__ == '__main__': 28 | from rasa.__main__ import main 29 | 30 | main() 31 | -------------------------------------------------------------------------------- /run_rasa_shell_with_ide.py: -------------------------------------------------------------------------------- 1 | """This script allows use of an IDE (Wing, Pycharm, ...) to run the rasa shell: 2 | 3 | (-) Place this script in root of Rasa bot project 4 | (-) Open & run it from within your IDE 5 | 6 | (-) In Wing, use External Console for better experience. 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | # insert path of this script in syspath so custom modules will be found 13 | sys.path.insert(1, os.path.dirname(os.path.abspath(__file__))) 14 | 15 | # 16 | # This is exactly like issuing the command: 17 | # $ rasa shell --debug 18 | # 19 | 20 | # 21 | sys.argv.append("shell") 22 | sys.argv.append("--enable-api") 23 | sys.argv.append("--debug") 24 | 25 | if __name__ == "__main__": 26 | from rasa.__main__ import main 27 | 28 | main() 29 | -------------------------------------------------------------------------------- /run_rasa_test_with_ide.py: -------------------------------------------------------------------------------- 1 | """This script allows use of an IDE (Wing, Pycharm, ...) to run the rasa shell: 2 | 3 | (-) Place this script in root of Rasa bot project 4 | (-) Open & run it from within your IDE 5 | 6 | (-) In Wing, use External Console for better experience. 7 | """ 8 | 9 | import os 10 | import sys 11 | 12 | # insert path of this script in syspath so custom modules will be found 13 | sys.path.insert(1, os.path.dirname(os.path.abspath(__file__))) 14 | 15 | # 16 | # This is exactly like issuing the command: 17 | # $ rasa shell --debug 18 | # 19 | 20 | # 21 | sys.argv.append("shell") 22 | sys.argv.append("--enable-api") 23 | sys.argv.append("--debug") 24 | 25 | if __name__ == "__main__": 26 | from rasa.__main__ import main 27 | 28 | main() 29 | -------------------------------------------------------------------------------- /scripts/delete_all_test_clusters.py: -------------------------------------------------------------------------------- 1 | """Deletes all EKS test clusters prefixed with `financial-demo-`""" 2 | import sys 3 | import subprocess 4 | import json 5 | import pprint 6 | from pathlib import Path 7 | 8 | cwd = Path(__file__).parent.parent 9 | print("--\ncwd={cwd}") 10 | 11 | ################################### 12 | print("--\nGet the EKS test clusters for financial-demo") 13 | cmd = [ 14 | "eksctl", 15 | "get", 16 | "cluster", 17 | "--output", 18 | "json", 19 | ] 20 | 21 | try: 22 | cp = subprocess.run(cmd, check=True, capture_output=True) 23 | 24 | except subprocess.CalledProcessError as e: 25 | print(e.stderr.decode("utf-8")) 26 | sys.exit(e.returncode) 27 | 28 | # all clusters 29 | clusters = [x["metadata"]["name"] for x in json.loads(cp.stdout.decode("utf-8"))] 30 | 31 | # financial-demo test clusters 32 | clusters = [ 33 | c 34 | for c in clusters 35 | if c.startswith("financial-demo-") and c != "financial-demo-production" 36 | ] 37 | 38 | print(f"Found {len(clusters)} financial-demo test clusters") 39 | if len(clusters) > 0: 40 | pprint.pprint(clusters) 41 | 42 | ################################### 43 | for cluster in clusters: 44 | print(f"--\nCleaning up EKS test cluster: {cluster}") 45 | 46 | try: 47 | # Note: see http://www.gnu.org/software/automake/manual/html_node/Tricks-For-Silencing-Make.html 48 | print(f"----\nSet kubeconfig to look at this cluster") 49 | cmd = [ 50 | "make", 51 | "--no-print-directory", 52 | "aws-eks-cluster-update-kubeconfig", 53 | f"AWS_EKS_CLUSTER_NAME={cluster}", 54 | ] 55 | subprocess.run(cmd, check=True, capture_output=False, cwd=cwd) 56 | 57 | print(f"----\nUninstall rasa enterprise") 58 | cmd = ["make", "--no-print-directory", "rasa-enterprise-uninstall"] 59 | subprocess.run(cmd, check=True, capture_output=False, cwd=cwd) 60 | 61 | print(f"----\nDelete Storage (PVCs)") 62 | cmd = ["make", "--no-print-directory", "rasa-enterprise-delete-pvc-all"] 63 | subprocess.run(cmd, check=True, capture_output=False, cwd=cwd) 64 | 65 | print(f"----\nDelete the EKS cluster") 66 | cmd = [ 67 | "make", 68 | "--no-print-directory", 69 | "aws-eks-cluster-delete", 70 | f"AWS_EKS_CLUSTER_NAME={cluster}", 71 | ] 72 | subprocess.run(cmd, check=True, capture_output=False, cwd=cwd) 73 | 74 | except subprocess.CalledProcessError as e: 75 | print(e.stderr.decode("utf-8")) 76 | sys.exit(e.returncode) 77 | -------------------------------------------------------------------------------- /scripts/patch_gcr_auth_json.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | with open('deploy/gcr-auth.json', 'r') as f: 4 | lines = f.readlines() 5 | for line in lines: 6 | print( 7 | line.replace( 8 | "set_as_environment_variable-GCR_AUTH_JSON_PRIVATE_KEY_ID", 9 | os.environ["GCR_AUTH_JSON_PRIVATE_KEY_ID"], 10 | ) 11 | .replace( 12 | "set_as_environment_variable-GCR_AUTH_JSON_PRIVATE_KEY", 13 | os.environ["GCR_AUTH_JSON_PRIVATE_KEY"], 14 | ) 15 | .replace( 16 | "set_as_environment_variable-GCR_AUTH_JSON_CLIENT_EMAIL", 17 | os.environ["GCR_AUTH_JSON_CLIENT_EMAIL"], 18 | ) 19 | .replace( 20 | "set_as_environment_variable-GCR_AUTH_JSON_CLIENT_ID", 21 | os.environ["GCR_AUTH_JSON_CLIENT_ID"], 22 | ), 23 | end='', 24 | ) 25 | -------------------------------------------------------------------------------- /scripts/smoketest.py: -------------------------------------------------------------------------------- 1 | """Performs Rasa Enterprise smoke tests 2 | 3 | Rasa X HTTP API: https://rasa.com/docs/rasa-x/pages/http-api 4 | 5 | Rasa HTTP API: https://rasa.com/docs/rasa/pages/http-api 6 | (Use BASE_URL/core/... to reach rasa-production server directly) 7 | """ 8 | from typing import Any 9 | import os 10 | import pprint 11 | 12 | from requests import request 13 | 14 | BASE_URL = os.environ.get("BASE_URL") 15 | USERNAME = os.environ.get("RASAX_INITIALUSER_USERNAME") 16 | PASSWORD = os.environ.get("RASAX_INITIALUSER_PASSWORD") 17 | VERBOSE = int(os.environ.get("VERBOSE", 1)) 18 | 19 | 20 | def my_print(msg: Any, status_code: int = None) -> None: 21 | """Pretty print msg""" 22 | if VERBOSE > 0: 23 | if status_code: 24 | print(f"status_code: {status_code}") 25 | 26 | if isinstance(msg, (list, dict)): 27 | pprint.pprint(msg) 28 | else: 29 | print(msg) 30 | 31 | 32 | quit = False 33 | if not BASE_URL: 34 | my_print("Please set environment variable: BASE_URL") 35 | quit = True 36 | if not USERNAME: 37 | my_print("Please set environment variable: RASAX_INITIALUSER_USERNAME") 38 | quit = True 39 | if not PASSWORD: 40 | my_print("Please set environment variable: RASAX_INITIALUSER_PASSWORD") 41 | quit = True 42 | if quit: 43 | exit(1) 44 | 45 | ################################### 46 | my_print("--\nBASE_URL") 47 | my_print(BASE_URL) 48 | 49 | ################################### 50 | my_print("--\nRasa X Health check") 51 | 52 | url = f"{BASE_URL}/api/health" 53 | r = request("GET", url) 54 | if r.status_code != 200: 55 | my_print(r.json(), r.status_code) 56 | exit(1) 57 | 58 | status_rasa_production = r.json().get("production", {}).get("status") 59 | status_rasa_worker = r.json().get("worker", {}).get("status") 60 | 61 | my_print(r.json(), r.status_code) 62 | 63 | if status_rasa_production != 200: 64 | my_print(f'rasa-production not OK!\nstatus = {status_rasa_production}') 65 | exit(1) 66 | 67 | if status_rasa_worker != 200: 68 | my_print(f'rasa-worker not OK!\nstatus = {status_rasa_worker}') 69 | exit(1) 70 | 71 | 72 | ################################### 73 | my_print("--\nGet access_token") 74 | 75 | url = f"{BASE_URL}/api/auth" 76 | payload = {"username": USERNAME, "password": PASSWORD} 77 | headers = {"Content-Type": "application/json"} 78 | r = request("POST", url, json=payload, headers=headers) 79 | if r.status_code != 200: 80 | my_print(r.json(), r.status_code) 81 | exit(1) 82 | 83 | access_token = r.json().get("access_token") 84 | 85 | if not access_token: 86 | my_print("access_token is empty???") 87 | exit(1) 88 | 89 | ################################### 90 | my_print("--\nCheck tagged production model") 91 | 92 | url = f"{BASE_URL}/api/projects/default/models" 93 | payload = {} 94 | headers = {"Authorization": f"Bearer {access_token}"} 95 | params = {'tag': 'production'} 96 | r = request("GET", url, json=payload, headers=headers, params=params) 97 | if r.status_code != 200: 98 | my_print(r.json(), r.status_code) 99 | exit(1) 100 | 101 | production_model = r.json()[0].get("model") 102 | my_print(f"Found production model: {production_model}") 103 | 104 | ################################### 105 | my_print("--\nSay hi") 106 | 107 | url = f"{BASE_URL}/api/chat" 108 | payload = {"message": "hi"} 109 | headers = {"Authorization": f"Bearer {access_token}"} 110 | params = {} 111 | r = request("POST", url, json=payload, headers=headers, params=params) 112 | if r.status_code != 200 or r.json() == []: 113 | my_print(r.json(), r.status_code) 114 | if r.status_code == 200 and r.json() == []: 115 | print( 116 | "Bot responded, but the response is empty.\n" 117 | "Probably the model is not loaded yet by rasa-prod container.\n" 118 | "Confirm by running `make rasa-enterprise-smoketest locally`.\n" 119 | "If so, increase sleep time in pipeline after tagging of production model." 120 | ) 121 | exit(1) 122 | 123 | my_print(r.json(), r.status_code) 124 | 125 | ################################### 126 | my_print("--\nSmoke tests passed") 127 | exit(0) 128 | -------------------------------------------------------------------------------- /scripts/wait_for_external_ip.sh: -------------------------------------------------------------------------------- 1 | AWS_EKS_NAMEPACE=${1:-"my-namespace"} 2 | AWS_EKS_RELEASE_NAME=${2:-"my-release"} 3 | ATTEMPTS=${3:-100} 4 | COUNT=1; 5 | IP="" 6 | while [ -z $IP ]; do 7 | IP=$(kubectl --namespace ${AWS_EKS_NAMEPACE} get service ${AWS_EKS_RELEASE_NAME}-rasa-x-nginx --output jsonpath='{.status.loadBalancer.ingress[0].hostname}') 8 | if [[ $COUNT -eq $ATTEMPTS ]]; then 9 | echo "# Limit of $ATTEMPTS attempts has exceeded." 10 | exit 1 11 | fi 12 | if [[ -z "$IP" ]]; then 13 | echo -e "$(( COUNT++ ))... \c" 14 | sleep 2 15 | fi 16 | done 17 | echo 'Found External IP' 18 | echo 'Login at: http://'$IP':80/login' 19 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 88 3 | # See these resources for descriptions of the error codes: 4 | # E***, F***, W*** : https://flake8.pycqa.org/en/latest/user/error-codes.html 5 | # D***: http://www.pydocstyle.org/en/stable/error_codes.html 6 | ignore = W503, E121, E126, E211, E225, E501, E203, E402, F401, F811, 7 | D100, # ignore missing docstrings in public module 8 | D104, # ignore missing docstrings in public package 9 | docstring-convention = google -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RasaHQ/financial-demo/7627cebef1571d55855b886cf4ac20459af9566c/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | import pytest 4 | import json 5 | import sqlalchemy as sa 6 | 7 | from rasa_sdk.executor import CollectingDispatcher 8 | from rasa_sdk import Tracker 9 | 10 | here = Path(__file__).parent.resolve() 11 | 12 | EMPTY_TRACKER = Tracker.from_dict(json.load(open(here / "./data/empty_tracker.json"))) 13 | PAY_CC_NOT_CONFIRMED = Tracker.from_dict( 14 | json.load(open(here / "./data/pay_cc_not_confirmed.json")) 15 | ) 16 | PAY_CC_CONFIRMED = Tracker.from_dict( 17 | json.load(open(here / "./data/pay_cc_confirmed.json")) 18 | ) 19 | DATABASE_URL = os.environ.setdefault("DATABASE_URL", "postgresql:///postgres") 20 | 21 | 22 | @pytest.fixture 23 | def dispatcher(): 24 | return CollectingDispatcher() 25 | 26 | 27 | @pytest.fixture 28 | def domain(): 29 | return dict() 30 | 31 | 32 | # @pytest.fixture 33 | # def session(): 34 | # engine = sa.create_engine(DATABASE_URL) 35 | # db_session = sessionmaker(bind=engine)() 36 | 37 | # try: 38 | # yield db_session 39 | # finally: 40 | # db_session.close() 41 | # Base.metadata.drop_all(engine) 42 | -------------------------------------------------------------------------------- /tests/data/all_slots_filled_tracker.json: -------------------------------------------------------------------------------- 1 | { 2 | "sender_id": "someone", 3 | "slots": { 4 | "AA_CONTINUE_FORM": null, 5 | "PERSON": null, 6 | "account_type": null, 7 | "amount-of-money": null, 8 | "amount_transferred":null, 9 | "credit_card": null, 10 | "currency": null, 11 | "end_time": null, 12 | "end_time_formatted": null, 13 | "grain": null, 14 | "handoff_to": null, 15 | "next_form_name": null, 16 | "number": null, 17 | "payment_amount_type": null, 18 | "previous_form_name": null, 19 | "repeated_validation_failures": null, 20 | "requested_slot": null, 21 | "search_type": null, 22 | "start_time": null, 23 | "start_time_formatted": null, 24 | "time": null, 25 | "time_formatted": null, 26 | "vendor_name": null, 27 | "zz_confirm_form": null 28 | }, 29 | "latest_message": { 30 | "intent": {}, 31 | "entities": [], 32 | "text": null, 33 | "message_id": null, 34 | "metadata": {} 35 | }, 36 | "latest_event_time": 1607396994.449022, 37 | "followup_action": null, 38 | "paused": false, 39 | "events": [ 40 | { 41 | "event": "action", 42 | "timestamp": 1607396994.448949, 43 | "name": "action_session_start", 44 | "policy": null, 45 | "confidence": 1.0 46 | }, 47 | { 48 | "event": "session_started", 49 | "timestamp": 1607396994.44901 50 | }, 51 | { 52 | "event": "action", 53 | "timestamp": 1607396994.449022, 54 | "name": "action_listen", 55 | "policy": null, 56 | "confidence": null 57 | } 58 | ], 59 | "latest_input_channel": null, 60 | "active_loop": {}, 61 | "latest_action": { 62 | "action_name": "action_listen" 63 | }, 64 | "latest_action_name": "action_listen" 65 | } 66 | -------------------------------------------------------------------------------- /tests/data/empty_tracker.json: -------------------------------------------------------------------------------- 1 | { 2 | "sender_id": "someone", 3 | "slots": { 4 | "AA_CONTINUE_FORM": null, 5 | "PERSON": null, 6 | "account_type": null, 7 | "amount-of-money": null, 8 | "amount_transferred":null, 9 | "credit_card": null, 10 | "currency": null, 11 | "end_time": null, 12 | "end_time_formatted": null, 13 | "grain": null, 14 | "handoff_to": null, 15 | "next_form_name": null, 16 | "number": null, 17 | "payment_amount_type": null, 18 | "previous_form_name": null, 19 | "repeated_validation_failures": null, 20 | "requested_slot": null, 21 | "search_type": null, 22 | "start_time": null, 23 | "start_time_formatted": null, 24 | "time": null, 25 | "time_formatted": null, 26 | "vendor_name": null, 27 | "zz_confirm_form": null 28 | }, 29 | "latest_message": { 30 | "intent": {}, 31 | "entities": [], 32 | "text": null, 33 | "message_id": null, 34 | "metadata": {} 35 | }, 36 | "latest_event_time": 1607396994.449022, 37 | "followup_action": null, 38 | "paused": false, 39 | "events": [ 40 | { 41 | "event": "action", 42 | "timestamp": 1607396994.448949, 43 | "name": "action_session_start", 44 | "policy": null, 45 | "confidence": 1.0 46 | }, 47 | { 48 | "event": "session_started", 49 | "timestamp": 1607396994.44901 50 | }, 51 | { 52 | "event": "action", 53 | "timestamp": 1607396994.449022, 54 | "name": "action_listen", 55 | "policy": null, 56 | "confidence": null 57 | } 58 | ], 59 | "latest_input_channel": null, 60 | "active_loop": {}, 61 | "latest_action": { 62 | "action_name": "action_listen" 63 | }, 64 | "latest_action_name": "action_listen" 65 | } 66 | -------------------------------------------------------------------------------- /tests/data/pay_cc_confirmed.json: -------------------------------------------------------------------------------- 1 | { 2 | "sender_id": "someone", 3 | "slots": { 4 | "zz_confirm_form": "yes", 5 | "credit_card": "justice bank", 6 | "amount-of-money": "301.33", 7 | "amount_transferred": "301.32" 8 | }, 9 | "latest_message": { 10 | "intent": {}, 11 | "entities": [], 12 | "text": null, 13 | "message_id": null, 14 | "metadata": {} 15 | }, 16 | "latest_event_time": 1607396994.449022, 17 | "followup_action": null, 18 | "paused": false, 19 | "events": [ 20 | { 21 | "event": "action", 22 | "timestamp": 1607396994.448949, 23 | "name": "action_session_start", 24 | "policy": null, 25 | "confidence": 1.0 26 | }, 27 | { 28 | "event": "session_started", 29 | "timestamp": 1607396994.44901 30 | }, 31 | { 32 | "event": "action", 33 | "timestamp": 1607396994.449022, 34 | "name": "action_listen", 35 | "policy": null, 36 | "confidence": null 37 | } 38 | ], 39 | "latest_input_channel": null, 40 | "active_loop": {}, 41 | "latest_action": { 42 | "action_name": "action_listen" 43 | }, 44 | "latest_action_name": "action_listen" 45 | } 46 | -------------------------------------------------------------------------------- /tests/data/pay_cc_not_confirmed.json: -------------------------------------------------------------------------------- 1 | { 2 | "sender_id": "someone", 3 | "slots": { 4 | "zz_confirm_form": "no" 5 | }, 6 | "latest_message": { 7 | "intent": {}, 8 | "entities": [], 9 | "text": null, 10 | "message_id": null, 11 | "metadata": {} 12 | }, 13 | "latest_event_time": 1607396994.449022, 14 | "followup_action": null, 15 | "paused": false, 16 | "events": [ 17 | { 18 | "event": "action", 19 | "timestamp": 1607396994.448949, 20 | "name": "action_session_start", 21 | "policy": null, 22 | "confidence": 1.0 23 | }, 24 | { 25 | "event": "session_started", 26 | "timestamp": 1607396994.44901 27 | }, 28 | { 29 | "event": "action", 30 | "timestamp": 1607396994.449022, 31 | "name": "action_listen", 32 | "policy": null, 33 | "confidence": null 34 | } 35 | ], 36 | "latest_input_channel": null, 37 | "active_loop": {}, 38 | "latest_action": { 39 | "action_name": "action_listen" 40 | }, 41 | "latest_action_name": "action_listen" 42 | } 43 | -------------------------------------------------------------------------------- /tests/db_test.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlalchemy as sa 3 | import pytest 4 | 5 | from actions.profile_db import ( 6 | GENERAL_ACCOUNTS, 7 | create_database, 8 | ProfileDB, 9 | Account, 10 | ) 11 | 12 | PROFILE_DB_NAME = os.environ.get("PROFILE_DB_NAME", "profile") 13 | PROFILE_DB_URL = os.environ.get("PROFILE_DB_URL", f"sqlite:///{PROFILE_DB_NAME}.db") 14 | ENGINE = sa.create_engine(PROFILE_DB_URL) 15 | create_database(ENGINE, PROFILE_DB_NAME) 16 | 17 | profile_db = ProfileDB(ENGINE) 18 | profile_db.create_tables() 19 | 20 | session_id = "test" 21 | profile_db.populate_profile_db(session_id) 22 | currency = profile_db.get_currency(session_id) 23 | 24 | account = profile_db.get_account_from_session_id(session_id) 25 | account_number = profile_db.get_account_number(account) 26 | account_from_number = profile_db.get_account_from_number(account_number) 27 | 28 | recipient_names = profile_db.list_known_recipients(session_id) 29 | recipient_name = recipient_names[0] 30 | recipient = profile_db.get_recipient_from_name(session_id, recipient_name) 31 | recipient_account_number = profile_db.get_account_number(recipient) 32 | 33 | account_balance = profile_db.get_account_balance(session_id) 34 | profile_db.transact(account_number, recipient_account_number, 100) 35 | account_balance_now = profile_db.get_account_balance(session_id) 36 | 37 | credit_cards = profile_db.list_credit_cards(session_id) 38 | balance_types = profile_db.list_balance_types() 39 | 40 | 41 | def test_profile_initialization(): 42 | assert profile_db.check_session_id_exists(session_id) 43 | assert profile_db.check_general_accounts_populated(GENERAL_ACCOUNTS) 44 | assert profile_db.get_currency(session_id) == "$" 45 | 46 | 47 | def test_account_from_account_number(): 48 | assert account is account_from_number 49 | 50 | 51 | def test_transact(): 52 | assert account_balance_now == account_balance - 100 53 | 54 | 55 | def test_cc_payment_1(): 56 | credit_card_name = credit_cards[0] 57 | balance_type = balance_types[0] 58 | 59 | credit_card_balance = profile_db.get_credit_card_balance( 60 | session_id, credit_card_name, balance_type 61 | ) 62 | profile_db.pay_off_credit_card(session_id, credit_card_name, credit_card_balance) 63 | credit_card_balance_now = profile_db.get_credit_card_balance( 64 | session_id, credit_card_name, balance_type 65 | ) 66 | assert credit_card_balance_now == 0 67 | 68 | 69 | def test_cc_payment_2(): 70 | credit_card_name = credit_cards[1] 71 | balance_type = balance_types[1] 72 | 73 | credit_card_balance = profile_db.get_credit_card_balance( 74 | session_id, credit_card_name, balance_type 75 | ) 76 | profile_db.pay_off_credit_card(session_id, credit_card_name, credit_card_balance) 77 | credit_card_balance_now = profile_db.get_credit_card_balance( 78 | session_id, credit_card_name, balance_type 79 | ) 80 | assert credit_card_balance_now == 0 81 | -------------------------------------------------------------------------------- /tests/test_actions.py: -------------------------------------------------------------------------------- 1 | import json 2 | import pytest 3 | 4 | from rasa_sdk.executor import CollectingDispatcher, Tracker 5 | from rasa_sdk.events import SlotSet, ActionExecuted, SessionStarted 6 | 7 | from tests.conftest import EMPTY_TRACKER, PAY_CC_CONFIRMED, PAY_CC_NOT_CONFIRMED 8 | from actions import actions 9 | 10 | 11 | @pytest.mark.asyncio 12 | async def test_run_action_session_start(dispatcher, domain): 13 | tracker = EMPTY_TRACKER 14 | action = actions.ActionSessionStart() 15 | events = await action.run(dispatcher, tracker, domain) 16 | expected_events = [ 17 | SessionStarted(), 18 | SlotSet("currency", "$"), 19 | ActionExecuted("action_listen"), 20 | ] 21 | assert events == expected_events 22 | 23 | 24 | async def test_action_pay_cc_not_confirmed(dispatcher, domain): 25 | tracker = PAY_CC_NOT_CONFIRMED 26 | action = actions.ActionPayCC() 27 | events = await action.run(dispatcher, tracker, domain) 28 | expected_events = [ 29 | SlotSet("AA_CONTINUE_FORM", None), 30 | SlotSet("zz_confirm_form", None), 31 | SlotSet("credit_card", None), 32 | SlotSet("account_type", None), 33 | SlotSet("amount-of-money", None), 34 | SlotSet("time", None), 35 | SlotSet("time_formatted", None), 36 | SlotSet("start_time", None), 37 | SlotSet("end_time", None), 38 | SlotSet("start_time_formatted", None), 39 | SlotSet("end_time_formatted", None), 40 | SlotSet("grain", None), 41 | SlotSet("number", None), 42 | ] 43 | expected_response = "utter_cc_pay_cancelled" 44 | assert events == expected_events 45 | assert dispatcher.messages[0]["response"] == expected_response 46 | 47 | 48 | async def test_action_pay_cc_confirmed(dispatcher, domain): 49 | tracker = EMPTY_TRACKER 50 | action = actions.ActionSessionStart() 51 | await action.run(dispatcher, tracker, domain) 52 | tracker = PAY_CC_CONFIRMED 53 | action = actions.ActionPayCC() 54 | events = await action.run(dispatcher, tracker, domain) 55 | expected_events = [ 56 | SlotSet("AA_CONTINUE_FORM", None), 57 | SlotSet("zz_confirm_form", None), 58 | SlotSet("credit_card", None), 59 | SlotSet("account_type", None), 60 | SlotSet("amount-of-money", None), 61 | SlotSet("time", None), 62 | SlotSet("time_formatted", None), 63 | SlotSet("start_time", None), 64 | SlotSet("end_time", None), 65 | SlotSet("start_time_formatted", None), 66 | SlotSet("end_time_formatted", None), 67 | SlotSet("grain", None), 68 | SlotSet("number", None), 69 | SlotSet("amount_transferred", value=602.65), 70 | ] 71 | expected_response = "utter_cc_pay_scheduled" 72 | assert events == expected_events 73 | assert dispatcher.messages[0]["response"] == expected_response 74 | -------------------------------------------------------------------------------- /tests/test_stories.yml: -------------------------------------------------------------------------------- 1 | version: "3.1" 2 | stories: 3 | - story: greet/bye path 4 | steps: 5 | - intent: greet 6 | user: |- 7 | hi 8 | - action: utter_greet 9 | - action: utter_help 10 | - intent: goodbye 11 | user: |- 12 | bye 13 | - action: utter_goodbye 14 | - story: say goodbye 15 | steps: 16 | - intent: goodbye 17 | user: |- 18 | bye 19 | - action: utter_goodbye 20 | - story: help 21 | steps: 22 | - intent: help 23 | user: |- 24 | how do I use this 25 | - action: utter_help 26 | - story: out of scope 27 | steps: 28 | - intent: out_of_scope 29 | user: |- 30 | I want to order a pizza 31 | - action: utter_out_of_scope 32 | - story: is there a transfer charge 33 | steps: 34 | - intent: ask_transfer_charge 35 | user: |- 36 | is there a transfer charge 37 | - action: action_show_transfer_charge 38 | - story: Show list of known recipients 39 | steps: 40 | - intent: check_recipients 41 | user: |- 42 | Who can I send money to? 43 | - action: action_show_recipients 44 | - story: Show bank account balance 45 | steps: 46 | - intent: check_balance 47 | user: |- 48 | How much money is on my account? 49 | - action: action_show_balance 50 | - story: Show specific credit card account balance 51 | steps: 52 | - intent: check_balance 53 | user: |- 54 | What's my [emblem](credit_card) [card]{"entity": "account_type", "value": "credit"} [balance]{"entity": "amount-of-money", "value": "current balance"} 55 | - action: action_show_balance 56 | - story: Show all credit cards account balances 57 | steps: 58 | - intent: check_balance 59 | user: |- 60 | What are my [credit](account_type) accounts 61 | - action: action_show_balance 62 | - story: check list of known recipients 63 | steps: 64 | - intent: check_recipients 65 | user: |- 66 | Show me my list of recipients 67 | - action: action_show_recipients 68 | - story: from Rasa X 56efe3eaf7c0456db86a6669421f885c 69 | steps: 70 | - intent: help 71 | user: |- 72 | help 73 | - action: utter_help 74 | - intent: greet 75 | user: |- 76 | hello 77 | - action: utter_greet 78 | - action: utter_help 79 | - intent: thankyou 80 | user: |- 81 | Thanks! 82 | - action: utter_noworries 83 | - story: from Rasa X 56efe3eaf7c0456db86a6669421f885c (if intent were right) 84 | steps: 85 | - intent: help 86 | user: |- 87 | help 88 | - action: utter_help 89 | - intent: greet 90 | user: |- 91 | hello 92 | - action: utter_greet 93 | - action: utter_help 94 | - intent: affirm 95 | user: |- 96 | That sounds good 97 | - action: utter_ok 98 | - story: pay credit card happy path 99 | steps: 100 | - intent: pay_cc 101 | user: |- 102 | i want to pay off my [emblm]{"entity": "credit_card", "value": "emblem"} 103 | - action: cc_payment_form 104 | - active_loop: cc_payment_form 105 | - active_loop: null 106 | - action: action_pay_cc 107 | - story: transfer money happy path w/o recipient 108 | steps: 109 | - intent: transfer_money 110 | user: |- 111 | i want to transfer money 112 | - action: transfer_money_form 113 | - active_loop: transfer_money_form 114 | - active_loop: null 115 | - action: action_transfer_money 116 | - story: transfer money happy path w/ recipient 117 | steps: 118 | - intent: transfer_money 119 | user: |- 120 | i want to transfer money to [Latisha](PERSON) 121 | - slot_was_set: 122 | - PERSON: Latisha 123 | - action: transfer_money_form 124 | - active_loop: transfer_money_form 125 | - active_loop: null 126 | - action: action_transfer_money 127 | - story: check earnings happy path 128 | steps: 129 | - intent: check_earnings 130 | user: |- 131 | how much did I earn [in January 2019]{"entity": "time", "value": "2019-01-01T00:00:00.000-08:00"} 132 | - action: transaction_search_form 133 | - active_loop: transaction_search_form 134 | - active_loop: null 135 | - action: action_transaction_search 136 | - story: search transactions happy path 137 | steps: 138 | - intent: search_transactions 139 | user: |- 140 | I want to search my spending history' 141 | - action: transaction_search_form 142 | - active_loop: transaction_search_form 143 | - active_loop: null 144 | - action: action_transaction_search 145 | - story: greet + search transactions happy path + thankyou 146 | steps: 147 | - intent: greet 148 | user: |- 149 | hi 150 | - action: utter_greet 151 | - action: utter_help 152 | - intent: search_transactions 153 | user: |- 154 | I want to search my spending history' 155 | - action: transaction_search_form 156 | - active_loop: transaction_search_form 157 | - active_loop: null 158 | - action: action_transaction_search 159 | - intent: thankyou 160 | user: |- 161 | Thanks! 162 | - action: utter_noworries 163 | - story: Show bank account balance + transfer money 164 | steps: 165 | - intent: check_balance 166 | user: |- 167 | How much money is on my account? 168 | - action: action_show_balance 169 | - intent: transfer_money 170 | user: |- 171 | i want to transfer money 172 | - action: transfer_money_form 173 | - active_loop: transfer_money_form 174 | - active_loop: null 175 | - action: action_transfer_money 176 | - story: Show bank account balance + thankyou + transfer money 177 | steps: 178 | - intent: check_balance 179 | user: |- 180 | How much money is on my account? 181 | - action: action_show_balance 182 | - intent: thankyou 183 | user: |- 184 | Thanks! 185 | - action: utter_noworries 186 | - intent: transfer_money 187 | user: |- 188 | i want to transfer money 189 | - action: transfer_money_form 190 | - active_loop: transfer_money_form 191 | - active_loop: null 192 | - action: action_transfer_money 193 | - story: Show bank account balance + ok + transfer money 194 | steps: 195 | - intent: check_balance 196 | user: |- 197 | How much money is on my account? 198 | - action: action_show_balance 199 | - intent: affirm 200 | user: |- 201 | Ok 202 | - action: utter_ok 203 | - intent: transfer_money 204 | user: |- 205 | i want to transfer money 206 | - action: transfer_money_form 207 | - active_loop: transfer_money_form 208 | - active_loop: null 209 | - action: action_transfer_money 210 | - story: Show bank account balance + ok + show recipients + ok + show transfer charge + ok + transfer money 211 | steps: 212 | - intent: check_balance 213 | user: |- 214 | How much money is on my account? 215 | - action: action_show_balance 216 | - intent: affirm 217 | user: |- 218 | Ok 219 | - action: utter_ok 220 | - intent: check_recipients 221 | user: |- 222 | Who can I send money to? 223 | - action: action_show_recipients 224 | - intent: affirm 225 | user: |- 226 | Ok 227 | - action: utter_ok 228 | - intent: ask_transfer_charge 229 | user: |- 230 | is there a transfer charge 231 | - action: action_show_transfer_charge 232 | - intent: affirm 233 | user: |- 234 | Ok 235 | - action: utter_ok 236 | - intent: transfer_money 237 | user: |- 238 | i want to transfer money 239 | - action: transfer_money_form 240 | - active_loop: transfer_money_form 241 | - active_loop: null 242 | - action: action_transfer_money 243 | - story: Show bank account balance + ok + pay credit card 244 | steps: 245 | - intent: check_balance 246 | user: |- 247 | How much money is on my account? 248 | - action: action_show_balance 249 | - intent: affirm 250 | user: |- 251 | Ok 252 | - action: utter_ok 253 | - intent: pay_cc 254 | user: |- 255 | i want to pay off my [emblm]{"entity": "credit_card", "value": "emblem"} 256 | - action: cc_payment_form 257 | - active_loop: cc_payment_form 258 | - active_loop: null 259 | - action: action_pay_cc 260 | - story: pay credit card and afterwards check account balance 261 | steps: 262 | - intent: pay_cc 263 | user: |- 264 | i want to pay off my [emblm]{"entity": "credit_card", "value": "emblem"} 265 | - action: cc_payment_form 266 | - active_loop: cc_payment_form 267 | - active_loop: null 268 | - action: action_pay_cc 269 | - intent: check_balance 270 | user: |- 271 | How much money is on my account? 272 | - action: action_show_balance 273 | - story: Pay CC, while in form ask account balance 274 | steps: 275 | - intent: pay_cc 276 | user: |- 277 | pay [credit card]{"entity": "account_type", "value": "credit"} 278 | - action: cc_payment_form 279 | - active_loop: cc_payment_form 280 | - slot_was_set: 281 | - AA_CONTINUE_FORM: yes 282 | - slot_was_set: 283 | - requested_slot: credit_card 284 | - intent: check_balance 285 | user: |- 286 | How much money is on my [bank](account_type) account? 287 | - slot_was_set: 288 | - account_type: bank 289 | - action: action_show_balance 290 | - action: cc_payment_form 291 | - slot_was_set: 292 | - requested_slot: null 293 | - active_loop: null 294 | - action: action_pay_cc 295 | - story: transfer money, while in form ask transfer charge 296 | steps: 297 | - intent: transfer_money 298 | user: |- 299 | i want to transfer money 300 | - action: transfer_money_form 301 | - active_loop: transfer_money_form 302 | - intent: ask_transfer_charge 303 | user: |- 304 | is there a transfer charge 305 | - action: action_show_transfer_charge 306 | - action: transfer_money_form 307 | - slot_was_set: 308 | - requested_slot: null 309 | - active_loop: null 310 | - action: action_transfer_money 311 | - story: transfer money, while in form ask known recipients 312 | steps: 313 | - intent: transfer_money 314 | user: |- 315 | i want to transfer money 316 | - action: transfer_money_form 317 | - active_loop: transfer_money_form 318 | - intent: check_recipients 319 | user: |- 320 | Who can I send money to? 321 | - action: action_show_recipients 322 | - action: transfer_money_form 323 | - slot_was_set: 324 | - requested_slot: null 325 | - active_loop: null 326 | - action: action_transfer_money 327 | - story: transfer money, while in form ask balance 328 | steps: 329 | - intent: transfer_money 330 | user: |- 331 | i want to transfer money 332 | - action: transfer_money_form 333 | - active_loop: transfer_money_form 334 | - intent: check_balance 335 | user: |- 336 | What is my account [balance]{"entity": "amount-of-money", "value": "current balance"}? 337 | - action: action_show_balance 338 | - action: transfer_money_form 339 | - slot_was_set: 340 | - requested_slot: null 341 | - active_loop: null 342 | - action: action_transfer_money 343 | - story: transfer money, while in form ask known recipients and transfer charge 344 | steps: 345 | - intent: transfer_money 346 | user: |- 347 | i want to transfer money 348 | - action: transfer_money_form 349 | - active_loop: transfer_money_form 350 | - intent: check_recipients 351 | user: |- 352 | Who can I send money to? 353 | - action: action_show_recipients 354 | - action: transfer_money_form 355 | - intent: ask_transfer_charge 356 | user: |- 357 | is there a transfer charge 358 | - action: action_show_transfer_charge 359 | - action: transfer_money_form 360 | - slot_was_set: 361 | - requested_slot: null 362 | - active_loop: null 363 | - action: action_transfer_money 364 | - story: pay credit card + switch to transfer money, deny 365 | steps: 366 | - intent: pay_cc 367 | user: |- 368 | pay [credit card]{"entity": "account_type", "value": "credit"} 369 | - action: cc_payment_form 370 | - active_loop: cc_payment_form 371 | - intent: transfer_money 372 | user: |- 373 | transfer money 374 | - action: action_switch_forms_ask 375 | - slot_was_set: 376 | - next_form_name: transfer_money_form 377 | - intent: deny 378 | - action: action_switch_forms_deny 379 | - slot_was_set: 380 | - next_form_name: null 381 | - action: cc_payment_form 382 | - active_loop: cc_payment_form 383 | - story: pay credit card + switch to search transactions, deny 384 | steps: 385 | - intent: pay_cc 386 | user: |- 387 | pay [credit card]{"entity": "account_type", "value": "credit"} 388 | - action: cc_payment_form 389 | - active_loop: cc_payment_form 390 | - intent: check_earnings 391 | user: |- 392 | check earnings 393 | - action: action_switch_forms_ask 394 | - slot_was_set: 395 | - next_form_name: transaction_search_form 396 | - intent: deny 397 | - action: action_switch_forms_deny 398 | - slot_was_set: 399 | - next_form_name: null 400 | - action: cc_payment_form 401 | - active_loop: cc_payment_form 402 | - story: search transactions + switch to transfer money, deny 403 | steps: 404 | - intent: check_earnings 405 | user: |- 406 | check earnings 407 | - action: transaction_search_form 408 | - active_loop: transaction_search_form 409 | - intent: transfer_money 410 | user: |- 411 | transfer money 412 | - action: action_switch_forms_ask 413 | - slot_was_set: 414 | - next_form_name: transfer_money_form 415 | - intent: deny 416 | - action: action_switch_forms_deny 417 | - slot_was_set: 418 | - next_form_name: null 419 | - action: transaction_search_form 420 | - active_loop: transaction_search_form 421 | - story: search transactions + switch to pay credit card, deny 422 | steps: 423 | - intent: check_earnings 424 | user: |- 425 | check earnings 426 | - action: transaction_search_form 427 | - active_loop: transaction_search_form 428 | - intent: pay_cc 429 | user: |- 430 | pay [credit card]{"entity": "account_type", "value": "credit"} 431 | - action: action_switch_forms_ask 432 | - slot_was_set: 433 | - next_form_name: cc_payment_form 434 | - intent: deny 435 | - action: action_switch_forms_deny 436 | - slot_was_set: 437 | - next_form_name: null 438 | - action: transaction_search_form 439 | - active_loop: transaction_search_form 440 | - story: transfer money + switch to search transactions, deny 441 | steps: 442 | - intent: transfer_money 443 | user: |- 444 | transfer money 445 | - action: transfer_money_form 446 | - active_loop: transfer_money_form 447 | - intent: check_earnings 448 | user: |- 449 | check earnings 450 | - action: action_switch_forms_ask 451 | - slot_was_set: 452 | - next_form_name: transaction_search_form 453 | - intent: deny 454 | - action: action_switch_forms_deny 455 | - slot_was_set: 456 | - next_form_name: null 457 | - action: transfer_money_form 458 | - active_loop: transfer_money_form 459 | - story: transfer money + switch to pay credit card, deny 460 | steps: 461 | - intent: transfer_money 462 | user: |- 463 | transfer money 464 | - action: transfer_money_form 465 | - active_loop: transfer_money_form 466 | - intent: pay_cc 467 | user: |- 468 | pay [credit card]{"entity": "account_type", "value": "credit"} 469 | - action: action_switch_forms_ask 470 | - slot_was_set: 471 | - next_form_name: cc_payment_form 472 | - intent: deny 473 | - action: action_switch_forms_deny 474 | - slot_was_set: 475 | - next_form_name: null 476 | - action: transfer_money_form 477 | - active_loop: transfer_money_form 478 | - story: pay credit card + switch to search transactions, affirm + switch back, deny 479 | steps: 480 | - intent: pay_cc 481 | user: |- 482 | pay [credit card]{"entity": "account_type", "value": "credit"} 483 | - action: cc_payment_form 484 | - active_loop: cc_payment_form 485 | - intent: check_earnings 486 | user: |- 487 | check earnings 488 | - action: action_switch_forms_ask 489 | - slot_was_set: 490 | - next_form_name: transaction_search_form 491 | - intent: affirm 492 | - action: action_switch_forms_affirm 493 | - slot_was_set: 494 | - next_form_name: null 495 | - slot_was_set: 496 | - previous_form_name: cc_payment_form 497 | - action: transaction_search_form 498 | - active_loop: transaction_search_form 499 | - active_loop: null 500 | - slot_was_set: 501 | - requested_slot: null 502 | - action: action_transaction_search 503 | - action: action_switch_back_ask 504 | - slot_was_set: 505 | - previous_form_name: null 506 | - intent: deny 507 | - action: utter_ask_whatelse 508 | - story: pay credit card + switch to transfer money, affirm + switch back, deny 509 | steps: 510 | - intent: pay_cc 511 | user: |- 512 | pay [credit card]{"entity": "account_type", "value": "credit"} 513 | - action: cc_payment_form 514 | - active_loop: cc_payment_form 515 | - intent: transfer_money 516 | user: |- 517 | transfer money 518 | - action: action_switch_forms_ask 519 | - slot_was_set: 520 | - next_form_name: transfer_money_form 521 | - intent: affirm 522 | - action: action_switch_forms_affirm 523 | - slot_was_set: 524 | - next_form_name: null 525 | - slot_was_set: 526 | - previous_form_name: cc_payment_form 527 | - action: transfer_money_form 528 | - active_loop: transfer_money_form 529 | - active_loop: null 530 | - slot_was_set: 531 | - requested_slot: null 532 | - action: action_transfer_money 533 | - action: action_switch_back_ask 534 | - slot_was_set: 535 | - previous_form_name: null 536 | - intent: deny 537 | - action: utter_ask_whatelse 538 | - story: search transactions + switch to transfer money, affirm + switch back, deny 539 | steps: 540 | - intent: check_earnings 541 | user: |- 542 | check earnings 543 | - action: transaction_search_form 544 | - active_loop: transaction_search_form 545 | - intent: transfer_money 546 | user: |- 547 | transfer money 548 | - action: action_switch_forms_ask 549 | - slot_was_set: 550 | - next_form_name: transfer_money_form 551 | - intent: affirm 552 | user: |- 553 | Yes 554 | - action: action_switch_forms_affirm 555 | - slot_was_set: 556 | - next_form_name: null 557 | - slot_was_set: 558 | - previous_form_name: transaction_search_form 559 | - action: transfer_money_form 560 | - active_loop: transfer_money_form 561 | - active_loop: null 562 | - slot_was_set: 563 | - requested_slot: null 564 | - action: action_transfer_money 565 | - action: action_switch_back_ask 566 | - slot_was_set: 567 | - previous_form_name: null 568 | - intent: deny 569 | user: |- 570 | No 571 | - action: utter_ask_whatelse 572 | - story: search transactions + switch to pay credit card, affirm + switch back, deny 573 | steps: 574 | - intent: check_earnings 575 | user: |- 576 | check earnings 577 | - action: transaction_search_form 578 | - active_loop: transaction_search_form 579 | - intent: pay_cc 580 | user: |- 581 | pay [credit card]{"entity": "account_type", "value": "credit"} 582 | - action: action_switch_forms_ask 583 | - slot_was_set: 584 | - next_form_name: cc_payment_form 585 | - intent: affirm 586 | - action: action_switch_forms_affirm 587 | - slot_was_set: 588 | - next_form_name: null 589 | - slot_was_set: 590 | - previous_form_name: transaction_search_form 591 | - action: cc_payment_form 592 | - active_loop: cc_payment_form 593 | - active_loop: null 594 | - slot_was_set: 595 | - requested_slot: null 596 | - action: action_pay_cc 597 | - action: action_switch_back_ask 598 | - slot_was_set: 599 | - previous_form_name: null 600 | - intent: deny 601 | - action: utter_ask_whatelse 602 | - story: transfer money + switch to search transactions, affirm + switch back, deny 603 | steps: 604 | - intent: transfer_money 605 | user: |- 606 | transfer money 607 | - action: transfer_money_form 608 | - active_loop: transfer_money_form 609 | - intent: check_earnings 610 | user: |- 611 | check earnings 612 | - action: action_switch_forms_ask 613 | - slot_was_set: 614 | - next_form_name: transaction_search_form 615 | - intent: affirm 616 | - action: action_switch_forms_affirm 617 | - slot_was_set: 618 | - next_form_name: null 619 | - slot_was_set: 620 | - previous_form_name: transfer_money_form 621 | - action: transaction_search_form 622 | - active_loop: transaction_search_form 623 | - active_loop: null 624 | - slot_was_set: 625 | - requested_slot: null 626 | - action: action_transaction_search 627 | - action: action_switch_back_ask 628 | - slot_was_set: 629 | - previous_form_name: null 630 | - intent: deny 631 | - action: utter_ask_whatelse 632 | - story: transfer money + switch to pay credit card, affirm + switch back, deny 633 | steps: 634 | - intent: transfer_money 635 | user: |- 636 | transfer money 637 | - action: transfer_money_form 638 | - active_loop: transfer_money_form 639 | - intent: pay_cc 640 | user: |- 641 | pay [credit card]{"entity": "account_type", "value": "credit"} 642 | - action: action_switch_forms_ask 643 | - slot_was_set: 644 | - next_form_name: cc_payment_form 645 | - intent: affirm 646 | - action: action_switch_forms_affirm 647 | - slot_was_set: 648 | - next_form_name: null 649 | - slot_was_set: 650 | - previous_form_name: transfer_money_form 651 | - action: cc_payment_form 652 | - active_loop: cc_payment_form 653 | - active_loop: null 654 | - slot_was_set: 655 | - requested_slot: null 656 | - action: action_pay_cc 657 | - action: action_switch_back_ask 658 | - slot_was_set: 659 | - previous_form_name: null 660 | - intent: deny 661 | - action: utter_ask_whatelse 662 | - story: pay credit card + switch to search transactions, affirm + switch back, affirm 663 | steps: 664 | - intent: pay_cc 665 | user: |- 666 | pay [credit card]{"entity": "account_type", "value": "credit"} 667 | - action: cc_payment_form 668 | - active_loop: cc_payment_form 669 | - intent: check_earnings 670 | user: |- 671 | check earnings 672 | - action: action_switch_forms_ask 673 | - slot_was_set: 674 | - next_form_name: transaction_search_form 675 | - intent: affirm 676 | - action: action_switch_forms_affirm 677 | - slot_was_set: 678 | - next_form_name: null 679 | - slot_was_set: 680 | - previous_form_name: cc_payment_form 681 | - action: transaction_search_form 682 | - active_loop: transaction_search_form 683 | - active_loop: null 684 | - slot_was_set: 685 | - requested_slot: null 686 | - action: action_transaction_search 687 | - action: action_switch_back_ask 688 | - slot_was_set: 689 | - previous_form_name: null 690 | - intent: affirm 691 | - action: cc_payment_form 692 | - active_loop: cc_payment_form 693 | - story: pay credit card + switch to transfer money, affirm + switch back, affirm 694 | steps: 695 | - intent: pay_cc 696 | user: |- 697 | pay [credit card]{"entity": "account_type", "value": "credit"} 698 | - action: cc_payment_form 699 | - active_loop: cc_payment_form 700 | - intent: transfer_money 701 | user: |- 702 | transfer money 703 | - action: action_switch_forms_ask 704 | - slot_was_set: 705 | - next_form_name: transfer_money_form 706 | - intent: affirm 707 | - action: action_switch_forms_affirm 708 | - slot_was_set: 709 | - next_form_name: null 710 | - slot_was_set: 711 | - previous_form_name: cc_payment_form 712 | - action: transfer_money_form 713 | - active_loop: transfer_money_form 714 | - active_loop: null 715 | - slot_was_set: 716 | - requested_slot: null 717 | - action: action_transfer_money 718 | - action: action_switch_back_ask 719 | - slot_was_set: 720 | - previous_form_name: null 721 | - intent: affirm 722 | - action: cc_payment_form 723 | - active_loop: cc_payment_form 724 | - story: search transactions + switch to transfer money, affirm + switch back, affirm 725 | steps: 726 | - intent: check_earnings 727 | user: |- 728 | check earnings 729 | - action: transaction_search_form 730 | - active_loop: transaction_search_form 731 | - intent: transfer_money 732 | user: |- 733 | transfer money 734 | - action: action_switch_forms_ask 735 | - slot_was_set: 736 | - next_form_name: transfer_money_form 737 | - intent: affirm 738 | user: |- 739 | Yes 740 | - action: action_switch_forms_affirm 741 | - slot_was_set: 742 | - next_form_name: null 743 | - slot_was_set: 744 | - previous_form_name: transaction_search_form 745 | - action: transfer_money_form 746 | - active_loop: transfer_money_form 747 | - active_loop: null 748 | - slot_was_set: 749 | - requested_slot: null 750 | - action: action_transfer_money 751 | - action: action_switch_back_ask 752 | - slot_was_set: 753 | - previous_form_name: null 754 | - intent: affirm 755 | user: |- 756 | Yes 757 | - action: transaction_search_form 758 | - active_loop: transaction_search_form 759 | - story: search transactions + switch to pay credit card, affirm + switch back, affirm 760 | steps: 761 | - intent: check_earnings 762 | user: |- 763 | check earnings 764 | - action: transaction_search_form 765 | - active_loop: transaction_search_form 766 | - intent: pay_cc 767 | user: |- 768 | pay [credit card]{"entity": "account_type", "value": "credit"} 769 | - action: action_switch_forms_ask 770 | - slot_was_set: 771 | - next_form_name: cc_payment_form 772 | - intent: affirm 773 | - action: action_switch_forms_affirm 774 | - slot_was_set: 775 | - next_form_name: null 776 | - slot_was_set: 777 | - previous_form_name: transaction_search_form 778 | - action: cc_payment_form 779 | - active_loop: cc_payment_form 780 | - active_loop: null 781 | - slot_was_set: 782 | - requested_slot: null 783 | - action: action_pay_cc 784 | - action: action_switch_back_ask 785 | - slot_was_set: 786 | - previous_form_name: null 787 | - intent: affirm 788 | - action: transaction_search_form 789 | - active_loop: transaction_search_form 790 | - story: transfer money+ switch to search transactions, affirm + switch back, affirm 791 | steps: 792 | - intent: transfer_money 793 | user: |- 794 | transfer money 795 | - action: transfer_money_form 796 | - active_loop: transfer_money_form 797 | - intent: check_earnings 798 | user: |- 799 | check earnings 800 | - action: action_switch_forms_ask 801 | - slot_was_set: 802 | - next_form_name: transaction_search_form 803 | - intent: affirm 804 | - action: action_switch_forms_affirm 805 | - slot_was_set: 806 | - next_form_name: null 807 | - slot_was_set: 808 | - previous_form_name: transfer_money_form 809 | - action: transaction_search_form 810 | - active_loop: transaction_search_form 811 | - active_loop: null 812 | - slot_was_set: 813 | - requested_slot: null 814 | - action: action_transaction_search 815 | - action: action_switch_back_ask 816 | - slot_was_set: 817 | - previous_form_name: null 818 | - intent: affirm 819 | - action: transfer_money_form 820 | - active_loop: transfer_money_form 821 | - story: transfer money+ switch to pay credit card, affirm + switch back, affirm 822 | steps: 823 | - intent: transfer_money 824 | user: |- 825 | transfer money 826 | - action: transfer_money_form 827 | - active_loop: transfer_money_form 828 | - intent: pay_cc 829 | user: |- 830 | pay [credit card]{"entity": "account_type", "value": "credit"} 831 | - action: action_switch_forms_ask 832 | - slot_was_set: 833 | - next_form_name: cc_payment_form 834 | - intent: affirm 835 | - action: action_switch_forms_affirm 836 | - slot_was_set: 837 | - next_form_name: null 838 | - slot_was_set: 839 | - previous_form_name: transfer_money_form 840 | - action: cc_payment_form 841 | - active_loop: cc_payment_form 842 | - active_loop: null 843 | - slot_was_set: 844 | - requested_slot: null 845 | - action: action_pay_cc 846 | - action: action_switch_back_ask 847 | - slot_was_set: 848 | - previous_form_name: null 849 | - intent: affirm 850 | - action: transfer_money_form 851 | - active_loop: transfer_money_form 852 | - story: providing a name must be inform intent 853 | steps: 854 | - intent: transfer_money 855 | user: |- 856 | i want to transfer money 857 | - action: transfer_money_form 858 | - active_loop: transfer_money_form 859 | - slot_was_set: 860 | - AA_CONTINUE_FORM: yes 861 | - slot_was_set: 862 | - requested_slot: PERSON 863 | - intent: inform 864 | user: |- 865 | [lisa](PERSON) 866 | - slot_was_set: 867 | - requested_slot: amount-of-money 868 | - action: transfer_money_form 869 | --------------------------------------------------------------------------------