From Boost to Bust, Thanks to ChatGPT!

Vin Bhalerao
9 min readFeb 7, 2023

--

In my last article, I described how to develop possibly simplest, yet somewhat useful app using the OpenAI API.

The app, named AIffirmations, allows the user to select a topic from a list of choices, consisting of “life” or “love” or “money” or “the world” in general, and displays an uplifting message about that topic, generated by AI.

Now, some of you might say that this is a bit cheesy, but guess what, the beinning of a new year is all about thinking of cheesy but uplifting things, called new year’s affirmations and resolutions, and it works!

Until February at least!

And I know, you will say that no new year’s affirmation or resolution list can be complete without “weight reduction” as one of the choices, but when I placed that as an option next to “life”, “love”, “money”, and “the world”, it really looked embarrassing, so I took it out!

Maybe you can add it as an exercise.

(See what I did there?😉)

Anyway, in this article, I am going to show you how to improve upon this app, by adding an appropriate uplifting image based on the user’s selection.

And you guessed it, I will be using OpenAI’s image generation API (which underlies DALL-E 2) to do this. Exciting!

So let us get down to it without further ado, starting with a demo, as is the tradition in these things.

Demo of AIffirmations 2.0

Description of the New Additions in this Version

As I described in the last article, AIffirmations is a Python Flask app that exposes the route “/getboost” that returns a “boost” — an uplifting message depending upon what the boost is about (life, love, money, or the world) and the type of boost (affirmation, poem, a haiku, or quote / fact).

It basically generates a prompt based on the parameters selected by the user, and calls the OpenAI API to generate the text.

In this second revision, I added a call to OpenAI’s image generation API, after generating an appropriate prompt for the image.

After both of these calls return results, the “/getboost” route puts them together into a single object and returns it back to the browser, where the javascript in the browser constructs the final display, with the image on top and the text below it.

As simple as that!

Here are the relevant snippets of the code.

app.py:

This contains the main Flask app code that services “getboost” requests from the client.

It unpacks the “boost about what” and “what type of boost” query parameters, calls methods that generate the image and the text for the boost, puts them together and returns the result back to the client.

@app.get("/getboost")
def get_boost():
"""Get a boost message of a given kind and about a given topic."""
about = request.args.get("about", "life")
kind = request.args.get("kind", "positive affirmation")
boost = generate_boost(about, kind)
return boost

def generate_boost(about: str, kind: str):
boost_text = boost_text_gen.generate(about, kind)
if (boost_text is None):
return None

boost_image = boost_image_gen.generate(about, kind)
if (boost_image is None):
return None

boost = combine_boost_text_image(boost_text, boost_image)

boost_text_gen.py:

This contains the text generation method — it basically creates the appropriate prompt text based on the user’s choices and calls the OpenAI “Completion” endpoint to get the text part of the boost.

def generate(about: str, kind: str):
"""Generate a boost message of a given kind and about a given topic."""

try:
prompt = generate_prompt(about, kind)
print("Calling API: " + prompt)
response = openai.Completion.create(
model="text-davinci-003", # "text-curie-001",
prompt=prompt,
temperature=0.6,
max_tokens=200,
)
except Exception as e:
# If API call fails, fall back on returning a random boost from the curated collection
print(e)
return None

boost_text = response.choices[0].text
return boost_text


def generate_prompt(about: str, kind: str):
""" Generate a prompt for the OpenAI API based on the kind and about parameters """
return f"Write a {kind} about {about}:"

boost_image_gen.py:

This file contains the image generation part — it creates the appropriate prompt for the boost based on the user’s choice and calls the OpenAI Image creation end point.

There is a little added complication because the image is returned as a base64 encoded string which needs to be decoded and then saved to a file so it can be returned to the client when it asks for it.

I know, I need not have saved the image to a file, but I wanted to do that because I wanted to keep a stash of these images and curate them down the road.

This is necessary because the generated images (or text) may not always be great or most fitting, so stashing the good ones away and using them instead of the API to serve to the user makes sense.

Not to mention it will reduce the number of OpenAI API calls.

(Note that in production scenarios, you will probably want to save the image to some cloud storage location rather than the local disk like I am doing here.)

Anyway, here is the code that calls the OpenAI API and saves the generated image to disk.

def generate(about: str, kind: str):
"""Generate a boost image of a given kind and about a given topic."""

try:
prompt = generate_prompt(about, kind)
print("Calling API: " + prompt)

response = openai.Image.create(
prompt=prompt,
n=1,
size="256x256",
response_format="b64_json"
)

file_name = f"{prompt[:5]}-{response['created']}.png"

for index, image_dict in enumerate(response["data"]):
image_data = b64decode(image_dict["b64_json"])
image_file = IMAGES_DIR / file_name
with open(image_file, mode="wb") as png:
png.write(image_data)
except Exception as e:
# If API call fails, fall back on returning a random boost from the curated collection
print(e)
return None

boost_image = "/img/" + file_name
return boost_image

generate_prompt (for images):

The image prompt generation here is a bit more involved because I had to create different prompts for the diferent scenarios, but essentially it boils down to selecting a prompt from a dictionary of prompts keyed by what they are about.

def generate_prompt(about: str, kind: str):
""" Generate a prompt for the OpenAI API based on the kind and about parameters """
stems = {
"life":
[
"a person standing triumphantly on top of a hill with both their hands in the air, daytime, blue sky",
"a person meditating on top of a mountain, daytime, blue sky",
"a happy family enjoying a picnic in a park, kids playing around, flowers blooming in the background",
"drone shot of a white sandy beach with palm trees on a beautiful tropical paradise with blue ocean",
"beautiful landscape with blue sky and snow capped mountains reflecting into a lake, upbeat, golden hour"
],
"love":
[
"a happy couple holding hands at the beach, with a sunset in the background",
"a happy person holding a bouquet of beautiful flowers",
"two people in love dancing the tango with each other at a ballroom party",
"a happy person holding a heart-shaped balloon in a beautiful garden",
"two people in love sitting close to each other on a bench in a beautiful garden"
],
"money":
[
"a smiling person holding a big check",
"beach umbrella on a white sandy beach with palm trees on a beautiful tropical paradise with blue ocean, photorealistic, dreamy",
"an ecstatic person dancing while cash rains on them",
"a smiling person sitting on a pile of golden coins with the sun shining on them",
"huge mansion surrounded by lush garden, with an expensive car in the drive way",
"view from a huge expensive mansion on a hill, with an infinity pool, golden hour"
],
"the world" :
[
"a smiling and wise person holding a globe in their hands",
"a smiling and wise person holding a world map in their hands",
"a beautiful picture of the earth from space",
"a group of happy people of different ages and races holding hands",
"beautiful landscape with blue sky and snow capped mountains reflecting into a lake, upbeat, golden hour"
]
}
styles = ["photorealistic", "impressionist painting", "oil on canvas", "wartercolor painting"]

prompt = f'{random.choice(stems[about])}, {random.choice(styles)}, upbeat, high definition, 4K, masterpiece, award winning'

return prompt

main.js:

Finally, here is the javascript file which contains the code that executes when the user clicks on “Get your boost”.

It collects the user’s choices for what they want to get boosted about and the type of boost they desire, and sends a request to the server to get the boost based on it.

Since the calls take a bit of time, it shows an animation while waiting to receive a response from the server.

When the server responds with the boost, in the form of an image and a text, it displays them to the user.

const get_boost_btn = document.querySelector("#getBoostBtn");
get_boost_btn.onclick = () => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
var resultHTML = "";
if (xhr.status == 200) { // analyze HTTP status of the response
var response = JSON.parse(this.response);
if (response) {
if (response.image)
resultHTML = `<div><image class="generated_image" src="${response.image}" /></div>`;
if (response.text)
resultHTML = resultHTML + `<p class="generated_text">${response.text.replace(/^\n+|\n+$/g, '').replace(/\n/g, '<br/>')}</p>`;
}
else
resultHTML = `<p>Error: Empty response received.</p>`;
} else {
resultHTML = `<p>Error ${xhr.status}: ${xhr.statusText}</p>`;
}

document.querySelector(".waitAnim").style.display = "none";
document.querySelector(".result").innerHTML = resultHTML;
document.querySelector(".resultContainer").style.display = "block";
};

const about = document.querySelector('input[name="about"]:checked').value;;
const kind = document.querySelector('input[name="kind"]:checked').value;;

xhr.open("GET", "getboost?about=" + about + "&kind=" + kind);
xhr.send();
document.querySelector(".resultContainer").style.display = "none";
document.querySelector(".waitAnim").style.display = "block";
}

That’s it. The rest is all supporting and glue code.

Ok, but didn’t you say something about getting busted? We really want to hear about that!

I know, I am getting to it.

After I had mostly written this article, I wondered how ChatGPT would have written this article.

So I asked it to write one:

Write an article explaining how to write a Python Flask web app that 
generates and displays uplifting images about life or money or love or
the world, using the DALL-E 2 API. The intended audience for this
article is programmers and AI enthusiasts.

And guess what, it wrote a perfectly good article, describing all the things I mentioned above, along with all the important pieces of the code and their descriptions!

Of course, as you might have noticed, the prompt above talks only about images, so that’s a bit of a simplification. The app uses images as well as text.

But, as you may have realized yourself, writing precise prompts gets unweildy rather quickly as the complexity and details of the problem increase.

It becomes more optimal to break the problem down into smaller components, get answers to those and then stitch them together to create the desired output.

As I mentioned in my previous post, we may need to come up with a new language for prompts — Prompthon? —that can handle such level of details and complexity.

Anyway, coming back to my point about writing tutorial articles, here is just the top part of the article ChatGPT wrote in response to my prompt above:

It went on to write the whole article along with code snippets and helpful descriptions and so on, just like I have done in this post!

The article actually ended up being longer than their current word limit for responses, so I had to ask it to continue the article a couple of times before it got completely done.

(I decided not to include the entire response here because I am hearing rumblings from various corners about banning ChatGPT-generated content and I don’t want to get into trouble.)

In fact, I am trying to make exactly the opposite point.

Does it make sense for human writers to write this type of articles anymore?

Anyone can simply ask ChatGPT — even exactly specify their particular situation (maybe they want background music or animation?) and ChatGPT would provide a customized tutorial for their specific scenario.

At least for things that it knows about, of course. Which is a lot, particularly when it comes to coding since it has read a ton of code, way more than any real human could ever hope to read in their entire life.

And yes, it does make mitakes and even make stuff up sometimes, so you need to check everything — which turns out to be easy in the case of code, because ultimately you can see if it works or not.

But the ultimate question is, does it make sense for human writers to write such articles anymore?

I’d say probably not.

Of course, you the reader will probably miss the human touch, (such as my dry wit and intelligent sense of humor, thank you)! 😉

Oh, but wait, ChatGPT can be witty and funny too! And poetic. And scholarly. And romantic. And edgy. And… Well, practically whatever you can ask it to be!

Maybe not at the level of Shakespeare, but possibly better than the typical engineer trying to write a tutorial in his spare time, right?

Bottomline, my idea of writing tutorial articles of this type looks busted! At least on any topics that ChatGPT might know about.

I will be pondering this over the next few days.

Maybe I will go back to writing more original / philosophical or scifi stories like I used to. I think we still have time until ChatGPT becomes good at stuff like that.

Or maybe I will think of something else.

We’ll see how it goes. For sure, these new AI tools are going to make us pivot constantly, as we have been warned for a long time now.

I guess that time has come, so strap in and start pivoting.

After this, I think I am going to need an emotional boost — about life or the world in general. 😂

Anyone knows where I can get one?

Trying to give myself a boost, using my own app!

--

--

Vin Bhalerao

I write about science / engineering and their significance and value in our lives. | My book: “An Engineer’s Search for Meaning” (https://meaning.lifevisor.ai).