Table of contents
Abstract
Speaking gizmo gets festive quotes from OpenAI / ChatGPT
Project
ChatGPT Talks
My Festivus gizmo connects to ChatGPT to ask it for "artificial intelligence generated" festivus quotes. It will then (try to ) 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 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 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 source: actual screenshot
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 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 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
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.
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:
housing design: nightcafe
This is the real housing :
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 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 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:
-
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.
-
Time Efficiency: Generating festive quotes manually can be time-consuming. OpenAI can swiftly produce numerous quotes in a short period, saving time and effort.
-
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.
-
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.
-
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.
-
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.
-
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 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:
-
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.
-
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.
-
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.
-
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.
-
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 .
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 cstanton 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