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 = ""
headers = {"Authorization": "Bearer {API_TOKEN}"}

def query(payload):
	response =, 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 = ""
headers = {"Authorization": f"Bearer {anvil.secrets.get_secret('api_token')}"}

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

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"]

def send_message(message):
    conversation = get_conversation()
    model_output = query({
        "inputs": {
            "text": message,
    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: