dockernginxsocket.iorasa

Troubleshooting deployed assistant's missing responses


I've just deployed a (veeery) minimally viable virtual assistant (explorer-ai.chat) using Rasa Open-Source. It's currently running as a simple web app from a network of Docker containers built on an E2 machine (Ubuntu 18.04) hosted by Google Cloud.

Background that will help you interact with the chatbot: It's intended to function as a virtual docent to the Exploratorium in San Francisco. In particular, it should be able to answer basic questions that a human docent might be asked by a patron. The user intents fall into these categories:

There are lots of issues with both NLU and dialog management that I'm working on fixing, but the purpose of this post is to understand behavior illustrated here:

conversation snippet

Specifically, the bot often fails to respond to a user utterance at some point in the conversation. Here are some items that might be relevant:

Here's docker-compose.yml for the network:

version: '3'
services:

  rasa_server:
    container_name: "rasa_server"
    build:
      context: backend
    user: root
    ports:
      - "5005:5005"
    expose:
      - 5005

  action_server:
    container_name: "action_server"
    build:
      context: actions
    ports:
      - "5055:5055"
    expose:
      - 5055

  web_server:
    container_name: "web_server"
    image: nginx
    depends_on:
      - "rasa_server"
      - "action_server"
    ports:
      - "80:80"
      - "443:443"
    restart: always
    volumes:
      - ./web/conf/default.conf:/etc/nginx/conf.d/default.conf
      - ./web/conf/explorer-ai.chat.conf:/etc/nginx/conf.d/explorer-ai.chat.conf
      - ./web/conf/nginx.conf:/etc/nginx/nginx.conf
      - ./web/html:/var/www/html
      - ./certbot/www:/var/www/certbot
      - ./certbot/conf:/etc/letsencrypt

  certbot:
    container_name: "certbot"
    image: certbot/certbot
    volumes:
      - ./certbot/www:/var/www/cerbot:rw
      - ./certbot/conf:/etc/letsencrypt:rw

Dockerfile for backend:

FROM rasa/rasa:3.3.1

WORKDIR /app

USER root

COPY ./models /app/models
COPY ./data /app/data
COPY . /app

RUN rasa train

ENTRYPOINT ["rasa", "run", "-m", "models", "--enable-api", "--cors", "*", "--debug"]

Dockerfile for actions (in case it's useful):

FROM rasa/rasa-sdk:3.3.0

WORKDIR /app

COPY ./requirements-actions.txt requirements.txt
COPY . /app

USER root

RUN pip install --no-cache-dir -r requirements.txt

USER 1001

EXPOSE 5055

ENTRYPOINT ["python", "-m", "rasa_sdk", "--actions", "actions"]

My Rasa (backend) credentials.yml file includes

socketio:
  user_message_evt: user_uttered
  bot_message_evt: bot_uttered
  session_persistence: false

My index.html file contains:

<script
  src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.2/socket.io.js"
  integrity="sha512-YybopSVjZU0fe8TY4YDuQbP5bhwpGBE/T6eBUEZ0usM72IWBfWrgVI13qfX4V2A/W7Hdqnm7PIOYOwP9YHnICw=="
  crossorigin="anonymous" referrerpolicy="no-referrer">
</script>
<script>
  const socket = io("https://explorer-ai.chat");

  const messages = document.getElementById('messages');
  const form = document.getElementById('form');
  const messageInput = document.getElementById('message-input');

  function scrollToBottom() {
    window.scrollTo(0, document.body.scrollHeight);
  }

  function appendMessage(msg, type) {
    const item = document.createElement('div');
    item.textContent = msg;
    item.classList.add("message");
    item.classList.add(`message_${type}`);
    messages.appendChild(item);
    scrollToBottom();
  }

  const welcome = "Hello! I'm Explorer AI, a virtual assistant for visitors to the " +
    "Exploratorium in San Francisco. I've been trained to answer questions about the " + 
    "Exploratorium's exhibits and the concepts they illustrate.";
  const how_help = "How may I assist you?";

  appendMessage(welcome, "received");
  appendMessage(how_help, "received");

  form.addEventListener('submit', function (e) {
    e.preventDefault();
    const msg = messageInput.value;
    if (msg) {
      socket.emit('user_uttered', {
        "message": msg,
      });
      messageInput.value = '';

      appendMessage(msg, "sent");
    }
  });

  socket.on('connect', function () {
    console.log("Connected to Socket.io server");
  });

  socket.on('connect_error', (error) => {
            
    console.log("Connection to Socket.io FAILED");
    console.error(error);
  });

  socket.on('bot_uttered', function (response) {
    console.log("Bot uttered:", response);
    if (response.text) {
      appendMessage(response.text, "received");
    }
    if (response.attachment) {
      appendImage(response.attachment.payload.src, "received");
    }
    if (response.quick_replies) {
      appendQuickReplies(response.quick_replies);
    }
  });

</script>

And here are my web_server configurations:

server {

    listen 80;
    listen [::]:80;
    server_name explorer-ai.chat www.explorer-ai.chat;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://explorer-ai.chat$request_uri;
    }

} 

server {

    listen 443 ssl;
    listen [::]:443 ssl;
    server_name explorer-ai.chat www.explorer-ai.chat;

    ssl_certificate         /etc/letsencrypt/live/explorer-ai.chat/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/explorer-ai.chat/privkey.pem;
    include                 /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam             /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/html;
    index index.html;

    location /socket.io/ {
        proxy_pass http://rasa_server:5005;

    }

}

Here is a section of the log corresponding to the very beginning of the conversation above:

log snippet #1

It seems that the correct action is predicted and a BotUttered event was triggered. But there's no action from the web server during this time. (Also, why do all of the logged events have the same timestamp?) In fact, the next log entries from the web server are these, which occur before the user's next message.

log snippet #2

Am I right in thinking this has something to do with the networking (Nginx, socket.io), and not the Rasa components?

Thanks so much for reading, and for any help you can provide! In case it's not obvious, I'm a noob. :)


Solution

  • The issue was that I didn't have the proxy configured correctly. The /socket.io/ location needed some additional directives. This ended up working:

    location /socket.io/ {
        proxy_pass          http://rasa_server:5005;
        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
    }
    

    and, in order to avoid an unknown "connection_upgrade" variable error, I also needed to add the following directive to the http block my nginx.conf file:

    map $http_upgrade $connection_upgrade {  
        default   upgrade;
        ''        close;
    }
    

    I think that, previously, the proxy connection was falling back to HTTP long polling (the default for socket.io).