Thursday, 10 September, 2020 UTC


Summary

As a musician/songwriter, one thing I find myself struggling with all too often is writing unique, compelling song lyrics. Metal lyrics in particular are especially difficult for me. Writer's block is usually the issue, but even worse is when I come up with a great line to start but nothing to follow it.
Thanks to OpenAI and their GPT-3 API, this is no longer an issue. Now I can just write a few lines myself and let a trained model write the rest. Is it cheating? Probably. But is it fun? Definitely.
This post will demonstrate how to use Twilio Functions to assist in writing song lyrics by using OpenAI's GPT-3 engine and their Completion API. Here are a few examples of interesting lyrics I was able to generate using this tool:
> Cut out the heart of a living man, put it in a box made from the bones of your husband, and throw the box into the sea. The dead son will come back to life.
> Evil will be seen everywhere, laughing in the night, playing with our fears. The sorcerer, behind the black door. The master invites you to play the game.
> The Man in the Moon is my friend and I will not leave this world.
Twilio's own Miguel Grinberg has recently published blog posts which go into detail about Open AI's GPT-3 Language Model as well as how to use it to write a chatbot using Python. His posts contain a lot of background on the OpenAI technologies and although they're not a prerequisite to this post they're definitely worth reading through.
Requirements
The following is required to complete this blog post:
  • A desktop/command line to call the Twilio Function via HTTP.
  • A Twilio account. If you are new to Twilio click here to create a free account now and receive $10 credit when you upgrade to a paid account. See the docs for more info on features and limitations of a free Twilio account.
  • An OpenAI API key. Request beta accesshere.
  • Optional: An SMS-enabled Twilio phone-number and a smartphone that can send and receive SMS so that the Twilio Function can be called from anywhere.
OpenAI Playground
The best way to get familiar with the OpenAI Completion API is via the OpenAI Playground. The playground loads your API keys automatically and includes a GUI to adjust all the parameters that the API accepts, as well as descriptions about the parameters.
I used the Playground to determine the parameter values used in the JavaScript code shown later in this post. Feel free to adjust these parameters to whatever values best suit you.
The parameters used here were decided using trial and error, but their descriptions helped determine whether to set them relatively high or low.
  • Temperature controls the randomness of the completion. Setting it to a high value ensures that a completely different response is returned even if a prompt is reused. If an idempotent response is desired instead, set this to zero.
  • Frequency Penalty controls the likelihood of the same/similar lines being repeated in a completion. This is set pretty high to avoid repetition. However, it could make sense to lower this value if writing lyrics for a song's chorus.
  • Presence Penalty seems similar to Frequency Penalty, but is actually used to determine how likely the completion will jump to new topics. This is set somewhere in the middle so that the completion shares a similar theme overall but can still sometimes jump to new topics.
Set up a Twilio Function
Now that we've found the optimal parameters to send to the OpenAI API, the next step is to write the JavaScript code for the Twilio Function so that we can replicate this behaviour outside the OpenAPI Playground.
Twilio Functions are a great use case for a small app like this because they're cheap, secure, serverless, and scale automatically. Although the cURL command could do a lot of the same thing as the JavaScript code, using a Function has two main benefits:
  • Functions are easy to share as they can be publicly accessible (cURL would require the API key to be present)
  • It will be accessible via SMS, allowing the Function to be invoked without a laptop
Start by navigating to the Functions overview in the Twilio console. Click on the Create Service button and create a service with a relevant name such as openAI as seen in the screenshot below:
Inside this service, click on the Add + button and select Add Function. This creates a new endpoint which will be named as /lyrics and set to public. It needs to be public since the endpoint will be accessed via a cURL request and not only from Twilio.
Next, navigate to the Settings at the bottom of the page and select the Environment Variables section of the service to add your OpenAI secret key. You can find the OpenAI secret key at the top of the Developer Quickstart.
Copy and paste the API secret key to the Value text field and name the Key as "OPENAI_SECRET_KEY", as seen in the screenshot below:
Last, navigate to Dependencies under Settings and add the latest version of the got package (11.5.2 is the latest version at the time of writing), which will be used in the Function to make an outgoing HTTP request.
Enter "got" into the Module text field and the version number "11.5.2" into the Version text field, seen below:
Handle webhooks
Now that the Function is configured, replace the code inside of the /lyrics endpoint function with the following:
const got = require('got');  exports.handler = function(context, event, callback) {  let message = "";  let twiml = new Twilio.twiml.MessagingResponse();  const prompt = event["prompt"]   got.post('https://api.openai.com/v1/engines/davinci/completions', {  json: {  "prompt": prompt,  "max_tokens": 50,  "temperature": 0.9,  "top_p": 1,  "presence_penalty": 0.4,  "frequency_penalty": 0.75,  "stop": "\n",  },  headers: {  'Authorization': `Bearer ${context.OPENAI_SECRET_KEY}`,  }  })  .json()  .then(response => {  message = response.choices[0].text  })  .catch(error => {  console.log(error);  message = "Oops! Something went wrong.";  })  .finally(() => {  twiml.message(message);  callback(null, twiml);  }); }; 
The code above will execute the following:
  • Extracts a parameter from the event object called prompt.
  • Sends a POST request to the OpenAI endpoint with the necessary parameters (including prompt) and authorization headers.
  • Builds a TwiML <Message> response containing the parsed response from the OpenAI request.
Test the Twilio Function
Click Save and locate the Deploy All button at the bottom of the screen. In order to test the function, copy the full URL by clicking on the three dots on the /lyrics endpoint and select Copy URL.
Open a command line and enter a command like the one below, but replace the URL shown below with the URL copied from your own Function. Set the prompt to whatever completion string you want:
$ curl -X POST -d "prompt='hey how are you doing'" https://openai-XXXX.twil.io/lyrics <?xml version="1.0" encoding="UTF-8"?><Response><Message> because I couldn't get a word out of my mouth. He instantly said to me 'it is okay man it happens' and then got up and walked and kept on playing football. I was just in complete awe of what he did (or didn</Message></Response> 
The xmllint tool can be used to extract the OpenAI response directly from the Twiml returned by the Function, as seen here:
$ curl -X POST -d "prompt='hey how are you doing'" https://openai-XXXX.twil.io/lyrics | xmllint --format --xpath "string(//Message)" - hello beautiful" . I know it's short , but it'll do for now. He looks like a gangster with those shades on, and he's a hottie ! .I was expecting this class to be boring , instead I met the 
Handle SMS with a Twilio Function
This section is optional but opens up an additional method of interaction with the Function. I often find inspiration for lyrics at times when I don't have access to my laptop, such as in a car or train during a commute. Luckily, it's easy to add SMS integration into a Function so that lyrics can be sent from my phone for a quick and inspirational response.
Start by purchasing a Twilio Phone Number with SMS capabilities. Then click on the Twilio phone number to see the configuration page. Scroll down and set the Messaging options to forward to your new Twilio Function.
Under "A MESSAGE COMES IN", select Function. For "Service", select the newly created Function named "OpenAI". For "Function Path", select "/lyrics".
Next, go back to the Function code. Only one line in the Function needs to be updated to handle SMS. Delete the line shown in red and replace it with line shown in green below:
- const prompt = event["prompt"] + const prompt = event.Body !== undefined ? event.Body.trim() : event["prompt"] 
This line now checks if there's a Body object nested in the Functions Event object. If Body is present, we can assume that this Function is being invoked by an incoming SMS message and set the prompt to that object. Otherwise, we set it to event["prompt"], which is extracted from the POST request body.
Save and deploy the Function again. It is now able to handle both POST requests as well as SMS messages.
Test it by sending an SMS to the Twilio number you just configured and set the message body to the lyrics you are working on. I tested it by sending some lyrics from the song "Bleed" by one of my favorite metal bands, Meshuggah. The result that returned was definitely pretty metal:
Conclusion
Rock on! Remember that this Function can be reached from anywhere that can make a POST request! Try integrating it into a Python or Node app for even more usability.
OpenAI includes a number of other features that weren't mentioned in this post and are certainly worth exploring such as semantic search. Additionally, it's fun to play around with the different language models other than the default Davinci model and see how the responses change.
As always, thanks for reading! If you come across any issues or questions, feel free to reach out on Twitter or GitHub.