Eliza Example

This tutorial is to help one to understand how visualization works together with the pipeline. We will walk you through the steps to set up an Eliza chatbot that generates responses based on Eliza rules. We will utilize stave to visualize the dialogue page that allows you to chat with the bot interactively.

Introduction

Eliza chatbot (https://en.wikipedia.org/wiki/ELIZA) is a famous rule-based chatbot invented in 1964. The rule-based and model-less nature makes it highly suitable for demonstration purposes. This tutorial is based on ElizaProcessor. For more details, refer to https://github.com/asyml/forte/blob/master/forte/processors/nlp/eliza_processor.py.

Quick Start

Install Dependencies

[ ]:
!pip install torch
!pip install "forte[remote]@git+https://github.com/asyml/forte.git@master"
!pip install stave==0.0.2.dev1

Start an Eliza pipeline service

Run the following python script to start a pipeline service to process input queries based on Eliza rules.

[ ]:
from threading import Thread
from forte.pipeline import Pipeline
from forte.data.data_pack import DataPack
from forte.data.readers import RawDataDeserializeReader
from forte.processors.nlp import ElizaProcessor


def start_eliza_service(
    input_format: str = "DataPack", service_name: str = "test_name"
) -> None:
    """
    Start a remote service for ElizaProcessor
    """
    pipeline = Pipeline[DataPack]()
    pipeline.set_reader(RawDataDeserializeReader())
    pipeline.add(ElizaProcessor())
    pipeline.serve(input_format=input_format, service_name=service_name)


if __name__ == "__main__":
    Thread(target=start_eliza_service).start()

Visualize the chatbot in stave

We will need stave to open a chatbot window to test the pipeline service that we just started in the previous step. stave is a fast, lightweight, extensible web-based text annotation and visualization tool. It supports a wide range of data types and NLP tasks. For more details, refer to https://github.com/asyml/stave.

Run the following command:

[ ]:
!stave -s start -o -l -n 8889

Now we should see a browser window popped out which directs to a login page (http://localhost:8889/login) of stave. You can log in with the default username (admin) and password (admin).

After successfully logging into stave, we can navigate to the All Projects page. Click the VIEW PROJECT button under Eliza project. Then click eliza.json on the left side to enter the dialogue page where you can enter queries and get responses from the Eliza chatbot.

Code Explained

Overview

The Eliza example showcases forte’s ability to expose a pipeline to be served as a remote service that can be called from another pipeline using RemoteProcessor. This allows users to port their local pipeline to a remote endpoint that can be accessed and shared by other users to call its functionality. We will use the Eliza example to show how we can achieve this.

Start a Pipeline Service

In the code snippet above we build a simple pipeline and start it as a service for Eliza chatbot:

pipeline = Pipeline[DataPack]()
pipeline.set_reader(RawDataDeserializeReader())
pipeline.add(ElizaProcessor())
pipeline.serve()

Here we set RawDataDeserializeReader as a reader of the pipeline since we expect the input request will be a sequence of serialized DataPack strings. This reader can deserialize these strings to DataPacks.

ElizaProcessor is responsible for generating responses based on Eliza rules from the input queries. The responses will be appended to the text payload of input DataPack annotated as Utterance.

Pipeline.serve(host, port) will start a pipeline service at a specific endpoint. You may configure host and port to specify the endpoint.

Call a Pipeline Service

After setting up the service, you will be able to access it from http://{host}:{port}. You can also call it from another forte pipeline with RemoteProcessor. In the pre-loaded Eliza project, stave has already set up a forte pipeline to invoke the Eliza service:

# Adapted from https://github.com/asyml/stave/blob/master/simple-backend/stave_backend/handlers/nlp.py#L49
pipeline = Pipeline[DataPack](do_init_type_check)
pipeline.set_reader(RawDataDeserializeReader())
pipeline.add(RemoteProcessor(), config)
pipeline.initialize()

RawDataDeserializeReader is set as the reader of this pipeline because stave stores DataPacks as serialized strings in the database. Input queries entered by users are integrated into an existing chatbot DataPack and saved to the database, which will be fed into the pipeline above.

RemoteProcessor provides a wrapping of interactions with the remote forte service endpoint. Each input DataPack from the upstream component will be serialized and packed into a POST request to be sent to a remote service, which should return a response that can be parsed into a DataPack to update the input. In the Eliza example, it will 1. prepare a POST request from the deserialized DataPack (which contains the user inputs) 2. send it to the remote service we just set up 3. parse the response (which contains the generated text from ElizaProcessor) into a new DataPack that can be passed to the downstream components.

Create Your Own Chatbot Service

You might notice that the pipeline of Eliza project in stave actually doesn’t constrain the remote service to be an Eliza chatbot. It should be able to support any type of chatbot service as long as the service can process input DataPacks in a way that stave can understand.

To render a chatbot page, stave will retrieve the Utterance annotations stored in DataPack and lay out the dialogues based on Utterance.speaker. If speaker of an Utterance is "ai" (e.g., the initial prompt message and chatbot responses), then its message will be placed on the left side of the page. For messages users enter, stave will append them to DataPack as new Utterance annotations with their speaker field set to "user" and place them on the right side of the chatbot window.

The chatbot service must conform to the protocol described above to display its response in stave correctly. This service will receive a serialized DataPack. Hence it always needs to set RawDataDeserializeReader as the reader. The downstream processor should retrieve the Utterance annotations from it. The processor can choose to analyze the whole chat history or generate a response simply based on the latest query from the user. It must append its responses to DataPack and annotate them as Utterances whose speaker should be set to "ai".