ChatGPT Festivus

Table of contents

ChatGPT Festivus

Abstract

Speaking gizmo gets festive quotes from OpenAI / ChatGPT

ChatGPT Talks

My Festivus gizmo connects to ChatGPT to ask it for "artificial intelligence generated" festivus quotes. It will then (try to Slight smile ) turn the received reply into voice, and say the message. 
Consistent with this "artificial content generation" theme: the logo is generated by Nightcafe. I asked it to create "chatgpt logo in christmas style".

Example quote:

Look and Feel

It's a plain looking device (yeah - I know - not very Holiday compliant). A box with a button, a speaker and a wall plug. When the bored partygoers at your year end party want some distraction, they can push the button. They will get energised by a new intelligent festive thought.

image
image source: nightcafe

Technology and Flow

Simplicity and off-the-shelf are the key words. I want to use existing assets.

  • OpenAI's Chat API
  • Python and shell scripts
  • OpenAI's Text To Speech API
  • Raspberry Pi
  • Little amplifier and speaker
  • Button

When the button is pushed, my scripts will run to call the API. The API returns a quote that's handed over to the speech module.

Artificial Intelligence content:

  • the gizmo quotes are generated by OpenAI
  • all speech is generated by OpenAI
  • all art in this article is generated by Nightcafe
  • part of this article

OpenAI API

This section describes how to get access to the API and use it as a query / response unit.

image
image source: nightcafe

Key provisioning

Get yourself a ChatGPT / OpenAI account. The standard free one will do. You can then request a secret API key.

image
image source: actual screenshot

image
image source: actual screenshot

Copy the key to a safe store. Don't share it. Note to self: also don't put it in source code that is uploaded to the forum.

Install OpenAI library

execute this command:

pip3 install -q openai

Code

I based the initial version on OpenAI's Assistant example.

Set the environment variable OPENAI_API_KEY

Windows PowerShell

$Env:OPENAI_API_KEY = "<api key>"

Raspberry OS:

export OPENAI_API_KEY="<api key>"

Test with minimal changes to example:

# ---
assistant = client.beta.assistants.create(
    name="Festivus",
    instructions="You are a festivus narrator. Generate a random festivus quote when asked.",
    tools=[{"type": "code_interpreter"}],
    model="gpt-3.5-turbo",
)

# ---

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="Please generate a new random festive quote",
)

run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
    instructions="Be merry. Always generate a fresh quote.",
)

# ---

Result

PS > & python3.9.exe openai_festivus.py
checking assistant status.
in progress...
done!
messages:
{'role': 'assistant', 'message': 'Of course! Here\'s a merry quote just for you:\n\n"May your days be merry and bright, and may all your troubles be out of sight."'}
{'role': 'user', 'message': 'Please generate a new random quote'}
PS > & python3.9.exe openai_festivus.py
checking assistant status.
in progress...
in progress...
done!
messages:
{'role': 'assistant', 'message': 'Of course! Here\'s a new random quote just for you:\n\n"Believe you can and you\'re halfway there." - Theodore Roosevelt'}
{'role': 'user', 'message': 'Please generate a new random quote'}

Early success!

Train OpenAI to give only the quote. No confirmation or comments.

run = client.beta.threads.runs.create(
    thread_id=thread.id,
    assistant_id=assistant.id,
    instructions="Be merry. Always generate a fresh quote. Do not include any explanation.",
)

Result:

messages:
{'role': 'assistant', 'message': '"May your days be merry and bright."'}
{'role': 'user', 'message': 'Please generate a new random festive quote'}

Better!

image
image source: nightcafe

Get the actual quote:

            if message.role == "assistant" :
                print(message.content[0].text.value)

'Tis the season to be jolly!

Best!

To reduce OpenAI costs, I let the code first check if an Assistant exists for this project. If it does, I reuse it. If not, I create an assistant.
This reduces the amount of tokens used by 50%, after first run. Free accounts have a limited amount. Paying accounts get a (tiny) charge.

Speech

image
image source: nightcafe

OpenAI has a speech module. You send it a text, and it can generate an mp3 file. I pass the part of the payload that represents the quote:

from pathlib import Path

# ---

speech_file_path = Path(__file__).parent / "speech.mp3"

# ---

            if message.role == "assistant" :
                response = openai.audio.speech.create(model="tts-1", voice="alloy", input=message.content[0].text.value)
                response.stream_to_file(speech_file_path)

# ---

Example result:

'Tis the season to be jolly!

Success!

To play the sound back on Windows, I used playsound

pip3 install playsound

on Raspberry OS (I use headless)

pip3 install pygame

pip3 install pygobject

sudo apt-get install git curl libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-2.0-0

Button Listener

button listener
image source: nightcafe

I used pgiozero. Very easy. I just have to declare a button, then tell it what to do when clicked. The rest magically works.

if platform.system() == 'Windows':
    # ---
else:
    from gpiozero import Button
    from signal import pause
    btn_getquote = Button(21, bounce_time=1)

if platform.system() == 'Windows':
    get_quote()
else:
    print("wait for click")
    btn_getquote.when_pressed = get_quote
    pause()

On Windows, The script runs one time, plays a quote and exits. On a Pi, it waits for a button click, then plays a quote, looping until forever.

My initial code waited for a click, then handed off to OpenAI to fetch a quote and generate the spoken message. Youtuber iSteal hinted to optimise this. The final code buffers one quote, so that it's ready when the button is clicked.

hardware and software hook up
image source: nightcafe

Hardware Hookup

For audio, I plugged an amplifier into the Pi 3 audio jacket.
The button is connected between GPIO2 and ground.
Nothing else needed , except a Wi-Fi connection and power.

I asked AI to design the hardware:

artificial housing design
housing design: nightcafe

This is the real housing :

image
image AI enhanced with LetsEnhance.io

Software Hookup

No AI in the software design. I used the OpenAI Python examples, doc and my own brain. Almost everything is contained into a single python script. OpenAI does the majority of the work in the cloud: "get quote, turn to speech, say it".

Full Python source:

#!/usr/bin/env python

from pathlib import Path
import time
import openai
import platform

if platform.system() == 'Windows':
    from playsound import playsound
else: 
    import pygame
    from gpiozero import Button
    from signal import pause
    btn_getquote = Button(21,bounce_time=1)


def playwav(wavefile):
    if platform.system() == 'Windows':
        playsound(wavefile)
    else:
        pygame.mixer.init(44100, -16, 2, 2048)
        pygame.mixer.music.load(wavefile)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy() == True:
            continue

# gets API Key from environment variable OPENAI_API_KEY
client = openai.OpenAI()

speech_file_path = Path(__file__).parent

assistant_name_base = "Festivus."
assistant_name = assistant_name_base + "0.0.1"
have_good_sound = False
next_button_click_welcome = True


def get_quote():
	global have_good_sound
	# try to reuse assistant for cost saving
	existing_assistent_found = False
	assitant_obj_list = client.beta.assistants.list(limit=100, order='desc',extra_query={'name': assistant_name}).data
	for i in range(len(assitant_obj_list)):
			if assitant_obj_list[i].name == assistant_name:
				existing_assistent_found = True
				assistant = assitant_obj_list[i]
				break
			else:
				if assitant_obj_list[i].name.startswith(assistant_name_base):
					client.beta.assistants.delete(assitant_obj_list[i].id)
	if not existing_assistent_found:
		assistant = client.beta.assistants.create(
			name=assistant_name,
			instructions="You are a festivus narrator. Generate a random festivus quote when asked.",
			tools=[{"type": "code_interpreter"}],
			model="gpt-3.5-turbo",
	)	
	
	thread = client.beta.threads.create()

	message = client.beta.threads.messages.create(
		thread_id=thread.id,
		role="user",
		content="Please generate a new random festive quote",
	)
	run = client.beta.threads.runs.create(
		thread_id=thread.id,
		assistant_id=assistant.id,
		instructions="Be merry. Always generate a fresh quote. Avoid religious references. Use the style of Oscar Wilde. Do not include any explanation.",
	)

	#print("checking assistant status. ")
	while True:
		run = client.beta.threads.runs.retrieve(thread_id=thread.id, run_id=run.id)

		if run.status == "completed":
			#print("done!")
			messages = client.beta.threads.messages.list(thread_id=thread.id)

			#print("messages: ")
			for message in messages:
				assert message.content[0].type == "text"
				if message.role == "assistant" :
					print(message.content[0].text.value)
					response = openai.audio.speech.create(model="tts-1", voice="alloy", input="Ho! Ho! Ho! . .  "
#						+ "Legal notice: you are hearing audio generated by AI and not Oscar Wilde: . . " 
						+ message.content[0].text.value)
					response.stream_to_file("./speech.mp3")
					have_good_sound = True
			# client.beta.assistants.delete(assistant.id)

			break
		elif run.status == "failed":
			print("error: " + run.last_error.message)
			response = openai.audio.speech.create(model="tts-1", voice="alloy", input="Ai! Ai! Ai! . .  Legal notice: you are hearing an AI generated error message: . . " + run.last_error.message)
			response.stream_to_file("./speech.mp3")
			have_good_sound = True
			# client.beta.assistants.delete(assistant.id)
			break;
		else:
			#print("in progress...")
			time.sleep(5)

def invoke_voice():
	playwav('./speech.mp3')

def button_presshandler():
	global next_button_click_welcome
	global have_good_sound

	if not 	next_button_click_welcome:
		return
	next_button_click_welcome = False	
	print("clicked")	
	if have_good_sound:
		have_good_sound = False
		invoke_voice()
	get_quote();

def button_releasehandler():
	global next_button_click_welcome
	print("wait for click")
	next_button_click_welcome = True;

#prime first quote
get_quote()


if platform.system() == 'Windows':
	invoke_voice();
else: 
    print("wait for click")	
    btn_getquote.when_pressed = button_presshandler
    btn_getquote.when_released = button_releasehandler
    pause()

Run as a daemon

I created a service. This takes care that the project runs as a daemon once the Pi boots:

Scripts and commands:


-rwxr-xr-x 1 jancumps jancumps 222 Dec 3 14:56 openai_festivus_start.sh

#! /bin/sh

echo "start openai festivus."
export OPENAI_API_KEY="<REDACTED>"
cd /home/jancumps/develop/workspace_xmas
python3 /home/jancumps/develop/workspace_xmas/openai_festivus.py &
echo "start sequence completed"

-rwxr-xr-x 1 jancumps jancumps 170 Dec 3 15:07 openai_festivus_stop.sh

#! /bin/sh

echo "stop openai festivus."
ps -ef | grep 'python3 ./openai_festivus.py' | grep -v grep | awk '{print $2}' | xargs -r kill -9
echo "stop sequence completed"

-rw-r--r-- 1 jancumps jancumps 641 Dec 3 15:11 openai_festivus.service

[Unit]
Description=openai festivus service.
DefaultDependencies=no
Requires=ifupdown-pre.service
Wants=network.target
After=local-fs.target network-pre.target apparmor.service systemd-sysctl.service systemd-modules-load.service ifupdown-pre.service network.target
Before=shutdown.target
Conflicts=shutdown.target
[Install]
WantedBy=multi-user.target
WantedBy=network-online.target
[Service]
Type=simple
User=jancumps
Group=jancumps
ExecStart=/home/jancumps/develop/workspace_xmas/openai_festivus_start.sh
ExecStop=/home/jancumps/develop/workspace_xmas/openai_festivus_stop.sh
RemainAfterExit=true
TimeoutStartSec=5min


image
image source: nightcafe

The service has to be registered, and started the first time. I used my own (non-root) account to run it. A further improvement could be to create an account just for this application...

sudo chmod  644 ./openai_festivus.service
sudo ln -s /home/jancumps/develop/workspace_xmas/openai_festivus.service /lib/systemd/system/openai_festivus.service
sudo systemctl daemon-reload
jancumps@raspberrypi:~/develop/workspace_xmas $ sudo systemctl enable openai_festivus.service

If you reboot, the raspberry will start with the openai festivus ervice running. You can use the usual systemd commands to verify, stop and start the daemon.

sudo systemctl status openai_festivus.service
sudo systemctl stop openai_festivus.service
sudo systemctl start openai_festivus.service
sudo systemctl restart openai_festivus.service

It works!

image
image source: actual screenshot

Does this project make sense? Lets investigate...

What are the key advantages of using OpenAI to generate random festive quotes?

Using OpenAI to generate random festive quotes offers several advantages:

  1. Variety and Creativity: OpenAI models are trained on a vast dataset, allowing them to generate diverse and creative quotes. This ensures a wide range of festive quotes, avoiding repetition and providing fresh content.

  2. Time Efficiency: Generating festive quotes manually can be time-consuming. OpenAI can swiftly produce numerous quotes in a short period, saving time and effort.

  3. Customization: You can specify the tone, length, or theme of the quotes you want, allowing you to tailor the generated content to suit your needs or the occasion.

  4. Quality Assurance: OpenAI models strive to produce high-quality content. While not every generated quote may be perfect, the platform generally maintains a high standard of language and coherence.

  5. Inspiration and Engagement: These quotes can serve as inspiration for various purposes, such as social media posts, greeting cards, marketing materials, or personal messages, engaging your audience with festive-themed content.

  6. Adaptability: OpenAI models can be fine-tuned and adapted to generate specific types of quotes, ensuring relevance and resonance with the audience you're targeting.

  7. Availability: OpenAI's API allows for easy integration into various platforms, websites, or applications, making it accessible for generating festive quotes wherever and whenever needed.

However, it's important to review and curate the generated content as AI-generated text might occasionally contain errors or inappropriate content. Human oversight ensures the final output aligns with your intended purpose and is free from unintended issues.

image
image source: nightcafe

Is it useful to let a Raspberry Pi play the results as audio?

Using a Raspberry Pi to play audio generated by OpenAI can be a fun and interesting project, especially if you want to hear the festive quotes audibly. Here's why it could be useful or advantageous:

  1. Demonstration of AI Integration: It's a practical way to demonstrate how AI-generated text can be utilized in real-time applications. By having the Raspberry Pi play the audio, you showcase the integration of AI capabilities in tangible and audible ways.

  2. Accessibility: For certain applications where reading text isn't convenient or possible, converting text to speech can be highly beneficial. For instance, if you're creating an interactive display, having the Raspberry Pi speak the quotes aloud can engage users who might not be able to read them.

  3. Personalization: You can program the Raspberry Pi to generate and play specific festive quotes at predetermined times or in response to certain triggers, adding a personalized touch to the experience.

  4. Creativity and Entertainment: Hearing the quotes spoken aloud can enhance the overall festive ambiance, adding a layer of entertainment or whimsy to an event or space.

  5. Learning Experience: Building this project can serve as an educational opportunity, allowing you to learn more about programming on Raspberry Pi, text-to-speech conversion, and integration with AI APIs.

To achieve this, you'd need to connect the Raspberry Pi to a speaker and implement code that requests festive quotes from OpenAI, converts the text to speech, and then plays it through the connected audio output. There are libraries available (such as pyttsx3 in Python) that enable text-to-speech functionality on a Raspberry Pi.

Remember to consider privacy, particularly if the Raspberry Pi is used in a public space, and ensure compliance with any usage terms or permissions related to generating and playing the audio content.

Bye from image.

References

logo AI-generated: https://creator.nightcafe.studio/creation/ADSMAN2hDBg8iRYl1CWj

bored partygoer AI generated: https://creator.nightcafe.studio/creation/fHWqZDYB1ZRNunRenpTo

API I use to talk to OpenAI / ChatGPT: https://www.makeuseof.com/chatgpt-api-complete-guide/

examples I used as a starting point: https://github.com/openai/openai-python/tree/v1.3.4/examples

how to make result only return the quote: https://community.openai.com/t/how-to-let-gpt-do-not-return-any-accompanying-text/324513/8

text to speach: https://github.com/openai/openai-python/blob/v1.3.4/examples/audio.py

sound playback library on Windows - playsound: https://pypi.org/project/playsound/

sound playback library on Raspberry OS - pygame: https://www.pygame.org/wiki/GettingStarted

audio output for headless raspberry os: https://elinux.org/R-Pi_Troubleshooting#Sound

some blog content might be generated by ChatGPT:https://chat.openai.com/

wise words from  may apply here: Regulation of Content Created and Submitted to the Site using LLM tools and ChatGPT 

The button uses gpiozero. I got the example here: https://gpiozero.readthedocs.io/en/latest/faq.html#how-do-i-keep-my-script-running

Category : Holiday Projects
Parents
  • Ai Ai, I hit limitations:

Comment
  • Ai Ai, I hit limitations:

Children