Full Code of RasaHQ/helpdesk-assistant for AI

main 2e9f2258c664 cached
29 files
76.1 KB
19.7k tokens
32 symbols
1 requests
Download .txt
Repository: RasaHQ/helpdesk-assistant
Branch: main
Commit: 2e9f2258c664
Files: 29
Total size: 76.1 KB

Directory structure:
gitextract_o7a7u4a9/

├── .github/
│   └── workflows/
│       ├── continuous-integration.yml
│       ├── lint_and_test.yml
│       └── lint_and_test_pr.yml
├── .gitignore
├── .pre-commit-config.yaml
├── Dockerfile.chatroom
├── LICENSE
├── Makefile
├── README.md
├── actions/
│   ├── __init__.py
│   ├── actions.py
│   ├── handoff.py
│   ├── handoff_config.yml
│   ├── requirements-actions.txt
│   ├── snow.py
│   └── snow_credentials.yml
├── chatroom_handoff.html
├── config.yml
├── data/
│   ├── handoff.yml
│   ├── nlu.yml
│   ├── rules.yml
│   └── stories.yml
├── domain.yml
├── endpoints.yml
├── format_results.py
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
└── tests/
    └── test_conversations.yml

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/continuous-integration.yml
================================================
name: Continuous Integration
on:
  push:
    branches:
    - main
    paths:
    - 'actions/**'
    - '.github/workflows/continuous-integration.yml'

jobs:
  docker:
    name: Build Action Server Docker Image
    runs-on: ubuntu-latest
    env:
      DOCKERHUB_USERNAME: oakela
    steps:
      - name: Checkout git repository 🕝
        uses: actions/checkout@v2
      - name: Build Actions Server Image
        uses: RasaHQ/rasa-action-server-gha@main
        with:
          actions_directory: 'actions'
          requirements_file: 'actions/requirements-actions.txt'
          docker_registry_login: ${{ env.DOCKERHUB_USERNAME }}
          docker_registry_password: ${{ secrets.DOCKERHUB_PASSWORD }}
          docker_image_name: 'rasa/helpdesk-assistant'
          docker_image_tag: 'latest'
          rasa_sdk_version: '3.0.0'


================================================
FILE: .github/workflows/lint_and_test.yml
================================================
name: Lint and Test
on:
  push:
    branches:
      - main
    paths-ignore:
    - "README.md"
    - "Makefile"
    - "Dockerfile"

jobs:
  lint-testing:
    name: Code Formatting Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Install dependencies
      run: |
        python -m pip install -U pip
        pip install -r requirements-dev.txt
    - name: Code Formatting Tests
      working-directory: ${{ github.workspace }}
      run: |
        make lint
  type-testing:
    name: Type Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.6
      uses: actions/setup-python@v1
      with:
        python-version: 3.6
    - name: Install dependencies
      run: |
        python -m pip install -U pip
        pip install -r requirements-dev.txt
    - name: Type Checking
      working-directory: ${{ github.workspace }}
      run: |
        make types
  training-testing:
    name: Training and Testing
    runs-on: ubuntu-latest
    needs: [lint-testing, type-testing]
    steps:
      - uses: actions/checkout@v1
      - id: files
        uses: jitterbit/get-changed-files@v1
      - name: set_training
        if: |
            contains(  steps.files.outputs.all, 'data/' )
            || contains(  steps.files.outputs.all, 'config.yml' )
            || contains(  steps.files.outputs.all, 'domain.yml' )
        run: echo "RUN_TRAINING=true" >> $GITHUB_ENV
      - name: Rasa Train and Test GitHub Action
        if: env.RUN_TRAINING == 'true'
        uses: RasaHQ/rasa-train-test-gha@main
        with:
          rasa_version: '3.0.0'
          test_type: all
          data_validate: true
          cross_validation: true
          publish_summary: true
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Upload model
        if: github.ref == 'refs/heads/main'
        uses: actions/upload-artifact@main
        with:
          name: model
          path: models


================================================
FILE: .github/workflows/lint_and_test_pr.yml
================================================
name: Lint and Test PR
on:
  pull_request:
    paths-ignore:
    - "README.md"
    - "Makefile"
    - "Dockerfile"

jobs:
  lint-testing:
    name: Code Formatting Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Install dependencies
      run: |
        python -m pip install -U pip
        pip install -r requirements-dev.txt
    - name: Code Formatting Tests
      working-directory: ${{ github.workspace }}
      run: |
        make lint
  type-testing:
    name: Type Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7
    - name: Install dependencies
      run: |
        python -m pip install -U pip
        pip install -r requirements-dev.txt
    - name: Type Checking
      working-directory: ${{ github.workspace }}
      run: |
        make types
  training-testing:
    name: Training and Testing
    runs-on: ubuntu-latest
    needs: [lint-testing, type-testing]
    steps:
      - uses: actions/checkout@v1
      - id: files
        uses: jitterbit/get-changed-files@v1
      - name: set_training
        if: |
            contains(  steps.files.outputs.all, 'data/' )
            || contains(  steps.files.outputs.all, 'config.yml' )
            || contains(  steps.files.outputs.all, 'domain.yml' )
        run: echo "RUN_TRAINING=true" >> $GITHUB_ENV
      - name: Rasa Train and Test GitHub Action
        if: env.RUN_TRAINING == 'true'
        uses: RasaHQ/rasa-train-test-gha@main
        with:
          rasa_version: '3.0.0'
          test_type: all
          data_validate: true
          cross_validation: true
          publish_summary: true
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Upload model
        if: github.ref == 'refs/heads/main'
        uses: actions/upload-artifact@main
        with:
          name: model
          path: models


================================================
FILE: .gitignore
================================================
# Standard Ignores
venv
__pycache__/
.DS_Store
.vscode/
.history/
.pytype/

# Rasa Ignores
credentials.yml
*.db-*
rasa.db*
*.db
results/
models/
snow_connector.py
chatroom/
.idea/

================================================
FILE: .pre-commit-config.yaml
================================================
repos:
-   repo: https://github.com/ambv/black
    rev: stable
    hooks:
    - id: black


================================================
FILE: Dockerfile.chatroom
================================================
# docker build -t chatroom -f Dockerfile.chatroom
# docker run --name chatroom -p 8080:3000 -d chatroom
FROM node:14

RUN node --version

RUN git clone https://github.com/RasaHQ/chatroom.git

WORKDIR /chatroom

RUN curl -o- -L https://yarnpkg.com/install.sh | bash
RUN yarn

# replace default chatroom index.html
COPY chatroom_handoff.html index.html

RUN yarn build

EXPOSE 8080

CMD [ "yarn", "serve" ]

================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright 2020 Rasa Technologies GmbH

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: Makefile
================================================
help:
	@echo "make"
	@echo "    clean"
	@echo "        Remove Python/build artifacts."
	@echo "    formatter"
	@echo "        Apply black formatting to code."
	@echo "    lint"
	@echo "        Lint code with flake8, and check if black formatter should be applied."
	@echo "    types"
	@echo "        Check for type errors using pytype."
	@echo "    validate"
	@echo "        Runs the rasa data validate to verify data."
	@echo "    test"
	@echo "        Runs the rasa test suite checking for issues."
	@echo "    crossval"
	@echo "        Runs the rasa cross validation tests and creates results.md"
	@echo "    shell"
	@echo "        Runs the rasa train and rasa shell for testing"


clean:
	find . -name '*.pyc' -exec rm -f {} +
	find . -name '*.pyo' -exec rm -f {} +
	find . -name '*~' -exec rm -f  {} +
	rm -rf build/
	rm -rf .pytype/
	rm -rf dist/
	rm -rf docs/_build

formatter:
	black actions --line-length 79

lint:
	flake8 actions
	black --check actions

types:
	pytype --keep-going actions

validate:
	rasa train
	rasa data validate --debug

test:
	rasa train
	rasa test --fail-on-prediction-errors

crossval:
	rasa test nlu -f 5 --cross-validation
	python format_results.py

shell:
	rasa train --debug
	rasa shell --debug

================================================
FILE: README.md
================================================
# Rasa Helpdesk Assistant Example

This is a Rasa chatbot example demonstrating how to build an AI assistant for an IT Helpdesk. It includes an integration with the Service Now API to open incident reports and check on incident report statuses. Below is an example conversation, showing the bot helping a user open a support ticket and query its status. You can use this chatbot as a starting point for building customer service assistants or as a template for collecting required pieces of information from a user before making an API call.

Here is an example of a conversation you can have with this bot:

![Screenshot](./screenshots/demo_ss.png?raw=true)


<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents**

- [Rasa Helpdesk Assistant Example](#rasa-helpdesk-assistant-example)
  - [Setup](#setup)
    - [Install the dependencies](#install-the-dependencies)
    - [Optional: Connect to a ServiceNow instance](#optional-connect-to-a-servicenow-instance)
  - [Running the bot](#running-the-bot)
  - [Things you can ask the bot](#things-you-can-ask-the-bot)
  - [Example conversations](#example-conversations)
  - [Handoff](#handoff)
    - [Try it out](#try-it-out)
    - [How it works](#how-it-works)
    - [Bot-side configuration](#bot-side-configuration)
  - [Testing the bot](#testing-the-bot)
  - [Rasa X Deployment](#rasa-x-deployment)
    - [Action Server Image](#action-server-image)
  - [Notes on Chatroom](#notes-on-chatroom)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Setup

### Install the dependencies

In a Python3 virtual environment run:

```bash
pip install -r requirements.txt
```

To install development dependencies, run:

```bash
pip install -r requirements-dev.txt
pre-commit install
```

> With pre-commit installed, the `black` and `doctoc` hooks will run on every `git commit`.
> If any changes are made by the hooks, you will need to re-add changed files and re-commit your changes.

### Optional: Connect to a ServiceNow instance

You can run this bot without connecting to a ServiceNow instance, in which case it will
send responses without creating an incident or checking the actual status of one.
To run the bot  without connecting ServiceNow,
you don't need to change anything in `actions/snow_credentials.yml`; `localmode` should already be set
to `true`

If you do want to connect to ServiceNow, you can get your own free Developer instance
to test this with [here](https://developer.servicenow.com/app.do#!/home)

To connect to your ServiceNow developer instance, configure the following in `actions/snow_credentials.yml`:

- `snow_instance` - This is the address of the ServiceNow developer instance, you don't need the leading https.

- `snow_user` - The username of the service account for the ServiceNow developer instance

- `snow_pw` - The password of the service account for the ServiceNow developer instance

- `localmode` -  Whether the action server should **not** try to reach out to a `snow_instance` based on the credentials in `actions/snow_credentials.yml`. When set to `True` (default in the code), it will just take all the data in and message out the information that would normally be sent.

## Running the bot

Use `rasa train` to train a model.

Then, to run, first set up your action server in one terminal window:

```bash
rasa run actions
```

In another window, run the duckling server (for entity extraction):

```bash
docker run -p 8000:8000 rasa/duckling
```

Then to talk to the bot, run:

```bash
rasa shell --debug
```

Note that `--debug` mode will produce a lot of output meant to help you understand how the bot is working
under the hood. You can also add this flag to the action server command. To simply talk to the bot, you can remove this flag.

You can also try out the bot locally using Rasa X by running

```bash
rasa x
```

Refer to our guided workflow in the [Wiki page](https://github.com/RasaHQ/helpdesk-assistant/wiki/Using-Rasa-X-with-the-Helpdesk-Assistant) for how to get started with Rasa X in local mode.

## Things you can ask the bot

The bot has two main skills:
1. Opening an incident in ServiceNow.
2. Checking the status of incidents in ServiceNow by email address of the caller. It will prompt the user to re-use previously provided (during the chat session) email addresses, if any exist.
For the purposes of illustration, the bot recognizes the following as requests to open an incident:

1. Asking to open an incident directly e.g. "I want to open an incident"
2. Asking about a problem resetting their password e.g. "I can't reset my password"
3. Asking about a problem with outlook/email e.g. "I can't log in to my outlook"

Take a look at `data/nlu.md` to see what the bot is currently trained to recognize.

It can also respond to requests for help (e.g. "help me").

If configured, the bot can also hand off to another bot in response to the user asking for handoff. More [details on handoff](#handoff) below.

## Example conversations

With `localmode=true`:

```
Bot loaded. Type a message and press enter (use '/stop' to exit):
Your input ->  hi
    Hello!
    I can help you open a service request ticket, or check the status of your open incidents.
    You can ask me things like:
    - Open an incident
    - Help me reset my password
    - I'm having a issue with my email
    - What's the status of the ticket I opened?
Your input ->  i have a problem with my email
    What is your email address?
Your input ->  anything@example.com
    ? What is the priority of this issue?
Your input -> 1: low (/inform{"priority":"low"})
    What is the problem description for the issue?
Your input ->  Can't log in to my email
    ? Should I open an incident with the following details?
    email: anything@example.com
    problem description: Can't log in to my email
    title: Problem with email
Your input -> priority: low  1: Yes (/affirm)
    An incident with the following details would be opened if ServiceNow was connected:
    email: anything@example.com
    problem description: Can't log in to my email
    title: Problem with email
    priority: low
Your input ->  thanks. Can i check the status of my other tickets?
    Would you like to use the last email address you used, anything@example.com?
Your input ->  Yes please
    Since ServiceNow isn't connected, I'm making this up!
    The most recent incident for anything@example.com is currently awaiting triage
```

With `localmode=false`:

With a Service Now instance connected, it will check if the email address is in the instance database and provide an incident number for the final response:

```
Your input ->  help me reset my password
    What is your email address?
Your input ->  idontexist@example.com
    Sorry, "idontexist@example.com" isn't in our incident management system. Please try again.
    What is your email address?
Your input ->  abraham.lincoln@example.com
    ? What is the priority of this issue?
Your input -> 3: high (/inform{"priority":"high"})
    What is the problem description for the issue?
Your input ->  Password stuck in a loop
    ? Should I open an incident with the following details?
    email: abraham.lincoln@example.com
    problem description: Password stuck in a loop
    title: Problem resetting password
    priority: high
Your input ->  1: Yes (/affirm)
    Successfully opened up incident INC0010008 for you.  Someone will reach out soon.
Your input ->  Can I check the status of my tickets?
    Would you like to use the last email address you used, abraham.lincoln@example.com?
Your input ->  Yes please
    Incident INC0010002: "Email Log in problem", opened on 2020-05-21 09:57:06 is currently in progress
    Incident INC0010008: "Problem resetting password", opened on 2020-05-21 12:12:49 is currently awaiting triage
Your input ->  thanks
    You're welcome!
```


## Handoff

This bot includes a simple skill for handing off the conversation to another bot or a human.
This demo relies on [this fork of chatroom](https://github.com/RasaHQ/chatroom) to work, however you
could implement similar behaviour in another channel and then use that instead. See the chatroom README for
more details on channel-side configuration.


Using the default set up, the handoff skill enables this kind of conversation with two bots:

<img src="./handoff.gif" width="200">

### Try it out

The simplest way to use the handoff feature is to do the following:

1. Clone [chatroom](https://github.com/RasaHQ/chatroom) and [Financial-Demo](https://github.com/RasaHQ/Financial-Demo) alongside this repo
2. In the chatroom repo, install the dependencies:
```bash
yarn install
```
3. In the chatroom repo, build and serve chatroom:
```bash
yarn build
yarn serve
```
4. In the Financial-Demo repo, install the dependencies and train a model (see the Financial-Demo README)
5. In the Helpdesk-Assistant repo (i.e. this repo), run the rasa server and action server at the default ports (shown here for clarity)
   In one terminal window:
    ```bash
    rasa run --enable-api --cors "*" --port 5005 --debug
    ```
    In another terminal window:
    ```bash
    rasa run actions --port 5055 --debug
    ```
6. In the Financial-Demo repo, run the rasa server and action server at **the non-default ports shown below**
   In one terminal window:
    ```bash
    rasa run --enable-api --cors "*" --port 5006 --debug
    ```
    In another terminal window:
    ```bash
    rasa run actions --port 5056 --debug
    ```
7. Open `chatroom_handoff.html` in a browser to see handoff in action


### How it works

Using chatroom, the general approach is as follows:

1. User asks original bot for a handoff.
2. The original bot handles the request and eventually
   sends a message with the following custom json payload:
    ```
        {
            "handoff_host": "<url of handoff host endpoint>",
            "title": "<title for bot/channel handed off to>"
            }
    ```
    This message is not displayed in the Chatroom window.
3. Chatroom switches the host to the specified `handoff_host`
4. The original bot no longer receives any messages.
5. The handoff host receives the message `/handoff{"from_host":"<original bot url">}`
6. The handoff host should be configured to respond to this message with something like,
   "Hi, I'm <so and so>, how can I help you??"
7. The handoff host can send a message in the same format as specified above to hand back to the original bot.
   In this case the same pattern repeats, but with
   the roles reversed. It could also hand off to yet another bot/human.

### Bot-side configuration

The "try it out" section doesn't require any further configuration; this section is for those
who want to change or further understand the set up.

For this demo, the user can ask for a human, but they'll be offered a bot (or bots) instead,
so that the conversation looks like this:


For handoff to work, you need at least one "handoff_host". You can specify any number of handoff hosts in the file `actions/hanodff_config.yml`.
```
handoff_hosts:
    financial_demo:
      title: "Financial Demo"
      url: "http://localhost:5006"
    ## you can add more handoff hosts to this list e.g.
    # moodbot:
    #   title: "MoodBot"
    #   url: "http://localhost:5007"
```

Handoff hosts can be other locally running rasa bots, or anything that serves responses in the format that chatroom
accepts. If a handoff host is not a rasa bot, you will of course want to update the response text to tell the user
who/what they are being handed off to.

The [Financial-Demo](https://github.com/RasaHQ/Financial-Demo) bot has been set up to handle handoff in exactly the same way as Helpdesk-Assistant,
so the simplest way to see handoff in action is to clone Financial-Demo alongside this repo.

If you list other locally running bots as handoff hosts, make sure the ports on which the various rasa servers & action servers are running do not conflict with each other.


## Testing the bot

You can test the bot on the test conversations by running  `rasa test`.
This will run [end-to-end testing](https://rasa.com/docs/rasa/user-guide/testing-your-assistant/#end-to-end-testing) on the conversations in `tests/conversation_tests.md`.

## Rasa X Deployment

To [deploy helpdesk-assistant](https://rasa.com/docs/rasa/user-guide/how-to-deploy/), it is highly recommended to make use of the
[one line deploy script](https://rasa.com/docs/rasa-x/installation-and-setup/one-line-deploy-script/) for Rasa X. As part of the deployment, you'll need to set up [git integration](https://rasa.com/docs/rasa-x/installation-and-setup/integrated-version-control/#connect-your-rasa-x-server-to-a-git-repository) to pull in your data and
configurations, and build or pull an action server image.

### Action Server Image

You will need to have docker installed in order to build the action server image. If you haven't made any changes to the action code, you can also use
the [public image on Dockerhub](https://hub.docker.com/r/rasa/helpdesk-assistant) instead of building it yourself.


See the Dockerfile for what is included in the action server image,

To build the image:

```bash
docker build . -t <name of your custom image>:<tag of your custom image>
```

To test the container locally, you can then run the action server container with:

```bash
docker run -p 5055:5055 <name of your custom image>:<tag of your custom image>
```

Once you have confirmed that the container works as it should, you can push the container image to a registry with `docker push`

It is recommended to use an[automated CI/CD process](https://rasa.com/docs/rasa/user-guide/setting-up-ci-cd) to keep your action server up to date in a production environment.

## Notes on Chatroom

If you want to try the transfer to another bot feature, you'll need to use Chatroom.  As of this writing, the main Scalable Minds chatroom [project](https://github.com/scalableminds/chatroom) has not included this feature so you will need to build from a fork. The following docker commands will build an image from the adapted Chatroom and run it.

```
docker build -t chatroom -f Dockerfile.chatroom .
docker run --name chatroom -p 8080:8080 -d chatroom
```

From the `docker-compose.yml` below, you can start chatroom with `docker-compose up -d`

Here's an example docker-compose.yml for this image. Note that the initial Rasa endpoint URL is hard coded in `chatroom_handoff.html`; to use your locally running bot, point it to `http://localhost:5005`.

```
version: "3.4"

services:
  chatroom:
    image: chatroom
    build:
      context: ./
      dockerfile: Dockerfile.chatroom
    ports:
      - "8080:8080"
    command: [ "yarn", "serve" ]
```


================================================
FILE: actions/__init__.py
================================================


================================================
FILE: actions/actions.py
================================================
import logging
from typing import Dict, Text, Any, List
from rasa_sdk import Tracker
from rasa_sdk.executor import CollectingDispatcher, Action
from rasa_sdk.forms import FormValidationAction
from rasa_sdk.events import AllSlotsReset, SlotSet
from actions.snow import SnowAPI
import random


logger = logging.getLogger(__name__)
vers = "vers: 0.1.0, date: Apr 2, 2020"
logger.debug(vers)

snow = SnowAPI()
localmode = snow.localmode
logger.debug(f"Local mode: {snow.localmode}")


class ActionAskEmail(Action):
    def name(self) -> Text:
        return "action_ask_email"

    def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict]:
        if tracker.get_slot("previous_email"):
            dispatcher.utter_message(template=f"utter_ask_use_previous_email",)
        else:
            dispatcher.utter_message(template=f"utter_ask_email")
        return []


def _validate_email(
    value: Text,
    dispatcher: CollectingDispatcher,
    tracker: Tracker,
    domain: Dict[Text, Any],
) -> Dict[Text, Any]:
    """Validate email is in ticket system."""
    if not value:
        return {"email": None, "previous_email": None}
    elif isinstance(value, bool):
        value = tracker.get_slot("previous_email")

    if localmode:
        return {"email": value}

    results = snow.email_to_sysid(value)
    caller_id = results.get("caller_id")

    if caller_id:
        return {"email": value, "caller_id": caller_id}
    elif isinstance(caller_id, list):
        dispatcher.utter_message(template="utter_no_email")
        return {"email": None}
    else:
        dispatcher.utter_message(results.get("error"))
        return {"email": None}


class ValidateOpenIncidentForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_open_incident_form"

    def validate_email(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        """Validate email is in ticket system."""
        return _validate_email(value, dispatcher, tracker, domain)

    def validate_priority(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        """Validate priority is a valid value."""

        if value.lower() in snow.priority_db():
            return {"priority": value}
        else:
            dispatcher.utter_message(template="utter_no_priority")
            return {"priority": None}


class ActionOpenIncident(Action):
    def name(self) -> Text:
        return "action_open_incident"

    def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict]:
        """Create an incident and return details or
        if localmode return incident details as if incident
        was created
        """

        priority = tracker.get_slot("priority")
        email = tracker.get_slot("email")
        problem_description = tracker.get_slot("problem_description")
        incident_title = tracker.get_slot("incident_title")
        confirm = tracker.get_slot("confirm")
        if not confirm:
            dispatcher.utter_message(
                template="utter_incident_creation_canceled"
            )
            return [AllSlotsReset(), SlotSet("previous_email", email)]

        if localmode:
            message = (
                f"An incident with the following details would be opened "
                f"if ServiceNow was connected:\n"
                f"email: {email}\n"
                f"problem description: {problem_description}\n"
                f"title: {incident_title}\npriority: {priority}"
            )
        else:
            snow_priority = snow.priority_db().get(priority)
            response = snow.create_incident(
                description=problem_description,
                short_description=incident_title,
                priority=snow_priority,
                email=email,
            )
            incident_number = (
                response.get("content", {}).get("result", {}).get("number")
            )
            if incident_number:
                message = (
                    f"Successfully opened up incident {incident_number} "
                    f"for you. Someone will reach out soon."
                )
            else:
                message = (
                    f"Something went wrong while opening an incident for you. "
                    f"{response.get('error')}"
                )
        dispatcher.utter_message(message)
        return [AllSlotsReset(), SlotSet("previous_email", email)]


class IncidentStatusForm(FormValidationAction):
    def name(self) -> Text:
        return "validate_incident_status_form"

    def validate_email(
        self,
        value: Text,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        """Validate email is in ticket system."""
        return _validate_email(value, dispatcher, tracker, domain)


class ActionCheckIncidentStatus(Action):
    def name(self) -> Text:
        return "action_check_incident_status"

    def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict]:
        """Look up all incidents associated with email address
           and return status of each"""

        email = tracker.get_slot("email")

        incident_states = {
            "New": "is currently awaiting triage",
            "In Progress": "is currently in progress",
            "On Hold": "has been put on hold",
            "Closed": "has been closed",
        }
        if localmode:
            status = random.choice(list(incident_states.values()))
            message = (
                f"Since ServiceNow isn't connected, I'm making this up!\n"
                f"The most recent incident for {email} {status}"
            )
        else:
            incidents_result = snow.retrieve_incidents(email)
            incidents = incidents_result.get("incidents")
            if incidents:
                message = "\n".join(
                    [
                        f'Incident {i.get("number")}: '
                        f'"{i.get("short_description")}", '
                        f'opened on {i.get("opened_at")} '
                        f'{incident_states.get(i.get("incident_state"))}'
                        for i in incidents
                    ]
                )

            else:
                message = f"{incidents_result.get('error')}"

        dispatcher.utter_message(message)
        return [AllSlotsReset(), SlotSet("previous_email", email)]


================================================
FILE: actions/handoff.py
================================================
from rasa_sdk import Tracker, Action
from rasa_sdk.executor import CollectingDispatcher

import ruamel.yaml
import pathlib
from typing import Dict, Text, Any, List
from rasa_sdk.events import EventType

here = pathlib.Path(__file__).parent.absolute()
handoff_config = (
    ruamel.yaml.safe_load(open(f"{here}/handoff_config.yml", "r")) or {}
).get("handoff_hosts", {})


class ActionHandoffOptions(Action):
    def name(self) -> Text:
        return "action_handoff_options"

    async def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[EventType]:

        if not any(
            [config.get("url") for bot, config in handoff_config.items()]
        ):
            dispatcher.utter_message(template="utter_no_handoff")
        else:
            buttons = [
                {
                    "title": config.get("title"),
                    "payload": f'/trigger_handoff{{"handoff_to":"{bot}"}}',
                }
                for bot, config in handoff_config.items()
            ]
            dispatcher.utter_message(
                text="I can't transfer you to a human, \
                      but I can transfer you to one of these bots",
                buttons=buttons,
            )
        return []


class ActionHandoff(Action):
    def name(self) -> Text:
        return "action_handoff"

    async def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[EventType]:

        dispatcher.utter_message(template="utter_handoff")
        handoff_to = tracker.get_slot("handoff_to")

        handoff_bot = handoff_config.get(handoff_to, {})
        url = handoff_bot.get("url")

        if url:
            if tracker.get_latest_input_channel() == "rest":
                dispatcher.utter_message(
                    json_message={
                        "handoff_host": url,
                        "title": handoff_bot.get("title"),
                    }
                )
            else:
                dispatcher.utter_message(
                    template="utter_wouldve_handed_off", handoffhost=url
                )
        else:
            dispatcher.utter_message(template="utter_no_handoff")

        return []


================================================
FILE: actions/handoff_config.yml
================================================
handoff_hosts:
    financial_demo:
      title: "Financial Assistant"
      url: "http://localhost:5006"
    ## you can add more handoff hosts to this list e.g.
    # moodbot:
    #   title: "MoodBot"
    #   url: "http://localhost:5007"

================================================
FILE: actions/requirements-actions.txt
================================================
pytablewriter
requests
ruamel.yaml

================================================
FILE: actions/snow.py
================================================
import logging
import requests
import json
import pathlib
import ruamel.yaml
from typing import Dict, Text, Any

logger = logging.getLogger(__name__)

here = pathlib.Path(__file__).parent.absolute()

json_headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
}


class SnowAPI(object):
    """class to connect to the ServiceNow API"""

    def __init__(self):
        snow_config = (
            ruamel.yaml.safe_load(open(f"{here}/snow_credentials.yml", "r"))
            or {}
        )
        self.snow_user = snow_config.get("snow_user")
        self.snow_pw = snow_config.get("snow_pw")
        self.snow_instance = snow_config.get("snow_instance")
        self.localmode = snow_config.get("localmode", True)
        self.base_api_url = "https://{}/api/now".format(self.snow_instance)

    def handle_request(
        self, request_method=requests.get, request_args={}
    ) -> Dict[Text, Any]:
        result = dict()
        try:
            response = request_method(**request_args)
            result["status_code"] = response.status_code
            if response.status_code >= 200 < 300:
                result["content"] = response.json()
            else:
                error = (
                    f"ServiceNow error: {response.status_code}: "
                    f'{response.json().get("error",{}).get("message")}'
                )
                logger.debug(error)
                result["error"] = error
        except requests.exceptions.Timeout:
            error = "Could not connect to ServiceNow (Timeout)"
            logger.debug(error)
            result["error"] = error
        return result

    def email_to_sysid(self, email) -> Dict[Text, Any]:
        lookup_url = (
            f"{self.base_api_url}/table/sys_user?"
            f"sysparm_query=email={email}&sysparm_display_value=true"
        )
        request_args = {
            "url": lookup_url,
            "auth": (self.snow_user, self.snow_pw),
            "headers": json_headers,
        }
        result = self.handle_request(requests.get, request_args)
        records = result.get("content", {}).get("result")
        if len(records) == 1:
            caller_id = records[0].get("sys_id")
            result["caller_id"] = caller_id
        elif isinstance(records, list):
            result["caller_id"] = []
            result["error"] = (
                f"Could not retrieve caller id; "
                f"{records} records found for email {email}"
            )
        return result

    def retrieve_incidents(self, email) -> Dict[Text, Any]:
        result = self.email_to_sysid(email)
        caller_id = result.get("caller_id")
        if caller_id:
            incident_url = (
                f"{self.base_api_url}/table/incident?"
                f"sysparm_query=caller_id={caller_id}"
                f"&sysparm_display_value=true"
            )
            request_args = {
                "url": incident_url,
                "auth": (self.snow_user, self.snow_pw),
                "headers": json_headers,
            }
            result = self.handle_request(requests.get, request_args)
            incidents = result.get(
                "content", {}  # pytype: disable=attribute-error
            ).get("result")
            if incidents:
                result["incidents"] = incidents
            elif isinstance(incidents, list):
                result["error"] = f"No incidents on record for {email}"
        return result

    def create_incident(
        self, description, short_description, priority, email
    ) -> Dict[Text, Any]:
        result = self.email_to_sysid(email)
        caller_id = result.get("caller_id")
        if caller_id:
            incident_url = f"{self.base_api_url}/table/incident"
            data = {
                "opened_by": caller_id,
                "short_description": short_description,
                "description": description,
                "urgency": priority,
                "caller_id": caller_id,
                "comments": description,
            }
            request_args = {
                "url": incident_url,
                "auth": (self.snow_user, self.snow_pw),
                "headers": json_headers,
                "data": json.dumps(data),
            }
            result = self.handle_request(requests.post, request_args)
        return result

    @staticmethod
    def priority_db() -> Dict[str, int]:
        """Database of supported priorities"""
        priorities = {"low": 3, "medium": 2, "high": 1}
        return priorities


================================================
FILE: actions/snow_credentials.yml
================================================
# snow_instance: "dev97377.service-now.com"
# snow_user: "admin"
# snow_pw: "mySnowinstance1"
# localmode: false

================================================
FILE: chatroom_handoff.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  <link rel="stylesheet" href="./dist/Chatroom.css" />
  <link rel="stylesheet" type="text/css" href="./index.css">
</head>
<body>
  <div class="chat-container"></div>

  <script src="./dist/Chatroom.js"></script>
  <script type="text/javascript">
    var chatroom = new window.Chatroom({
      host: "http://helpdesk-assistant.rasa.com",
      title: "Helpdesk Assistant",
      container: document.querySelector(".chat-container"),
      welcomeMessage: "Hi, how may I help you?"
      // speechRecognition: "en-US",
      // voiceLang: "en-US"
    });
    chatroom.openChat();
  </script>
</body>
</html>

================================================
FILE: config.yml
================================================
language: en
pipeline:
  - name: WhitespaceTokenizer
  - name: RegexFeaturizer
  - name: LexicalSyntacticFeaturizer
  - name: CountVectorsFeaturizer
  - name: CountVectorsFeaturizer
    analyzer: "char_wb"
    min_ngram: 1
    max_ngram: 4
  - name: DIETClassifier
    epochs: 150
    random_seed: 1
  - name: FallbackClassifier
    threshold: 0.7
  - name: DucklingEntityExtractor
    url: http://localhost:8000
    dimensions:
    - email
  - name: EntitySynonymMapper
policies:
  - name: AugmentedMemoizationPolicy
    max_history: 4
  - name: TEDPolicy
    max_history: 4
    epochs: 100
  - name: RulePolicy
    core_fallback_threshold: 0.4
    core_fallback_action_name: "action_default_fallback"
    enable_fallback_prediction: True



================================================
FILE: data/handoff.yml
================================================
version: "3.0"
stories:
- story: handoff
  steps:
  - intent: human_handoff
  - action: utter_ask_handoff
  - action: action_handoff_options
  - intent: trigger_handoff
  - action: action_handoff

- story: handoff
  steps:
  - intent: human_handoff
  - action: utter_ask_handoff
  - action: action_handoff_options
  - intent: trigger_handoff
    entities:
    - handoff_to: financial_demo
  - action: action_handoff

- story: handoff deny
  steps:
  - intent: human_handoff
  - action: utter_ask_handoff
  - action: action_handoff_options
  - intent: deny
  - action: utter_ask_whatelse


================================================
FILE: data/nlu.yml
================================================
version: "3.0"
nlu:
- intent: greet
  examples: |
    - hey
    - hello
    - hi
    - good morning
    - good evening
    - hey there
    - hi there
    - hello there
    - greetings
    - Hi
    - helo
    - Hi!
    - Hi'
    - Hi,
    - Hi, bot
    - Hieee
    - Hieeeeeeeeeeeee
    - Hola
    - What is up?
    - Whats up
    - Whats up my bot
    - Whats up?
    - ayyyy whaddup
    - bonjour
    - ey boss
    - I said, helllllloooooO!!!!
    - Well hello there ;)
- intent: goodbye
  examples: |
    - bye
    - goodbye
    - see you around
    - see you later
    - see ya
    - buh bye
    - Bye
    - Bye bye
    - adios
    - adios?
    - bye :P
    - bye bot
    - bye bye
    - bye bye bot
    - bye for now
    - bye udo
    - bye was nice talking to you
    - bye!
    - byee
    - catch you later
    - ciao
    - cya
    - farewell
    - good bye
    - good bye rasa bot!
    - good night
    - goodbye
    - goodbye.
    - goodnight
    - gotta go
    - ok Bye
    - ok bye
    - ok, bye
    - ok.bye
    - see u later
    - see ya
    - see you
    - see you . bye
    - take care
    - then bye
    - tlak to you later
    - toodle-oo
    - byr
- intent: thank
  examples: |
    - thanks!
    - thank you
    - thanks a lot
    - great thanks
    - appreciate it
    - cool thanks
    - Cool. Thanks
    - Great, thanks
    - Thank you so much
    - Thank's!
    - Thanks bot
    - Thanks for that
    - Thanks!
    - amazing, thanks
    - cool thank you
    - cool, thanks
    - danke
    - great thanks
    - ok thanks
    - ok thanks
    - ok thanks!
    - perfect thank you
    - thank u
    - thank you
    - thank you anyways
    - thanks
    - thanks a bunch for everything
    - thanks a lot
    - thanks for the help
    - thanks you
    - thanks!
    - thankyou
    - thnks
    - thx
    - yes thanks
    - thanks for your information
    - thanks f
- intent: bot_challenge
  examples: |
    - are you a bot?
    - are you a human?
    - am I talking to a bot?
    - am I talking to a human?
    - you are a bot right
    - aren't you a bot
    - Are you a bot?
    - are you a BOT
    - are you a bot?
    - are you a chatbot
    - are you a rasa bot?
    - are you a real bot?
    - are you a robot
    - are you ai
    - are you artificial
    - are you artificial intelligence
    - are you bot
    - are you bot?
    - are you rasa bot?
    - are you real
    - are you real lol
    - are you really a bot
    - are you robot
    - are you sure that you're a bot?
    - bot?
    - cuz you are a bot
    - i guess you are a chatbot
    - oh are you chatbot?
    - tell me, are you a bot?
    - what are you, a bot?
    - you are a robot
    - you are ai
    - you are chatbot
    - you're a bot
    - you robo
    - real bot then?
    - are you really a bbot?
- intent: password_reset
  examples: |
    - need to reset my password
    - need help resetting my password
    - I can't reset my password
    - need help with my password
    - having issues resetting my password
    - freaking password won't work
    - reset my password
    - password reset
    - forgot my password and can't reset it
    - uh password reset help please
    - I'm having so many problems resetting my password
    - Can you reset my password reset
    - My pass word isn't working for me what do I do
    - How can I reconfigure my pass word
    - pass word reset?
    - forgot my pass word again what now
    - password help fix 
    - show me how to set password again
    - can you tell me how to change my password
    - change pass word
    - I forgot my password what can I do
    - forgot my password help me reset it pls
- intent: open_incident
  examples: |
    - I need to open an incident
    - Can you open a case for me
    - I have an issue I need to open a incident for
    - open incident
    - can you help me open a incident please
    - I want to open a new incident
    - I want to open a incident
    - I'm having a weird issue.
    - I'm having a problem
    - I'm having a issue I need to open a incident for.
    - I have a issue I need to open a case for.
    - open an incident for aileen.mottern@example.com
    - open an [urgent]{"entity": "priority", "value": "high"} issue
    - I want to open an incident, but it's [low](priority) priority
    - I need to open a helpdesk ticket. it's [really important]{"entity": "priority", "value": "high"}
    - open an incident
    - open an incedent
    - i want to open an incident
    - i wanna open a incident
    - I want to open a ticket
    - can I open a ticket
    - pls help me open tix support
    - ticket support incident open now can you do that for me
- intent: help
  examples: |
    - I need help
    - what can you help me with
    - can you help me
    - what can you do
    - I need some help
    - help me
    - can you please help me
    - problem
    - big problem
    - How can you help me
    - How you help me?
    - I need some help
    - What are my options
    - What are you able to do?
    - What can I ask you?
    - What can I do?
    - What can you demo
    - What can you do for me?
    - What can you do?
    - What can you tell me?
    - What do you do
    - anything els
    - are there any other options?
    - can I ask you anything else?
    - can you do anything else?
    - can you help me?
    - come back
    - cool! can I do something else here?
    - hello what can you do for me
    - help
    - help me
    - help please
    - help pls
    - help?
    - hep me
- intent: problem_email
  examples: |
    - my outlook application won't open
    - I'm having issues with outlook.
    - issues with outlook
    - outlook application will not open
    - there's a problem with my email
    - something is broken with outlook
    - I can't log in to my email
    - can't log in to outlook
    - my email won't let me log in
    - i have a problem with my email
    - can't get to slack
    - i cant open my email
    - i can't able to login
    - I'm having trouble logging into my email
    - can you help me with me with my email please
    - what can you do about my email it's not working
    - can you fix outlook for me I don't know what to do
    - something isn't right with my e-mail
    - can you help me with my e-mail account
    - my internet mail isn't working
    - outlook is showing me a weird message
    - email isn't loading can you help
- intent: inform
  examples: |
    - my email is test@example.com
    - my email is abraham.lincoln@example.com
    - name.name@example.com
    - it is admin@example.com
    - aileen.mottern@example.com
    - [low](priority)
    - [medium](priority)
    - [high](priority)
    - it should be a [low](priority) priority
    - it should be [medium](priority)
    - [escalated]{"entity": "priority", "value": "high"}
    - [escalate]{"entity": "priority", "value": "high"}
    - [super high](priority)
    - john
    - the priority is [high](priority)
    - test@gmail.com
    - xyz@example.com
    - my email is ella@rasa.com
    - abraham.lincoln@example.com
    - steve
    - sara@rasa.com
    - [urgent]{"entity": "priority", "value": "high"}
    - [critical]{"entity": "priority", "value": "high"}
- intent: out_of_scope
  examples: |
    - what is the square root of 5
    - I want to know the weather
    - what is the meaning of life.
    - Fridge Isn't Running
    - my tv isn't working
    - I want a pizza
    - my washing machine isn't working
    - what year is it
    - order a pizza
    - I want to order a pizza
    - what is the weather today
    - what is the weather
    - how old are you?
    - add two plus two
    - Hdjejene
    - asd
    - title
    - how about the forecast
    - foolish
    - joke
    - Can I get a hamburger?
    - Can YouTube talk?
    - Can you call me back ?
    - Can you give me your datacenter's password
    - Can you give me your datacenter's password?
    - Can you make sandwiches?
    - Can you please send me an uber
- intent: incident_status
  examples: |
    - I want to check if my ticket has been closed
    - what's the status of my incident?
    - Has the incident been closed yet?
    - Is my ticket in progress?
    - What has happened with my open ticket?
    - what's going on with the incident I opened?
    - Do I have any open incidents at the moment?
    - Can I check the status of my open tickets?
    - Can I see the status of the ticket I opened?
    - Check ticket status
    - check incident status
    - ok, try looking it up for abraham.lincoln@example.com
    - look up ticket status for abel.tuter@example.com
    - my ticket status
    - What's the status of the ticket I opened?
    - what's my ticket status
    - how is my ticket going
    - an updates on my incident ticket?
    - I opened an issue last week how is it doing now
    - can you check the status my email is sara@rasa.com
    - has the ticket been closed yet?
    - is my issue still open
- intent: affirm
  examples: |
    - yes
    - yes please
    - yes I would
    - please do
    - yup
    - yep
    - that's right
    - indeed
    - Yes, please
    - yeah
    - sure
    - yes thanks
    - That would be great
    - YES
    - YUP
    - Yea
    - Yeah
    - Yeah sure
    - Yep
    - Yep that's fine
    - Yep!
    - Yepp
    - Yes
    - Yes I do
    - Yes please
    - Yes please!
    - Yes, I accept
- intent: deny
  examples: |
    - no
    - nope
    - no thanks
    - not that one
    - don't use that
    - please no
    - no don't do that
    - no, use abraham.lincoln@example.com
    - Neither
    - Never
    - Nevermind
    - No thank you
    - No, not really.
    - No, thank you
    - No.
    - Nopes
    - Not really
    - absolutely not
    - decline
    - definitely not
    - deny
    - na
    - nah
    - nah I'm good
- intent: human_handoff
  examples: |
    - I want a human
    - can I speak to an agent
    - real agent please
    - real human
    - chat with a live agent
    - give me a person please
    - i want to talk to a human
    - can you transfer me to a human?
    - handoff
    - talk to a human
    - i need to talk to a human
    - human please
    - i need help from a human
    - i need help from a real human
    - give me a human
    - i want to talk to human
    - i want a representative
    - i want to talk to a person
    - I don't wanna talk to a bot
    - I dont like to talk to a machine
    - I want to talk to a human
    - I want to talk to the founders
    - are there also humans working for your company?
    - can I speak to a person?
    - can i please speak to a human?
    - can you forward me to your team
    - can you please connect me to a real rasa employee?
    - can you put me in touch with a human?
    - do you have human support ?
    - gimme a proper human
    - give me a human now
    - human handoff
    - i dont wanna talk to a bot
    - i want to speak to a real person
    - i want to speak to customer service
    - i want to talk to a human
    - i want to talk to a person
    - i want to talk to human
    - i want to talk to someone at rasa
    - i want to talk to someone else
    - i want to talk to someone who is smarter than you
    - i would like to speak to a person
    - i'd rather speak with a real rasa employee
    - id like to talk to a real rasa employee
    - let me speak with a real person please
    - let me talk to a human
    - let me talk to a real person
    - please give me a human
    - service agent
    - someone from customer care
    - speak to a real person
    - talking to a bot is stupid
    - that's annoying I'd like to speak to someone real
    - thats not helping, can i talk to human?
    - wrong i want to speak to a human
    - can i speak to human
    - can i speak to your human
    - i want to chat with human
    - How do I talk to a human
    - talk with a human
    - Can i talk to a human instead
    - nevermind.... you're not human ... I need to talk to a live person
    - Can you get a human to assist me?
    - Can i talk to a human?
    - Can I talk to a human
    - Can I speak to a human?
    - can i speak to a human
    - no, i want to talk to human
    - can you hand a conversation over to a human?
    - can I talk to human?
    - can I talk to human
    - talk to human
    - i want human :(
    - can i talk to human
    - i want to talk to a human
    - i want to speak to human
    - can i talk to a real person?
    - connect me to a real person
    - I need a real person
    - can i took with a real person
    - let me speak to a real person
    - let me speak to a real person please
    - i want to talk to a real person
- synonym: high
  examples: |
    - urgent
    - really important
    - escalated
    - escalate


================================================
FILE: data/rules.yml
================================================
version: "3.0"
rules:
  - rule: handoff intent from other bot's handoff triggers greeting
    steps:
    - intent: handoff
    - action: utter_greet

  - rule: Ask the user to rephrase whenever they send a message with low NLU confidence
    steps:
    - intent: nlu_fallback
    - action: utter_default

  - rule: answer bot challenge
    steps:
    - intent: bot_challenge
    - action: utter_iamabot

  - rule: start open incident form
    steps:
    - or:
      - intent: open_incident
      - intent: password_reset
      - intent: problem_email
    - action: open_incident_form
    - active_loop: open_incident_form

  - rule: submit open incident form
    condition:
      - active_loop: open_incident_form
    steps:
    - action: open_incident_form
    - active_loop: null
    - action: action_open_incident

  - rule: start incident status form
    steps:
    - intent: incident_status
    - action: incident_status_form
    - active_loop: incident_status_form

  - rule: submit incident status form
    condition:
      - active_loop: incident_status_form
    steps:
    - action: incident_status_form
    - active_loop: null
    - action: action_check_incident_status


================================================
FILE: data/stories.yml
================================================
version: "3.0"
stories:

- story: answer out of scope
  steps:
  - intent: out_of_scope
  - action: utter_out_of_scope

- story: give help
  steps:
  - intent: help
  - action: utter_help

- story: thank
  steps:
  - intent: thank
  - action: utter_welcome

- story: greet + give info
  steps:
  - intent: greet
  - action: utter_greet
  - action: utter_help

- story: say goodbye
  steps:
  - intent: goodbye
  - action: utter_goodbye

- story: open incident form interrupted
  steps:
  - or:
    - intent: open_incident
    - intent: password_reset
    - intent: problem_email
  - action: open_incident_form
  - active_loop: open_incident_form
  - intent: help
  - action: utter_help
  - action: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: open incident form interrupted
  steps:
  - or:
    - intent: open_incident
    - intent: password_reset
    - intent: problem_email
  - action: open_incident_form
  - active_loop: open_incident_form
  - intent: out_of_scope
  - action: utter_out_of_scope
  - action: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: incident status form interrupted
  steps:
  - intent: incident_status
  - action: incident_status_form
  - active_loop: incident_status_form
  - intent: help
  - action: utter_help
  - action: incident_status_form
  - active_loop: null
  - action: action_check_incident_status

- story: incident status form interrupted
  steps:
  - intent: incident_status
  - action: incident_status_form
  - active_loop: incident_status_form
  - intent: out_of_scope
  - action: utter_out_of_scope
  - action: incident_status_form
  - active_loop: null
  - action: action_check_incident_status

- story: incident status form switch to open incident
  steps:
  - intent: incident_status
  - action: incident_status_form
  - active_loop: incident_status_form
  - or:
    - intent: open_incident
    - intent: password_reset
    - intent: problem_email
  - action: open_incident_form
  - active_loop: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: open incident form switch to incident status form
  steps:
  - or:
    - intent: open_incident
    - intent: password_reset
    - intent: problem_email
  - action: open_incident_form
  - active_loop: open_incident_form
  - intent: incident_status
  - action: incident_status_form
  - active_loop: incident_status_form
  - active_loop: null
  - action: action_check_incident_status


================================================
FILE: domain.yml
================================================
version: '3.0'
session_config:
  session_expiration_time: 0.0
  carry_over_slots_to_new_session: true
intents:
- greet
- goodbye
- bot_challenge
- password_reset
- inform
- thank
- help
- problem_email
- open_incident:
    use_entities: []
- incident_status
- out_of_scope
- restart
- affirm
- deny
- trigger_handoff
- human_handoff
- handoff
entities:
- email
- priority
- handoff_to
slots:
  confirm:
    type: bool
    influence_conversation: false
    mappings:
    - type: from_intent
      intent: affirm
      value: true
      conditions:
      - active_loop: open_incident_form
        requested_slot: confirm
    - type: from_intent
      intent: deny
      value: false
      conditions:
      - active_loop: open_incident_form
        requested_slot: confirm
  previous_email:
    type: text
    influence_conversation: false
    mappings:
    - type: custom
  caller_id:
    type: text
    influence_conversation: false
    mappings:
    - type: custom
  email:
    type: text
    influence_conversation: false
    mappings:
    - type: from_entity
      entity: email
      conditions:
      - active_loop: open_incident_form
        requested_slot: email
      - active_loop: incident_status_form
        requested_slot: email
    - type: from_intent
      intent: affirm
      value: true
      conditions:
      - active_loop: open_incident_form
        requested_slot: email
      - active_loop: incident_status_form
        requested_slot: email
    - type: from_intent
      intent: deny
      value: false
      conditions:
      - active_loop: open_incident_form
        requested_slot: email
      - active_loop: incident_status_form
        requested_slot: email
    - type: from_entity
      entity: email
  incident_title:
    type: text
    influence_conversation: false
    mappings:
    - type: from_trigger_intent
      intent: password_reset
      value: Problem resetting password
      conditions:
      - active_loop: open_incident_form
        requested_slot: incident_title
    - type: from_trigger_intent
      intent: problem_email
      value: Problem with email
      conditions:
      - active_loop: open_incident_form
        requested_slot: incident_title
    - type: from_text
      not_intent:
      - incident_status
      - bot_challenge
      - help
      - affirm
      - deny
      conditions:
      - active_loop: open_incident_form
        requested_slot: incident_title
  priority:
    type: text
    influence_conversation: false
    mappings:
    - type: from_entity
      entity: priority
      conditions:
      - active_loop: open_incident_form
    - type: from_entity
      entity: priority
  problem_description:
    type: text
    influence_conversation: false
    mappings:
    - type: from_text
      not_intent:
      - incident_status
      - bot_challenge
      - help
      - affirm
      - deny
      conditions:
      - active_loop: open_incident_form
        requested_slot: problem_description
  requested_slot:
    type: text
    influence_conversation: false
    mappings:
    - type: custom
  handoff_to:
    type: text
    influence_conversation: false
    mappings:
    - type: from_entity
      entity: handoff_to
responses:
  utter_out_of_scope:
  - text: Sorry, I'm not sure how to respond to that. Type "help" for assistance.
  utter_greet:
  - text: Hallo! I'm your IT Helpdesk Assistant.
  utter_goodbye:
  - text: Goodbye!
  utter_iamabot:
  - text: I am a bot, powered by Rasa.
  utter_ask_email:
  - text: What is your email address?
  utter_ask_incident_title:
  - text: What should we use for the title of this incident?
  utter_ask_problem_description:
  - text: What is the problem description for the issue?
  utter_ask_priority:
  - text: What is the priority of this issue?
    buttons:
    - payload: /inform{"priority":"low"}
      title: low
    - payload: /inform{"priority":"medium"}
      title: medium
    - payload: /inform{"priority":"high"}
      title: high
  utter_no_priority:
  - text: Sorry "{priority}" is not a valid priority. Please try again.
  utter_no_email:
  - text: Sorry, "{email}" isn't in our incident management system. Please try again.
  utter_help:
  - text: "I can help you open a service request ticket, or check the status of your open incidents. \nYou can ask me things like: \n- Open an incident \n- Help me reset my password \n- I'm having a issue with my email \n- What's the status of the ticket I opened?"
  utter_welcome:
  - text: You're welcome!
  utter_default:
  - text: I didn't quite understand that. Could you rephrase?
  utter_ask_use_previous_email:
  - text: Would you like to use the last email address you used, {previous_email}?
  utter_ask_confirm:
  - text: "Should I open an incident with the following details? \n    email: {email} \n    problem description: {problem_description} \n    title: {incident_title} \n    priority: {priority}"
    buttons:
    - title: Yes
      payload: /affirm
    - title: No, cancel the incident
      payload: /deny
  utter_incident_creation_canceled:
  - text: Alright, I have cancelled the incident.
  utter_ask_whatelse:
  - text: What else can I help you with?
  utter_ask_handoff:
  - text: It looks like you want to be transferred to a human agent.
  utter_handoff:
  - text: Alright, I'll try to transfer you.
  utter_wouldve_handed_off:
  - text: If you were talking to me via chatroom, I would have handed you off to {handoffhost}.
  utter_no_handoff:
  - text: Since you haven't configured a host to hand off to, I can't send you anywhere!
forms:
  open_incident_form:
    ignored_intents: []
    required_slots:
    - email
    - priority
    - problem_description
    - incident_title
    - confirm
  incident_status_form:
    ignored_intents: []
    required_slots:
    - email
actions:
- action_ask_email
- action_check_incident_status
- action_handoff
- action_handoff_options
- action_open_incident
- validate_open_incident_form
- validate_incident_status_form


================================================
FILE: endpoints.yml
================================================
# This file contains the different endpoints your bot can use.

# Server where the models are pulled from.
# https://rasa.com/docs/rasa/user-guide/running-the-server/#fetching-models-from-a-server/

#models:
#  url: http://my-server.com/models/default_core@latest
#  wait_time_between_pulls:  10   # [optional](default: 100)

# Server which runs your custom actions.
# https://rasa.com/docs/rasa/core/actions/#custom-actions/

action_endpoint:
 url: "http://localhost:5055/webhook"

# Tracker store which is used to store the conversations.
# By default the conversations are stored in memory.
# https://rasa.com/docs/rasa/api/tracker-stores/

#tracker_store:
#    type: redis
#    url: <host of the redis instance, e.g. localhost>
#    port: <port of your redis instance, usually 6379>
#    db: <number of your database within redis, e.g. 0>
#    password: <password used for authentication>
#    use_ssl: <whether or not the communication is encrypted, default false>

#tracker_store:
#    type: mongod
#    url: <url to your mongo instance, e.g. mongodb://localhost:27017>
#    db: <name of the db within your mongo instance, e.g. rasa>
#    username: <username used for authentication>
#    password: <password used for authentication>

# Event broker which all conversation events should be streamed to.
# https://rasa.com/docs/rasa/api/event-brokers/

#event_broker:
#  url: localhost
#  username: username
#  password: password
#  queue: queue


================================================
FILE: format_results.py
================================================
from pytablewriter import MarkdownTableWriter
import json


def intent_table():
    writer = MarkdownTableWriter()
    writer.table_name = "Intent Cross-Validation Results (5 folds)"

    with open("results/intent_report.json", "r") as f:
        data = json.loads(f.read())

    cols = ["support", "f1-score", "confused_with"]
    writer.headers = ["class"] + cols

    classes = list(data.keys())
    try:
        classes.remove("accuracy")
    except:
        pass
    classes.sort(key=lambda x: data[x]["support"], reverse=True)

    def format_cell(data, c, k):
        if not data[c].get(k):
            return "N/A"
        if k == "confused_with":
            return ", ".join([f"{k}({v})" for k, v in data[c][k].items()])
        else:
            return data[c][k]

    writer.value_matrix = [
        [c] + [format_cell(data, c, k) for k in cols] for c in classes
    ]

    return writer.dumps()


def entity_table():

    writer = MarkdownTableWriter()
    writer.table_name = "Entity Cross-Validation Results (5 folds)"

    with open("results/DIETClassifier_report.json", "r") as f:
        data = json.loads(f.read())

    cols = ["support", "f1-score", "precision", "recall"]
    writer.headers = ["entity"] + cols

    classes = list(data.keys())
    classes.sort(key=lambda x: data[x]["support"], reverse=True)

    def format_cell(data, c, k):
        if not data[c].get(k):
            return "N/A"
        else:
            return data[c][k]

    writer.value_matrix = [
        [c] + [format_cell(data, c, k) for k in cols] for c in classes
    ]

    return writer.dumps()


intents = intent_table()
entities = entity_table()

with open("results.md", "w") as f:
    f.write(intents)
    f.write("\n\n\n")
    f.write(entities)


================================================
FILE: pyproject.toml
================================================
[tool.black]
line-length = 79
include = '\.pyi?$'
exclude = '''
/(
    \.git
  | \.hg
  | \.mypy_cache
  | \.tox
  | \.venv
  | _build
  | buck-out
  | build
  | dist
)/
'''


================================================
FILE: requirements-dev.txt
================================================
-r requirements.txt

# lint/format/types
black==19.10b0
flake8==3.7.8
pytype==2019.7.11
pre-commit

================================================
FILE: requirements.txt
================================================
rasa~=3.0.0
rasa-sdk~=3.0.0  # if you change this, make sure to change the Dockerfile to match
-r actions/requirements-actions.txt


================================================
FILE: tests/test_conversations.yml
================================================
version: "2.0"
stories:
- story: bot challenge
  steps:
  - intent: bot_challenge
    user: |-
      Are you a bot?
  - action: utter_iamabot

- story: out of scope
  steps:
  - intent: out_of_scope
    user: |-
      I want a pizza
  - action: utter_out_of_scope

- story: open incident
  steps:
  - intent: greet
    user: |-
      hello
  - action: utter_greet
  - action: utter_help
  - intent: open_incident
    user: |-
      I need to open an incident
  - action: open_incident_form
  - active_loop: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: open password reset incident
  steps:
  - intent: password_reset
    user: |-
      I'm having issues with my password
  - action: open_incident_form
  - active_loop: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: email incident
  steps:
  - intent: greet
    user: |-
      hello
  - action: utter_greet
  - action: utter_help
  - intent: problem_email
    user: |-
      I have a problem with my email
  - action: open_incident_form
  - active_loop: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: interrupted
  steps:
  - intent: problem_email
    user: |-
      I have a problem with my email
  - action: open_incident_form
  - active_loop: open_incident_form
  - intent: help
  - action: utter_help
  - action: open_incident_form
  - active_loop: null
  - action: action_open_incident

- story: greet + thank
  steps:
  - intent: greet
    user: |-
      Hey there
  - action: utter_greet
  - action: utter_help
  - intent: thank
    user: |-
      Awesome, thanks!
  - action: utter_welcome

- story: user requests a human handoff
  steps:
  - intent: human_handoff
    user: |-
      I want to talk to a person now
  - action: utter_ask_handoff
  - action: action_handoff_options
  - intent: deny
    user: |-
      uh no
  - action: utter_ask_whatelse

- story: User says something out of scope
  steps:
  - intent: out_of_scope
    user: |-
      Can you get me a pizza
  - action: utter_out_of_scope

- story: trigger handoff
  steps:
  - intent: human_handoff
    user: |-
      give me a human
  - action: utter_ask_handoff
  - action: action_handoff_options
  - intent: trigger_handoff
  - action: action_handoff

- story: Test fallback
  steps:
  - intent: nlu_fallback
  - action: utter_default

- story: incident status happy path 
  steps:
  - intent: greet
    user: |-
      Hey there
  - action: utter_greet
  - action: utter_help
  - intent: incident_status
    user: |-
      Can you check the status of my incident
  - action: incident_status_form
  - active_loop: incident_status_form
  - active_loop: null
  - action: action_check_incident_status

Download .txt
gitextract_o7a7u4a9/

├── .github/
│   └── workflows/
│       ├── continuous-integration.yml
│       ├── lint_and_test.yml
│       └── lint_and_test_pr.yml
├── .gitignore
├── .pre-commit-config.yaml
├── Dockerfile.chatroom
├── LICENSE
├── Makefile
├── README.md
├── actions/
│   ├── __init__.py
│   ├── actions.py
│   ├── handoff.py
│   ├── handoff_config.yml
│   ├── requirements-actions.txt
│   ├── snow.py
│   └── snow_credentials.yml
├── chatroom_handoff.html
├── config.yml
├── data/
│   ├── handoff.yml
│   ├── nlu.yml
│   ├── rules.yml
│   └── stories.yml
├── domain.yml
├── endpoints.yml
├── format_results.py
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
└── tests/
    └── test_conversations.yml
Download .txt
SYMBOL INDEX (32 symbols across 4 files)

FILE: actions/actions.py
  class ActionAskEmail (line 20) | class ActionAskEmail(Action):
    method name (line 21) | def name(self) -> Text:
    method run (line 24) | def run(
  function _validate_email (line 37) | def _validate_email(
  class ValidateOpenIncidentForm (line 65) | class ValidateOpenIncidentForm(FormValidationAction):
    method name (line 66) | def name(self) -> Text:
    method validate_email (line 69) | def validate_email(
    method validate_priority (line 79) | def validate_priority(
  class ActionOpenIncident (line 95) | class ActionOpenIncident(Action):
    method name (line 96) | def name(self) -> Text:
    method run (line 99) | def run(
  class IncidentStatusForm (line 154) | class IncidentStatusForm(FormValidationAction):
    method name (line 155) | def name(self) -> Text:
    method validate_email (line 158) | def validate_email(
  class ActionCheckIncidentStatus (line 169) | class ActionCheckIncidentStatus(Action):
    method name (line 170) | def name(self) -> Text:
    method run (line 173) | def run(

FILE: actions/handoff.py
  class ActionHandoffOptions (line 15) | class ActionHandoffOptions(Action):
    method name (line 16) | def name(self) -> Text:
    method run (line 19) | async def run(
  class ActionHandoff (line 46) | class ActionHandoff(Action):
    method name (line 47) | def name(self) -> Text:
    method run (line 50) | async def run(

FILE: actions/snow.py
  class SnowAPI (line 18) | class SnowAPI(object):
    method __init__ (line 21) | def __init__(self):
    method handle_request (line 32) | def handle_request(
    method email_to_sysid (line 54) | def email_to_sysid(self, email) -> Dict[Text, Any]:
    method retrieve_incidents (line 77) | def retrieve_incidents(self, email) -> Dict[Text, Any]:
    method create_incident (line 101) | def create_incident(
    method priority_db (line 126) | def priority_db() -> Dict[str, int]:

FILE: format_results.py
  function intent_table (line 5) | def intent_table():
  function entity_table (line 37) | def entity_table():
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (83K chars).
[
  {
    "path": ".github/workflows/continuous-integration.yml",
    "chars": 831,
    "preview": "name: Continuous Integration\non:\n  push:\n    branches:\n    - main\n    paths:\n    - 'actions/**'\n    - '.github/workflows"
  },
  {
    "path": ".github/workflows/lint_and_test.yml",
    "chars": 2084,
    "preview": "name: Lint and Test\non:\n  push:\n    branches:\n      - main\n    paths-ignore:\n    - \"README.md\"\n    - \"Makefile\"\n    - \"D"
  },
  {
    "path": ".github/workflows/lint_and_test_pr.yml",
    "chars": 2068,
    "preview": "name: Lint and Test PR\non:\n  pull_request:\n    paths-ignore:\n    - \"README.md\"\n    - \"Makefile\"\n    - \"Dockerfile\"\n\njobs"
  },
  {
    "path": ".gitignore",
    "chars": 179,
    "preview": "# Standard Ignores\nvenv\n__pycache__/\n.DS_Store\n.vscode/\n.history/\n.pytype/\n\n# Rasa Ignores\ncredentials.yml\n*.db-*\nrasa.d"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 90,
    "preview": "repos:\n-   repo: https://github.com/ambv/black\n    rev: stable\n    hooks:\n    - id: black\n"
  },
  {
    "path": "Dockerfile.chatroom",
    "chars": 404,
    "preview": "# docker build -t chatroom -f Dockerfile.chatroom\n# docker run --name chatroom -p 8080:3000 -d chatroom\nFROM node:14\n\nRU"
  },
  {
    "path": "LICENSE",
    "chars": 11352,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "Makefile",
    "chars": 1232,
    "preview": "help:\n\t@echo \"make\"\n\t@echo \"    clean\"\n\t@echo \"        Remove Python/build artifacts.\"\n\t@echo \"    formatter\"\n\t@echo \"  "
  },
  {
    "path": "README.md",
    "chars": 14792,
    "preview": "# Rasa Helpdesk Assistant Example\n\nThis is a Rasa chatbot example demonstrating how to build an AI assistant for an IT H"
  },
  {
    "path": "actions/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "actions/actions.py",
    "chars": 6858,
    "preview": "import logging\nfrom typing import Dict, Text, Any, List\nfrom rasa_sdk import Tracker\nfrom rasa_sdk.executor import Colle"
  },
  {
    "path": "actions/handoff.py",
    "chars": 2317,
    "preview": "from rasa_sdk import Tracker, Action\nfrom rasa_sdk.executor import CollectingDispatcher\n\nimport ruamel.yaml\nimport pathl"
  },
  {
    "path": "actions/handoff_config.yml",
    "chars": 237,
    "preview": "handoff_hosts:\n    financial_demo:\n      title: \"Financial Assistant\"\n      url: \"http://localhost:5006\"\n    ## you can "
  },
  {
    "path": "actions/requirements-actions.txt",
    "chars": 34,
    "preview": "pytablewriter\nrequests\nruamel.yaml"
  },
  {
    "path": "actions/snow.py",
    "chars": 4567,
    "preview": "import logging\nimport requests\nimport json\nimport pathlib\nimport ruamel.yaml\nfrom typing import Dict, Text, Any\n\nlogger "
  },
  {
    "path": "actions/snow_credentials.yml",
    "chars": 112,
    "preview": "# snow_instance: \"dev97377.service-now.com\"\n# snow_user: \"admin\"\n# snow_pw: \"mySnowinstance1\"\n# localmode: false"
  },
  {
    "path": "chatroom_handoff.html",
    "chars": 751,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-wid"
  },
  {
    "path": "config.yml",
    "chars": 741,
    "preview": "language: en\npipeline:\n  - name: WhitespaceTokenizer\n  - name: RegexFeaturizer\n  - name: LexicalSyntacticFeaturizer\n  - "
  },
  {
    "path": "data/handoff.yml",
    "chars": 587,
    "preview": "version: \"3.0\"\nstories:\n- story: handoff\n  steps:\n  - intent: human_handoff\n  - action: utter_ask_handoff\n  - action: ac"
  },
  {
    "path": "data/nlu.yml",
    "chars": 12706,
    "preview": "version: \"3.0\"\nnlu:\n- intent: greet\n  examples: |\n    - hey\n    - hello\n    - hi\n    - good morning\n    - good evening\n "
  },
  {
    "path": "data/rules.yml",
    "chars": 1180,
    "preview": "version: \"3.0\"\nrules:\n  - rule: handoff intent from other bot's handoff triggers greeting\n    steps:\n    - intent: hando"
  },
  {
    "path": "data/stories.yml",
    "chars": 2483,
    "preview": "version: \"3.0\"\nstories:\n\n- story: answer out of scope\n  steps:\n  - intent: out_of_scope\n  - action: utter_out_of_scope\n\n"
  },
  {
    "path": "domain.yml",
    "chars": 5961,
    "preview": "version: '3.0'\nsession_config:\n  session_expiration_time: 0.0\n  carry_over_slots_to_new_session: true\nintents:\n- greet\n-"
  },
  {
    "path": "endpoints.yml",
    "chars": 1451,
    "preview": "# This file contains the different endpoints your bot can use.\n\n# Server where the models are pulled from.\n# https://ras"
  },
  {
    "path": "format_results.py",
    "chars": 1751,
    "preview": "from pytablewriter import MarkdownTableWriter\nimport json\n\n\ndef intent_table():\n    writer = MarkdownTableWriter()\n    w"
  },
  {
    "path": "pyproject.toml",
    "chars": 174,
    "preview": "[tool.black]\nline-length = 79\ninclude = '\\.pyi?$'\nexclude = '''\n/(\n    \\.git\n  | \\.hg\n  | \\.mypy_cache\n  | \\.tox\n  | \\.v"
  },
  {
    "path": "requirements-dev.txt",
    "chars": 98,
    "preview": "-r requirements.txt\n\n# lint/format/types\nblack==19.10b0\nflake8==3.7.8\npytype==2019.7.11\npre-commit"
  },
  {
    "path": "requirements.txt",
    "chars": 131,
    "preview": "rasa~=3.0.0\nrasa-sdk~=3.0.0  # if you change this, make sure to change the Dockerfile to match\n-r actions/requirements-a"
  },
  {
    "path": "tests/test_conversations.yml",
    "chars": 2737,
    "preview": "version: \"2.0\"\nstories:\n- story: bot challenge\n  steps:\n  - intent: bot_challenge\n    user: |-\n      Are you a bot?\n  - "
  }
]

About this extraction

This page contains the full source code of the RasaHQ/helpdesk-assistant GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (76.1 KB), approximately 19.7k tokens, and a symbol index with 32 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!