Day 22 of the Anvil Advent Calendar

Build a web app every day until Christmas, with nothing but Python!

Build a Chatbot UI

Did you know that there are some really good language models these days? And that quite a few are now open-source, and there are even APIs like HuggingFace that will host them for you?

Well, today, we’re going to use them to create a chatbot! Here’s the live app – try it yourself below:

You can see the source code here:

How it works

The Hugging Face API docs lay this one out nicely for us. I’ll just quote from the example in the DialoGPT model documentation (under Deploy):

import requests

API_URL = "https://api-inference.huggingface.co/models/microsoft/DialoGPT-large"
headers = {"Authorization": "Bearer {API_TOKEN}"}

def query(payload):
	response = requests.post(API_URL, headers=headers, json=payload)
	return response.json()

output = query({
    "inputs": {
		"past_user_inputs": ["Which movie is the best ?"],
		"generated_responses": ["It's Die Hard for sure."],
		"text": "Can you explain why ?",
	},
})

And here’s the sort of thing it produces if we try it from a REPL:

{
    'generated_text': "It's the best movie ever.",
    'conversation': {
        'generated_responses': ["It's Die Hard for sure.", "It's the best movie ever."],
        'past_user_inputs': ['Which movie is the best ?', 'Can you explain why ?']
    }
}

We just need to turn that into an Anvil Server Function, so we can drive it from a Python UI. We’ll use the session state to store previous lines of dialog:

import requests

API_URL = "https://api-inference.huggingface.co/models/microsoft/DialoGPT-large"
headers = {"Authorization": f"Bearer {anvil.secrets.get_secret('api_token')}"}

def query(payload):
	response = requests.post(API_URL, headers=headers, json=payload)
	return response.json()

@anvil.server.callable
def get_conversation():
    if "conversation" not in anvil.server.session:
        anvil.server.session["conversation"] = {
            "past_user_inputs": ["Merry Christmas!"],
            "generated_responses": ["And a Merry Christmas to you!"]
        }
    return anvil.server.session["conversation"]


@anvil.server.callable
def send_message(message):
    conversation = get_conversation()
    model_output = query({
        "inputs": {
            "text": message,
            **conversation
        }
    })
    anvil.server.session["conversation"] = model_output["conversation"]
    return model_output["conversation"]

Now it’s just time to build a user interface!

We’ll use a RepeatingPanel to display the conversation. The easiest way to drive a RepeatingPanel is a list of dictionaries, so we’ll massage our conversation object into the right shape:

    def refresh_conversation(self):
        messages = []
        user_inputs = self.conversation["past_user_inputs"]
        responses = self.conversation["generated_responses"]
        for idx in range(len(user_inputs)):
            messages.append({"from": "user", user_inputs[i]))
            messages.append({"from": "bot", responses[i]})
        self.repeating_panel_1.items = messages

Then in the RepeatingPanel’s template form, we use some Python logic and a little CSS to make the user- and bot-generated lines look different. Then we just wire up a TextBox and button to submit new messages to the model – and voila!

Source code

See the source code:


Give the Gift of Python

Share this post: