Building GPTunes: Lessons Learned from Crafting an End-to-End Playlist App with Generative AI
Have you ever wished you could automatically create an original Spotify playlist based on a specific theme? My side project, GPTunes, does exactly that! With this app, end-users can input their own custom theme and after some processing a playlist with a custom title, description and cover image is generated with generative AI, all in about 30 seconds.
The app consists of a frontend built in Dash and a backend built with Fastapi, in which several external APIs from OpenAI (Chat completion with GPT models and Image generation with Dall-E-2) and Spotify (for searching tracks and publishing playlists) are pieced together. Both the frontend and backend are served as containerized Cloud Run services on Google Cloud Platform (GCP). If you’re interested in the technical nuts and bolts of the project, they’re available on my GitHub page.
In the course of developing GPTunes end-to-end, I learned a number of valuable lessons that I’ll share with you in this post. If you think about developing your own application with generative AI, I hope these lessons will come in handy!
1. Dealing with Unexpected GPT Responses
In an attempt to prompt the model for structured output, I am instructing the system explicitly to only reply in valid JSON format and I also make use of one-shot prompting.
The function below parses the JSON response from the string output of the assistant, after removing newline characters and trimming any whitespaces and trailing backtick characters from the string. Still, GPT can occasionally provide JSON outputs with minor syntax errors. In such cases, a tool like dirtyjson can provide an extra layer of robustness since it can handle imperfect JSON strings.
Additionally, implementing error handling and logging the responses that fail to parse can save a lot of troubleshooting time down the line.
2. Pragmatism Over Purism in Infrastructure-as-Code
Initially, I attempted to automate all cloud-related tasks with Terraform. However, I soon encountered situations where I ran into problems with permissions. For example, before I could employ a service account to run my Terraform code within the CI/CD pipeline, that very account first needed to be assigned the correct permissions. This presented a sort of chicken-and-egg problem: I couldn’t use Terraform to establish these permissions because the permissions were needed to use Terraform in the first place!
It was a fellow developer that suggested a pragmatic solution: using a bash script with a series of gcloud commands to run locally for the initial setup. This method wasn’t just a one-time workaround; it also proved beneficial when setting up multiple similar environments, such as development and production, in the cloud.
Moreover, managing secrets manually turned out to be simpler than trying to handle them with Terraform. This experience taught me that while Terraform is a powerful tool, there are instances where alternative methods can be more effective.
3. Simplifying URLs with Custom Domain Mapping on Cloud Run
Cloud Run is a great tool for running the Dash frontend as a serverless web app. The default URL it provides, however, isn’t exactly user-friendly and doesn’t feel like it is your “own” place on the internet yet. If you’d like to have a more user-friendly URL, the common approach is to register a custom domain, and then map this domain to your Cloud Run service. I bought my domain name gptunes.nl from Strato and to verify to Google Cloud that it was mine, I added a DNS TXT to my domain. You can obtain the DNS TXT from the search-console if you choose ‘domain’ and paste your domain name.
Next, I created a DNS zone on Cloud DNS, which is essentially a section of the domain name space where I have administrative control. This is necessary because the DNS zone allows me to configure the mapping of my domain to the IP address where my app is hosted, essentially directing internet traffic to my Cloud Run service. With my DNS zone in place, I added my DNS records using the gcloud command-line tool.
gcloud dns --project=gptunes record-sets create [gptunes.nl](http://gptunes.nl/). --zone="gptunes-dns-zone" --type="A" --ttl="300" --rrdatas="188.8.131.52,184.108.40.206,220.127.116.11,18.104.22.168"
gcloud dns --project=gptunes record-sets update [gptunes.nl](http://gptunes.nl/). --type="AAAA" --zone="gptunes-dns-zone" --rrdatas="2001:4860:4802:32::15,2001:4860:4802:34::15,2001:4860:4802:36::15,2001:4860:4802:38::15" --ttl="300"
With this setup, the custom domain gptunes.nl will now point to my Cloud Run application, giving it a cleaner and more user-friendly URL.
4. The Power of External CSS Stylesheets in Dash
By using external CSS stylesheets, I was able to manage the visual appearance of the web app separately from its functionality. To do this, I created an `assets` folder where I placed all my styling elements, including a custom favicon, a background image that I created with Midjourney (how do you like the ‘Wes Anderson style music studio’ background?) and a `styles.css` file with the CSS rules that determine how to apply styling to HTML elements.
If you’re new to CSS like I was, it may seem challenging at first, but it quickly becomes straightforward. It took a while for me to understand the hierarchical nature of the stylesheets as I encountered bugs that seemed unexpected at first. If you’re using multiple stylesheets together or want to apply multiple rules to the same element, keep in mind that CSS rules form a hierarchy, or cascade, based on a set of factors such as specificity, importance, and order of declaration. ChatGPT was also immensely helpful in creating boilerplate code for components, speeding up the development process significantly.
Building GPTunes end-to-end was an excellent opportunity for me to integrate the amazing capabilities of generative AI in a working app. After putting it to the test with some friends and colleagues it was amazing to see the first playlists appear! The resulting playlists vary in relevancy for the given theme (even with the same inputs), but in general I am finding the results fun and sometimes surprising. Take this beautiful playlist generated with the prompt “Italian Gondola Ride” for instance!
In addition, I also gained hands-on experience in Infra-as-Code, CI/CD and web development. I hope my lessons learned prove useful for your own projects!
Ready to experience the magic? Head to gptunes.nl, enter your theme, relax, and let the music play!