Eliza Example

This tutorial is to help one to understand how visualization work together with the pipeline. We will walk you through the steps to set up an Eliza chatbot which 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 extremely 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 a 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 for us 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 designed to support 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 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 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 is able to 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 database. Input queries entered by users are integrated into an existing chatbot DataPack and saved to database, which will be fed into the pipeline above.

RemoteProcessor provides a wrapping of interactions with 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 prepare a POST request from the deserialized DataPack (which contains the user inputs), and send it to the remote service we just set up, and then 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 a Eliza chatbot. It should be able to support any types 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 at the left side of the page. For messages that are entered by users, stave will append them to DataPack as new Utterance annotations with their speaker field set to "user" and place them to the right side of chatbot window.

The chatbot service must conform to the protocol described above in order to correctly display its response in stave. 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 response simply based on the latest query from user. It must append its responses to DataPack and annotate them as Utterances whose speaker should be set to "ai".