ai-songwriter-beta / utils /song_utils.py
ajayarora1235's picture
beta version, add spaces remote
917b084
raw
history blame
No virus
7.76 kB
import os
from openai import OpenAI
from typing import Optional, Tuple, List, Dict
from dotenv import load_dotenv
from gradio import ChatMessage
import gradio as gr
# Load environment variables from .env file
load_dotenv()
client_key = os.getenv("OPEN_AI_KEY")
print(client_key)
oai_client = OpenAI(
api_key=client_key,
)
History = List[Tuple[str, str]] # a type: pairs of (query, response), where query is user input and response is system output
Messages = List[Dict[str, str]] # a type: list of messages with role and content
def generate_song_seed(baseline_seed):
"""
Generates a song seed based on a baseline seed description.
Args:
baseline_seed (str): The baseline seed description to generate the song concept from.
Yields:
str: The generated song concept in chunks.
"""
song_details_prompt = (
"Analyze this description of how someone is feeling and provide a suggestion of an interesting song concept to base a song off of. "
"Here are three examples, now provide a song concept for this fourth:\n\n"
)
song_seed_prompt_path = 'prompts/prompt_song_seed.txt'
with open(song_seed_prompt_path, 'r', encoding='utf-8') as file:
content_2 = file.read()
song_details_prompt += f"\n\n{content_2}{baseline_seed}\nSuggested Song Concept: "
response_generator = oai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": song_details_prompt}],
stream=True
)
current_response = ""
for chunk in response_generator:
delta_content = chunk.choices[0].delta.content
if delta_content:
current_response += delta_content
yield current_response
def get_sections(overall_meaning: str, section_list: str) -> str:
"""
Generates section meanings based on the overall meaning and section list.
Args:
overall_meaning (str): The overall meaning of the song.
section_list (str): A newline-separated string of section names.
Returns:
str: The generated section meanings.
"""
section_list = section_list.split("\n")
prompt_path = 'prompts/prompt_section_writer.txt'
with open(prompt_path, 'r', encoding='utf-8') as file:
prompt_content = file.read()
user_message = {
"role": "user",
"content": f"{prompt_content}\n\nOverall meaning: {overall_meaning}\nSection list: {', '.join(section_list)}\nSection meanings:"
}
response = oai_client.chat.completions.create(
model="gpt-4o",
messages=[user_message],
)
return response.choices[0].message.content
def messages_to_history(messages: Messages) -> Tuple[str, History]:
"""
Converts a list of messages into a history of user-assistant interactions.
Args:
messages (Messages): A list of message dictionaries, where each dictionary contains
'role' (str) and 'content' (str) keys.
Returns:
Tuple[str, History]: A tuple containing a string (empty in this case) and a list of tuples,
where each tuple represents a user-assistant message pair.
"""
assert messages[0]['role'] == 'system' and messages[1]['role'] == 'user'
# Filter out 'tool' messages and those containing 'tool_calls'
messages_for_parsing = [msg for msg in messages if msg['role'] != 'tool' and 'tool_calls' not in msg]
# Remove " Use write_section" from user messages
messages_for_parsing = [
{'role': msg['role'], 'content': msg['content'].split(" Use write_section")[0]} if msg['role'] == 'user' else msg
for msg in messages_for_parsing
]
# Create history from user-assistant message pairs
history = [
ChatMessage(role = q['role'], content = q['content'])
for q in messages_for_parsing[2:]
]
return history
def get_starting_messages(song_lengths: str, song_title: str, song_blurb: str, song_genre: str, init_sections: str) -> Tuple[List[Dict[str, str]], History]:
"""
Generates the initial messages for starting a songwriting session.
Args:
song_lengths (str): The lengths of the song sections.
song_title (str): The title of the song.
song_blurb (str): A brief description of the song.
song_genre (str): The genre of the song.
init_sections (str): The initial structure of the song sections.
Returns:
Tuple[List[Dict[str, str]], History]: A tuple containing the starting messages and the message history.
"""
system_prompt = (
"You are an expert at writing songs. You are with an everyday person, and you will write the lyrics of the song "
"based on this person's life by asking questions about a story of theirs. Design your questions using ask_question "
" to help you understand the user's story, so you can write a song about the user's experience that "
"resonates with them. We have equipped you with a set of tools to help you write this story; please use them. You are "
"very good at making the user feel comfortable, understood, and ready to share their feelings and story. Occasionally "
"(every 2 messages or so) you will suggest some lyrics, one section at a time, and see what the user thinks of them. "
"Do not suggest or ask for thoughts on more than one section at a time. Be concise and youthful."
)
user_prompt = (
f"I have a story that could make this concept work well. The title is {song_title}, it's about {song_blurb} with a genre "
f"{song_genre} and I think this should be the structure: {init_sections}\n{song_lengths}"
)
initial_messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"The user has stated the following:\n {user_prompt}\n Introduce yourself and kick-off the songwriting process with a question."},
]
first_msg_res = oai_client.chat.completions.create(
model="gpt-4o",
messages=initial_messages,
)
first_message = first_msg_res.choices[0].message.content
starting_messages = initial_messages + [{'role': 'assistant', 'content': first_message}]
history = [ChatMessage(role = x['role'], content = x['content']) for x in starting_messages]
history = history[2:]
return starting_messages, history
def update_song_details(instrumental_output: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
"""
Analyzes the given instrumental output to extract the genre, title, and blurb of a song.
Args:
instrumental_output (str): The assessment and suggestion of a song concept.
Returns:
Tuple[Optional[str], Optional[str], Optional[str]]: A tuple containing the genre, title, and blurb of the song.
"""
song_details_prompt = (
"Analyze this assessment and suggestion of a song concept to extract the genre, one sentence blurb of what the song is about. "
"Based on this, also suggest a song title. Output exactly three lines, in the format of 'genre: [genre]', 'title: [title]', 'blurb: [blurb]'.\n\n"
f"{instrumental_output}"
)
response = oai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": song_details_prompt}]
)
response_lines = response.choices[0].message.content.split('\n')
genre = next((line.split(": ")[1] for line in response_lines if "genre: " in line.lower()), None)
title = next((line.split(": ")[1] for line in response_lines if "title: " in line.lower()), None)
blurb = next((line.split(": ")[1] for line in response_lines if "blurb: " in line.lower()), None)
return genre, title, blurb