ajayarora1235 commited on
Commit
917b084
1 Parent(s): 8b3be6a

beta version, add spaces remote

Browse files
Files changed (7) hide show
  1. ai_tools.json +97 -11
  2. app.py +350 -58
  3. chat.py +329 -84
  4. data/conversation_history.json +1 -0
  5. gpt_calls.py +35 -35
  6. suno.py +57 -4
  7. utils/song_utils.py +17 -5
ai_tools.json CHANGED
@@ -50,6 +50,42 @@
50
  }
51
  }
52
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  {
54
  "type": "function",
55
  "function": {
@@ -73,14 +109,6 @@
73
  },
74
  "description": "Lines to revise, as a list of numbers. The first line is labelled 1, second 2, and so on. Lines to change should be assessed based on feedback user provides and where they suggest changes."
75
  },
76
- "snippet_instrumental_tags": {
77
- "type": "string",
78
- "description": "Instrumental tags to be used for this section revision. This should be the exact same as what was used for the snippet being revised. The instrumental tags are listed under the recent audio clip html."
79
- },
80
- "snippet_clip_to_continue_from": {
81
- "type": "string",
82
- "description": "The 'continued from clip:' clip id that was used in the original snippet we are now revising. This should be the exact same as 'continued from clip' for the snippet being revised, listed under the original snippet's audioplayer; if it is None, this should not be passed."
83
- },
84
  "relevant_ideas": {
85
  "type": "array",
86
  "items": {
@@ -96,7 +124,7 @@
96
  "description": "Words that are requested to be included in revised lines. Unless specific words are requested, this should be empty"
97
  }
98
  },
99
- "required": ["section_name", "current_section", "lines_to_revise", "snippet_instrumental_tags"]
100
  }
101
  }
102
  },
@@ -131,6 +159,64 @@
131
  }
132
  }
133
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  {
135
  "type": "function",
136
  "function": {
@@ -149,7 +235,7 @@
149
  },
150
  "snippet_clip_to_continue_from": {
151
  "type": "string",
152
- "description": "The last approved song clip to continue from. Almost always the most recent clip id outputted in the conversation. If user did not receive a snippet for the prior section or ever before, this should not be passed."
153
  }
154
  },
155
  "required": ["snippet_lyrics", "snippet_instrumental_tags"]
@@ -208,7 +294,7 @@
208
  },
209
  "snippet_clip_to_continue_from": {
210
  "type": "string",
211
- "description": "The last approved clip ID of the song to continue the remaining sections from. Almost always the most recent clip id outputted in the conversation. If user did not receive a snippet for the prior section or ever before, this should not be passed."
212
  },
213
  "sections_written": {
214
  "type": "array",
 
50
  }
51
  }
52
  },
53
+ {
54
+ "type": "function",
55
+ "function": {
56
+ "name": "ask_question",
57
+ "description": "Ask a question to the user to get more information. This function should be called when the AI needs more information about the user to continue writing the song.",
58
+ "parameters": {
59
+ "type": "object",
60
+ "properties": {},
61
+ "required": []
62
+ }
63
+ }
64
+ },
65
+ {
66
+ "type": "function",
67
+ "function": {
68
+ "name": "clarify_arguments",
69
+ "description": "Ask a question to the user to get more information. This function should be called when the AI needs clarification on arguments to pass to a different function call. Should always be called for snippet_clip_to_continue_from in get_audio_snippet and whole_song in revise_instrumental_tags.",
70
+ "parameters": {
71
+ "type": "object",
72
+ "properties": {
73
+ "function_name": {
74
+ "type": "string",
75
+ "description": "Function to call that needs clarification on arguments"
76
+ },
77
+ "arguments_to_clarify": {
78
+ "type": "array",
79
+ "items": {
80
+ "type": "string"
81
+ },
82
+ "description": "List of questions to ask the end user to clarify the arguments we don't know"
83
+ }
84
+ },
85
+ "required": ["function_name", "arguments_to_clarify"]
86
+ }
87
+ }
88
+ },
89
  {
90
  "type": "function",
91
  "function": {
 
109
  },
110
  "description": "Lines to revise, as a list of numbers. The first line is labelled 1, second 2, and so on. Lines to change should be assessed based on feedback user provides and where they suggest changes."
111
  },
 
 
 
 
 
 
 
 
112
  "relevant_ideas": {
113
  "type": "array",
114
  "items": {
 
124
  "description": "Words that are requested to be included in revised lines. Unless specific words are requested, this should be empty"
125
  }
126
  },
127
+ "required": ["section_name", "current_section", "lines_to_revise"]
128
  }
129
  }
130
  },
 
159
  }
160
  }
161
  },
162
+ {
163
+ "type": "function",
164
+ "function": {
165
+ "name": "revise_section_lyrics_and_instrumental",
166
+ "description": "Incorporates simultaneous feedback on lyrics and instrumental from a single user message. Call this when the user either has both feedback on lyric changes and instrumental changes that they ask for in a single message or they ask to regenerate a snippet with both changed lyrics and instrumental tags in mind.",
167
+ "parameters": {
168
+ "type": "object",
169
+ "properties": {
170
+ "current_instrumental_tags": {
171
+ "type": "array",
172
+ "items": {
173
+ "type": "string"
174
+ },
175
+ "description": "Current instrumental tags passed to instrumental generator. Deduce from most recent list of instrumental tags in conversation."
176
+ },
177
+ "user_instrumental_feedback": {
178
+ "type": "string",
179
+ "description": "A single sentence summary of the user's feedback on instrumental. Can include suggested moods, genres, or instruments."
180
+ },
181
+ "section_name": {
182
+ "type": "string",
183
+ "description": "Name of section we are revising"
184
+ },
185
+ "current_section": {
186
+ "type": "string",
187
+ "description": "Current version of lyrics in section of the song. The last outputted version of the section before user suggested changes that we are now implementing."
188
+ },
189
+ "lines_to_revise": {
190
+ "type": "array",
191
+ "items": {
192
+ "type": "number"
193
+ },
194
+ "description": "Lines to revise, as a list of numbers. The first line is labelled 1, second 2, and so on. Lines to change should be assessed based on feedback user provides and where they suggest changes."
195
+ },
196
+ "snippet_clip_to_continue_from": {
197
+ "type": "string",
198
+ "description": "The 'continued from clip:' clip id that was used in the original snippet we are now revising. This should be the exact same as 'continued from clip' for the snippet being revised, listed under the original section's snippet's audioplayer; if it is None, this should not be passed."
199
+ },
200
+ "relevant_ideas": {
201
+ "type": "array",
202
+ "items": {
203
+ "type": "string"
204
+ },
205
+ "description": "Ideas that should be included in the lines that are being changed. Should be directly derived from suggested user changes."
206
+ },
207
+ "relevant_words": {
208
+ "type": "array",
209
+ "items": {
210
+ "type": "string"
211
+ },
212
+ "description": "Words that are requested to be included in revised lines. Unless specific words are requested, this should be empty"
213
+ }
214
+
215
+ },
216
+ "required": ["current_instrumental_tags", "user_instrumental_feedback", "section_name", "current_section", "lines_to_revise"]
217
+ }
218
+ }
219
+ },
220
  {
221
  "type": "function",
222
  "function": {
 
235
  },
236
  "snippet_clip_to_continue_from": {
237
  "type": "string",
238
+ "description": "The last approved song clip to continue from. Almost always the most recent clip id outputted in the conversation. This is located in the tool message prior to the assistant message with the audio player for that audio. If user did not receive a snippet for the prior section or ever before, this should not be passed."
239
  }
240
  },
241
  "required": ["snippet_lyrics", "snippet_instrumental_tags"]
 
294
  },
295
  "snippet_clip_to_continue_from": {
296
  "type": "string",
297
+ "description": "The last approved clip ID of the song to continue the remaining sections from. Almost always the most recent clip id outputted in the conversation. This is located in the tool message prior to the assistant message with the audio player for that audio. If user did not receive a snippet for the prior section or ever before, this should not be passed."
298
  },
299
  "sections_written": {
300
  "type": "array",
app.py CHANGED
@@ -1,14 +1,27 @@
1
  from typing import List, Tuple, Dict
2
  import gradio as gr
 
 
3
 
4
  from utils.song_utils import generate_song_seed, get_starting_messages, messages_to_history, update_song_details, get_sections
5
  from chat import model_chat
6
- from openai import OpenAI
7
 
8
  History = List[Tuple[str, str]] # a type: pairs of (query, response), where query is user input and response is system output
9
  Messages = List[Dict[str, str]] # a type: list of messages with role and content
10
 
11
- with gr.Blocks() as demo:
 
 
 
 
 
 
 
 
 
 
 
12
  gr.Markdown("""<center><font size=8>AI Songwriter (alpha)</center>""")
13
  gr.Markdown("""<center><font size=4>Turning your stories into musical poetry. 2024 MIT Senior Thesis.</center>""")
14
 
@@ -18,8 +31,7 @@ with gr.Blocks() as demo:
18
  gr.Markdown("""<center><font size=4>First, let's try to find an interesting concept. Fill out the fields below and generate a song seed.</font></center>""")
19
  gr.Markdown("""<center><font size=3>If you're stuck, check out <a href="https://onestopforwriters.com/emotions" target="_blank">here</a>.</font></center>""")
20
  with gr.Row():
21
- feeling_input = gr.Textbox(label='How are you feeling today? More vulnerable you are, better the song will be.', placeholder='Enter your emotions', scale=2)
22
- songwriter_style = gr.Dropdown(label='Songwriter Style', value = "GPT 4o", choices=["GPT 4o", "d4vd (Indie Rock Ballad - Male)", "Lizzy McAlpine (Indie Pop Folk - Female)", "Phoebe Bridgers (Pop Sad Rock - Female)", "Daniel Caesar (R&B/Soul - Male)"], interactive=True)
23
  # audio_input = gr.Audio(sources=["upload"], type="numpy", label="Instrumental",
24
  # interactive=True, elem_id="instrumental-input")
25
 
@@ -36,7 +48,9 @@ with gr.Blocks() as demo:
36
  title_input = gr.Textbox(label='Title', placeholder='Enter a song title')
37
  genre_input = gr.Textbox(label='Genre', placeholder='Enter a genre')
38
  blurb_input = gr.Textbox(label='Blurb', placeholder='Enter a one-sentence blurb')
39
- instrumental_textbox = gr.TextArea(label="Song Structure", value="Verse 1: 4 measures\nChorus 1: 8 measures\nVerse 2: 8 measures\nChorus 2: 8 measures\nVerse 3: 8 measures\nChorus 3: 8 measures", interactive=True, max_lines=3)
 
 
40
  gr.Markdown("""<center><font size=4>Edit these to your liking and hit 'Continue to Next Step' to start creating!</font></center>""")
41
 
42
  def open_accordion(x):
@@ -52,6 +66,7 @@ with gr.Blocks() as demo:
52
  song_seed = song_seed.split("Suggested Song Concept:")[1].strip()
53
  return song_seed
54
  generate_seed_button.click(generate_song_seed, inputs=[feeling_input], outputs=[instrumental_output]).then(clean_song_seed, inputs=[instrumental_output], outputs=[instrumental_output])
 
55
 
56
  def make_row_visible(x):
57
  return gr.Row(visible=True), gr.Markdown("""<center><font size=4>Here it is! Hit 'Approve' to confirm this concept. Edit the concept directly or hit 'Try Again' to get another suggestion.</font></center>""", visible=True)
@@ -66,37 +81,332 @@ with gr.Blocks() as demo:
66
  return gr.Tabs(selected=id)
67
  continue_btn.click(change_tab, gr.Number(1, visible=False), tabs)
68
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  with gr.TabItem("Generation", id=1): #index is 1
71
  start_song_gen = gr.State(value=False)
72
- gr.Markdown("""<center><font size=4>Now, chat with an AI songwriter to make your song! Tip: get and tune an audio snippet well first and then put effort into the story. Hit finish when ready to hear full song.</font></center>""")
73
- generate_lyrics = gr.Button("STEP 2: Write a song with the AIs!")
74
-
75
  character = gr.State(value="A 18-year old boy who dreams of being a pop star that uplifts people going through the difficulties of life")
76
 
77
  starting_messages, starting_history = get_starting_messages("", "Home", "Missing home", "Ballad", instrumental_textbox.value)
78
-
79
  messages = gr.State(value=starting_messages)
 
80
  # journal_messages = gr.State(value=[journal_starting_message])
81
  # journal_response = gr.State(value="")
82
 
 
 
 
83
  with gr.Row():
84
- chatbot_history = gr.Chatbot(value=starting_history, label='SongChat', placeholder=None, layout='bubble', bubble_full_width=False, height=500, scale=2)
85
- with gr.Column():
86
- songwriter_creativity = gr.Slider(label="Songwriter LLM Temperature", minimum=0, maximum=1, step=0.01, value=1)
87
- lyrics_display = gr.TextArea("[...]", label="Generated Lyrics", show_copy_button=True, container=True)
 
 
 
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  section_meanings = gr.State(value="")
90
  approve_button.click(update_song_details, inputs=[instrumental_output], outputs=[genre_input, title_input, blurb_input]).then(get_sections, inputs=[blurb_input, instrumental_output], outputs=[section_meanings])
91
  continue_btn.click(get_starting_messages, inputs=[instrumental_textbox, title_input, blurb_input, genre_input, section_meanings], outputs=[messages, chatbot_history])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  with gr.Row():
94
- textbox = gr.Textbox(lines=1, label='Send a message', show_label=False, placeholder='Send a message', scale=4)
95
- submit = gr.Button("Send", scale=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  with gr.Row():
98
- get_snippet_button = gr.Button("Get Audio Snippet", scale=2)
99
- done = gr.Button("Finish Full Song 🎶", scale=2)
100
  #autoGPT_checkbox = gr.Checkbox(label="AutoGPT", value=True, info="Auto-generate responses from journal entry", interactive=True, scale=2)
101
  #journal_llm_creativity = gr.Slider(label="Journal LLM Temperature", minimum=0, maximum=1, step=0.01, value=1, interactive=True, scale=2)
102
  reset_button = gr.Button("Reset", scale=2)
@@ -104,57 +414,39 @@ with gr.Blocks() as demo:
104
  def reset_chat(messages, chatbot_history):
105
  messages = messages[:2]
106
  chatbot_history = messages_to_history(messages[:2])
107
- return messages, chatbot_history, ''
108
 
109
- reset_button.click(reset_chat, inputs=[messages, chatbot_history], outputs=[messages, chatbot_history, lyrics_display])
110
 
111
 
112
- with gr.Row():
113
- song_link = gr.State(value="")
114
- song = gr.HTML()
 
 
 
 
 
 
 
115
 
116
- # download_btn = gr.Button("Download Conversation")
117
 
118
- # def download_conversation(messages):
119
- # #get time
120
- # now = get_current_time()
121
- # # write messages to JSON file
122
- # with open(f'conversation_{now}.json', 'w') as f:
123
- # json.dump(messages, f)
124
 
125
 
126
- # with gr.Accordion("Advanced", open=False):
127
- # suno_tags = gr.Textbox(value="ballad, male, dramatic, emotional, strings", label="Gen input tags")
 
128
  # story_textbox = gr.TextArea(label="Story to provide context to songwriter", value="", max_lines=3)
129
 
 
 
 
 
130
 
131
- def reset_textbox(textbox):
132
- return ""
133
- def set_snippet_query(textbox):
134
- return "Can I have an audio snippet of what we have now?"
135
- def set_finish_query(textbox):
136
- return "I'm ready for the full song now! Can you finish it up?"
137
- def set_lyrics_song_displays(messages):
138
- final_message = messages[-1]['content']
139
- final_lyrics = final_message.split("Final Lyrics:")[1].split("Final song:")[0].strip("\n ")
140
- song = final_message.split("Final song:")[1].strip("\n ")
141
- return final_lyrics, song
142
-
143
- submit.click(model_chat,
144
- inputs=[genre_input, textbox, chatbot_history, messages],
145
- outputs=[textbox, chatbot_history, messages, lyrics_display]).then(reset_textbox, inputs=[textbox], outputs=[textbox])
146
- textbox.submit(model_chat,
147
- inputs=[genre_input, textbox, chatbot_history, messages],
148
- outputs=[textbox, chatbot_history, messages, lyrics_display]).then(reset_textbox, inputs=[textbox], outputs=[textbox])
149
-
150
- get_snippet_button.click(set_snippet_query, inputs=[textbox], outputs=[textbox]).then(model_chat,
151
- inputs=[genre_input, textbox, chatbot_history, messages],
152
- outputs=[textbox, chatbot_history, messages]).then(reset_textbox, inputs=[textbox], outputs=[textbox])
153
-
154
- done.click(set_finish_query, inputs=[textbox], outputs=[textbox]).then(model_chat,
155
- inputs=[genre_input, textbox, chatbot_history, messages],
156
- outputs=[textbox, chatbot_history, messages, lyrics_display]).then(
157
- set_lyrics_song_displays, inputs=[messages], outputs=[lyrics_display, song]).then(reset_textbox, inputs=[textbox], outputs=[textbox])
158
 
159
 
160
  demo.queue(api_open=False)
 
1
  from typing import List, Tuple, Dict
2
  import gradio as gr
3
+ import os
4
+ import json
5
 
6
  from utils.song_utils import generate_song_seed, get_starting_messages, messages_to_history, update_song_details, get_sections
7
  from chat import model_chat
8
+ from gradio_modal import Modal
9
 
10
  History = List[Tuple[str, str]] # a type: pairs of (query, response), where query is user input and response is system output
11
  Messages = List[Dict[str, str]] # a type: list of messages with role and content
12
 
13
+ css = """
14
+ #audio-group {
15
+ max-height: 800px;
16
+ overflow-y: scroll;
17
+ }
18
+ """
19
+
20
+ textbox = gr.Textbox(lines=1, label='Send a message', show_label=False, placeholder='Send a message', scale=4, visible=True)
21
+ submit = gr.Button("Send", scale=2, visible=True)
22
+
23
+
24
+ with gr.Blocks(css=css) as demo:
25
  gr.Markdown("""<center><font size=8>AI Songwriter (alpha)</center>""")
26
  gr.Markdown("""<center><font size=4>Turning your stories into musical poetry. 2024 MIT Senior Thesis.</center>""")
27
 
 
31
  gr.Markdown("""<center><font size=4>First, let's try to find an interesting concept. Fill out the fields below and generate a song seed.</font></center>""")
32
  gr.Markdown("""<center><font size=3>If you're stuck, check out <a href="https://onestopforwriters.com/emotions" target="_blank">here</a>.</font></center>""")
33
  with gr.Row():
34
+ feeling_input = gr.Textbox(label="What's an emotion(s) that you've been feeling a lot recently? And why?", placeholder='Enter your emotions', scale=2)
 
35
  # audio_input = gr.Audio(sources=["upload"], type="numpy", label="Instrumental",
36
  # interactive=True, elem_id="instrumental-input")
37
 
 
48
  title_input = gr.Textbox(label='Title', placeholder='Enter a song title')
49
  genre_input = gr.Textbox(label='Genre', placeholder='Enter a genre')
50
  blurb_input = gr.Textbox(label='Blurb', placeholder='Enter a one-sentence blurb')
51
+ songwriter_style = gr.Dropdown(label='Songwriter Style', value = "GPT 4o", choices=["GPT 4o", "d4vd (Indie Rock Ballad - Male)", "Lizzy McAlpine (Indie Pop Folk - Female)", "Phoebe Bridgers (Pop Sad Rock - Female)", "Daniel Caesar (R&B/Soul - Male)"], interactive=True)
52
+
53
+ instrumental_textbox = gr.TextArea(label="Song Structure", value="Verse 1: 4 measures\nChorus 1: 8 measures\nVerse 2: 8 measures\nChorus 2: 8 measures\nVerse 3: 8 measures\nChorus 3: 8 measures", visible=False, interactive=True, max_lines=3)
54
  gr.Markdown("""<center><font size=4>Edit these to your liking and hit 'Continue to Next Step' to start creating!</font></center>""")
55
 
56
  def open_accordion(x):
 
66
  song_seed = song_seed.split("Suggested Song Concept:")[1].strip()
67
  return song_seed
68
  generate_seed_button.click(generate_song_seed, inputs=[feeling_input], outputs=[instrumental_output]).then(clean_song_seed, inputs=[instrumental_output], outputs=[instrumental_output])
69
+ feeling_input.submit(generate_song_seed, inputs=[feeling_input], outputs=[instrumental_output]).then(clean_song_seed, inputs=[instrumental_output], outputs=[instrumental_output])
70
 
71
  def make_row_visible(x):
72
  return gr.Row(visible=True), gr.Markdown("""<center><font size=4>Here it is! Hit 'Approve' to confirm this concept. Edit the concept directly or hit 'Try Again' to get another suggestion.</font></center>""", visible=True)
 
81
  return gr.Tabs(selected=id)
82
  continue_btn.click(change_tab, gr.Number(1, visible=False), tabs)
83
 
84
+ # with gr.TabItem("Tutorial", id=1):
85
+ # gr.Markdown("""<center><font size=4>Now, let's walkthrough writing a verse together! Start chatting with the chatbot.</font></center>""")
86
+
87
+ # character = gr.State(value="A 18-year old boy who dreams of being a pop star that uplifts people going through the difficulties of life")
88
+
89
+ # starting_messages, starting_history = get_starting_messages("", "Home", "Missing home", "Ballad", instrumental_textbox.value)
90
+
91
+ # messages = gr.State(value=starting_messages)
92
+
93
+ # section_meanings = gr.State(value="")
94
+ # approve_button.click(update_song_details, inputs=[instrumental_output], outputs=[genre_input, title_input, blurb_input]).then(get_sections, inputs=[blurb_input, instrumental_output], outputs=[section_meanings])
95
+
96
+ # # lyrics_display = gr.State("")
97
+
98
+ # generated_audios = gr.State([])
99
+ # def reset_textbox(textbox):
100
+ # return ""
101
+
102
+ # character = gr.State(value="A 18-year old boy who dreams of being a pop star that uplifts people going through the difficulties of life")
103
 
104
+ # starting_messages, starting_history = get_starting_messages("", "Home", "Missing home", "Ballad", instrumental_textbox.value)
105
+ # print(starting_history, "STARTING HISTORY")
106
+ # messages = gr.State(value=starting_messages)
107
+ # # messages += [{"role": "assistant", "content": "You are a songwriter. You write songs."}]
108
+ # # journal_messages = gr.State(value=[journal_starting_message])
109
+ # # journal_response = gr.State(value="")
110
+
111
+ # generated_audios = gr.State(value=[])
112
+ # tutorial_step = gr.Number(0, visible=False)
113
+
114
+ # with gr.Row():
115
+ # with gr.Column(scale=2):
116
+ # chatbot_history = gr.Chatbot(type="messages", value=starting_history, label='SongChat', placeholder=None, layout='bubble', bubble_full_width=False, height=500)
117
+ # with gr.Row():
118
+ # typical_responses = [textbox, submit]
119
+
120
+ # def update_response_options(buttons, button_dict):
121
+ # return [gr.Textbox(visible=len(buttons)==0, scale=4), gr.Button(visible=len(buttons)==0, scale=2)] + [gr.Button(visible=(x in buttons)) for x in button_dict.keys()]
122
+
123
+ # button_options = gr.State([])
124
+ # button_dict = gr.State({
125
+ # "revise lyrics": "Can we revise the lyrics?",
126
+ # "generate audio snippet": "Can you generate an audio snippet?",
127
+ # "continue revising" : "Can we continue revising this section?",
128
+ # "generate audio snippet with new lyrics": "Can you generate an audio snippet with these new lyrics?",
129
+ # "return to original instrumental": "Can you use the original clip for this section instead?",
130
+ # "revise genre": "Can we revise the instrumental tags?",
131
+ # "re-revise genre": "Can we revise the instrumental tags?",
132
+ # "continue to next section": "Looks good! Let's move on to the next section.",
133
+ # "merge snippets": "Can you merge this snippet into its full song?"
134
+ # })
135
+
136
+ # for button in button_dict.value.keys():
137
+ # btn = gr.Button(button, visible=(button in button_options.value))
138
+ # typical_responses.append(btn)
139
+
140
+
141
+ # with gr.Column(elem_id="audio-group", scale=1) as audio_group_column:
142
+ # # songwriter_creativity = gr.Slider(label="Songwriter LLM Temperature", minimum=0, maximum=1, step=0.01, value=1)
143
+
144
+ # with gr.Group():
145
+ # # loop thru all audio in audio_clips
146
+ # gr.Markdown("""<center><font size=4>All Generations</font></center>""")
147
+
148
+ # @gr.render(inputs=generated_audios, triggers=[demo.load, generated_audios.change, textbox.submit, submit.click] + [btn.click for btn in typical_responses[2:]])
149
+ # def render_audio_group(generated_audios):
150
+ # # audio_group = gr.Group()
151
+ # for audio in generated_audios:
152
+ # clip_path, lyrics, instrumental, title, status = audio
153
+ # with gr.Accordion(title, open=False):
154
+ # if status == 'complete':
155
+ # gr.Audio(value=clip_path, label=title, interactive=False, show_label=False, waveform_options={"show_controls": False})
156
+ # else:
157
+ # gr.HTML(f'<audio controls><source src="{clip_path}" type="audio/mp3"></audio>')
158
+ # gr.TextArea(label="Lyrics", value=lyrics, interactive=False, show_label=False)
159
+ # gr.TextArea(label="Instrumental", value=instrumental, interactive=False, show_label=False, max_lines=1)
160
+
161
+ # gr.Markdown("""<center><font size=4>Current Generation</font></center>""")
162
+ # current_section = gr.Textbox(label="Current section", value="Verse 1", interactive=False, show_label=True)
163
+ # current_lyrics = gr.Textbox(label="Lyrics", value="", interactive=True, show_label=True)
164
+ # with gr.Row():
165
+ # curr_tags = gr.Textbox(label="Instrumental Tags", value="", interactive=True, show_label=True)
166
+ # # @gr.render(inputs=generated_audios, triggers=[demo.load])
167
+ # # def render_clip_to_continue(generated_audios):
168
+ # audio_clips = [x[3] for x in generated_audios.value]
169
+ # clip_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=audio_clips+[""], interactive=True)
170
+ # #clip_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=audio_clips+[""], interactive=True)
171
+ # songwriter_style = gr.Dropdown(label='Songwriter Style', value= "GPT 4o", choices=["GPT 4o", "d4vd"], interactive=True)
172
+ # with gr.Row():
173
+ # #curr_audio = gr.State("")
174
+ # curr_audio = gr.HTML(label="Generated section")
175
+ # regen = gr.Button("Re-generate")
176
+
177
+
178
+ # section_meanings = gr.State(value="")
179
+ # approve_button.click(update_song_details, inputs=[instrumental_output], outputs=[genre_input, title_input, blurb_input]).then(get_sections, inputs=[blurb_input, instrumental_output], outputs=[section_meanings])
180
+ # continue_btn.click(get_starting_messages, inputs=[instrumental_textbox, title_input, blurb_input, genre_input, section_meanings], outputs=[messages, chatbot_history])
181
+
182
+ # def set_response_buttons(button_dict, button_name):
183
+ # print(button_name)
184
+ # return button_dict[button_name]
185
+
186
+
187
+ # with gr.Row(visible=True) as chat_row_0:
188
+ # textbox_0 = gr.Textbox(lines=1, label='Send a message', show_label=False, placeholder='Send a message', scale=4)
189
+ # submit_0 = gr.Button("Send", scale=2)
190
+
191
+ # for btn in typical_responses[2:]:
192
+ # btn.click(set_response_buttons, inputs=[button_dict, btn], outputs=[textbox]).then(model_chat,
193
+ # inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
194
+ # outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox], outputs=[textbox]).then(
195
+ # update_response_options, [button_options, button_dict], typical_responses
196
+ # )
197
+
198
+
199
+
200
+ # with Modal(visible=False) as modal_0:
201
+ # gr.Markdown("Welcome to the AI songwriter! The AI songwriter will help you write a song. You can chat with the AI, generate lyrics, and listen to audio snippets. Let's start by chatting with the AI.")
202
+ # with Modal(visible=False) as modal:
203
+ # gr.Markdown("The chatbot is run by an AI songwriter. It can respond to your conversations, generate lyrics and audio, and edit prior generations.\n\nNow, continue and respond to this second question from the AI songwriter.")
204
+ # with Modal(visible=False) as modal_1:
205
+ # gr.Markdown("The AI songwriter has now proposed a first verse! You now have the option to hear an audio snippet, revise the lyrics, or continue to the next section. The latter two options continue the conversation, whereas the first starts audio generation models. Select the 'get audio snippet' button to continue to the next step.")
206
+ # with Modal(visible=False) as modal_2:
207
+ # gr.Markdown("Awesome! You generated your first audio snippet./n/n As you work thru each section, the generated snippets are populated on the right panel. You'll be able to listen thru snippets as you work thru the song. \n\n "
208
+ # "The current section is also displayed on the right panel. You'll be able to revise sections via the chat or directly via the right panel. \n\n "
209
+ # "You're ready to start your official song! Hit the 'Start' button to start.")
210
+ # start_button = gr.Button("Start")
211
+
212
+ # # start_button.click(change_tab, gr.Number(2, visible=False), tabs).then(update_generation_tab,
213
+ # # inputs=[],
214
+ # # outputs=[])
215
+ # continue_btn.click(lambda: Modal(visible=True), None, modal_0)
216
+
217
+ # def make_modal_visible(step_number):
218
+ # new_step_number = step_number + 1 if step_number in [0, 1, 2] else step_number
219
+ # modals = [Modal(visible=i == step_number) for i in range(3)]
220
+ # return new_step_number, *modals
221
+
222
+
223
+
224
+
225
+ # submit_0.click(update_textbox, [textbox_0, tutorial_step], [textbox_0]).then(model_chat,
226
+ # inputs=[genre_input, textbox_0, chatbot_history, messages, generated_audios],
227
+ # outputs=[textbox_0, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox_0], outputs=[textbox_0]).then(
228
+ # update_response_options, [button_options, button_dict], typical_responses
229
+ # ).then(
230
+ # make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
231
+ # )
232
+ # textbox_0.submit(update_textbox, [textbox_0, tutorial_step], [textbox_0]).then(model_chat,
233
+ # inputs=[genre_input, textbox_0, chatbot_history, messages, generated_audios],
234
+ # outputs=[textbox_0, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox_0], outputs=[textbox_0]).then(
235
+ # update_response_options, [button_options, button_dict], typical_responses
236
+ # ).then(
237
+ # make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
238
+ # )
239
+
240
+
241
+
242
  with gr.TabItem("Generation", id=1): #index is 1
243
  start_song_gen = gr.State(value=False)
244
+ gr.Markdown("""<center><font size=4>Now, chat with an AI songwriter to make your song! Hit finish when ready to hear full song.</font></center>""")
245
+
 
246
  character = gr.State(value="A 18-year old boy who dreams of being a pop star that uplifts people going through the difficulties of life")
247
 
248
  starting_messages, starting_history = get_starting_messages("", "Home", "Missing home", "Ballad", instrumental_textbox.value)
249
+ print(starting_history, "STARTING HISTORY")
250
  messages = gr.State(value=starting_messages)
251
+ # messages += [{"role": "assistant", "content": "You are a songwriter. You write songs."}]
252
  # journal_messages = gr.State(value=[journal_starting_message])
253
  # journal_response = gr.State(value="")
254
 
255
+ generated_audios = gr.State(value=[])
256
+ tutorial_step = gr.Number(value=0, visible=False)
257
+
258
  with gr.Row():
259
+ with gr.Column(scale=2):
260
+ chatbot_history = gr.Chatbot(type="messages", value=starting_history, label='SongChat', placeholder=None, layout='bubble', bubble_full_width=False, height=500)
261
+ with gr.Row():
262
+ typical_responses = [textbox, submit]
263
+
264
+ def update_response_options(buttons, button_dict):
265
+ return [gr.Textbox(visible=len(buttons)==0, scale=4), gr.Button(visible=len(buttons)==0, scale=2)] + [gr.Button(visible=(x in buttons)) for x in button_dict.keys()]
266
 
267
+ button_options = gr.State([])
268
+ button_dict = gr.State({
269
+ "revise lyrics": "Can we revise the lyrics?",
270
+ "generate audio snippet": "Can you generate an audio snippet?",
271
+ "continue revising" : "Can we continue revising this section?",
272
+ "generate audio snippet with new lyrics": "Can you generate an audio snippet with these new lyrics?",
273
+ "return to original instrumental": "Can you use the original clip for this section instead?",
274
+ "revise genre": "Can we revise the instrumental tags?",
275
+ "re-revise genre": "Can we revise the instrumental tags?",
276
+ "continue to next section": "Looks good! Let's move on to the next section.",
277
+ "merge snippets": "Can you merge this snippet into its full song?"
278
+ })
279
+
280
+ for button in button_dict.value.keys():
281
+ btn = gr.Button(button, visible=(button in button_options.value))
282
+ typical_responses.append(btn)
283
+
284
+
285
+ with gr.Column(elem_id="audio-group", scale=1):
286
+ # songwriter_creativity = gr.Slider(label="Songwriter LLM Temperature", minimum=0, maximum=1, step=0.01, value=1)
287
+
288
+ with gr.Group():
289
+ # loop thru all audio in audio_clips
290
+ gr.Markdown("""<center><font size=4>All Generations</font></center>""")
291
+
292
+ @gr.render(inputs=generated_audios, triggers=[demo.load, generated_audios.change, textbox.submit, submit.click] + [btn.click for btn in typical_responses[2:]])
293
+ def render_audio_group(generated_audios):
294
+ # audio_group = gr.Group()
295
+ for audio in generated_audios:
296
+ clip_path, lyrics, instrumental, title, status = audio
297
+ with gr.Accordion(title, open=False):
298
+ if status == 'complete':
299
+ gr.Audio(value=clip_path, label=title, interactive=False, show_label=False, waveform_options={"show_controls": False})
300
+ else:
301
+ gr.HTML(f'<audio controls><source src="{clip_path}" type="audio/mp3"></audio>')
302
+ gr.TextArea(label="Lyrics", value=lyrics, interactive=False, show_label=False)
303
+ gr.TextArea(label="Instrumental", value=instrumental, interactive=False, show_label=False, max_lines=1)
304
+
305
+ gr.Markdown("""<center><font size=4>Current Generation</font></center>""")
306
+ current_section = gr.Textbox(label="Current section", value="Verse 1", interactive=False, show_label=True)
307
+ current_lyrics = gr.Textbox(label="Lyrics", value="", interactive=True, show_label=True)
308
+ with gr.Row():
309
+ curr_tags = gr.Textbox(label="Instrumental Tags", value="", interactive=True, show_label=True)
310
+ # @gr.render(inputs=generated_audios, triggers=[demo.load])
311
+ # def render_clip_to_continue(generated_audios):
312
+ audio_clips = [x[3] for x in generated_audios.value]
313
+ clip_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=audio_clips+[""], interactive=True)
314
+ #clip_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=audio_clips+[""], interactive=True)
315
+ songwriter_style = gr.Dropdown(label='Songwriter Style', value= "GPT 4o", choices=["GPT 4o", "d4vd"], interactive=True)
316
+ with gr.Row():
317
+ #curr_audio = gr.State("")
318
+ curr_audio = gr.HTML(label="Generated section")
319
+ regen = gr.Button("Re-generate")
320
+
321
+
322
  section_meanings = gr.State(value="")
323
  approve_button.click(update_song_details, inputs=[instrumental_output], outputs=[genre_input, title_input, blurb_input]).then(get_sections, inputs=[blurb_input, instrumental_output], outputs=[section_meanings])
324
  continue_btn.click(get_starting_messages, inputs=[instrumental_textbox, title_input, blurb_input, genre_input, section_meanings], outputs=[messages, chatbot_history])
325
+
326
+ with Modal(visible=False) as modal_0:
327
+ gr.Markdown("Welcome to the AI songwriter! The AI songwriter will help you write a song. You can chat with the AI, generate lyrics, and listen to audio snippets. Let's start by chatting with the AI.")
328
+ with Modal(visible=False) as modal:
329
+ gr.Markdown("The chatbot is run by an AI songwriter. It can respond to your conversations, generate lyrics and audio, and edit prior generations.\n\nNow, continue and respond to this second question from the AI songwriter.")
330
+ with Modal(visible=False) as modal_1:
331
+ gr.Markdown("The AI songwriter has now proposed a first verse! You now have the option to hear an audio snippet, revise the lyrics, or continue to the next section. The latter two options continue the conversation, whereas the first starts audio generation models. Select the 'get audio snippet' button to continue to the next step.")
332
+ with Modal(visible=False) as modal_2:
333
+ gr.Markdown("Awesome! You generated your first audio snippet./n/n As you work thru each section, the generated snippets are populated on the right panel. You'll be able to listen thru snippets as you work thru the song. \n\n "
334
+ "The current section is also displayed on the right panel. You'll be able to revise sections via the chat or directly via the right panel. \n\n "
335
+ "You're ready to start your official song! Hit the 'Start' button to start.")
336
+ start_button = gr.Button("Start")
337
+
338
+ continue_btn.click(lambda: Modal(visible=True), None, modal_0)
339
+ start_button.click(lambda: Modal(visible=False), None, modal_2)
340
+
341
+ def make_modal_visible(step_number):
342
+ new_step_number = step_number + 1 if step_number in [0, 1, 2] else step_number
343
+ modals = [Modal(visible=i == step_number) for i in range(3)]
344
+ return new_step_number, *modals
345
+
346
+ def update_textbox(textbox, step_number):
347
+ if step_number == 0:
348
+ return textbox + "\nAsk me another question to inform the verse"
349
+ elif step_number == 1:
350
+ return textbox + "\nUse this info to write a verse"
351
+ else:
352
+ return textbox
353
+
354
+ def set_response_buttons(button_dict, button_name):
355
+ print(button_name)
356
+ return button_dict[button_name]
357
+
358
+ def set_regenerate_query(textbox, current_section, current_lyrics, curr_tags, clip_to_continue):
359
+ return f"Can you revise this section so it uses these lyrics and instrumentals and then generate an audio snippet using it?\nLyrics:\n{current_lyrics}Instrumental tags: {curr_tags}\nClip to continue: {clip_to_continue}"
360
+ def set_snippet_query(textbox):
361
+ return "Can I have an audio snippet of what we have now?"
362
+ def set_finish_query(textbox):
363
+ return "I'm ready for the full song now! Can you finish it up?"
364
+ def reset_textbox(textbox):
365
+ return ""
366
 
367
  with gr.Row():
368
+ textbox.render()
369
+ submit.render()
370
+
371
+ for btn in typical_responses[2:]:
372
+ btn.click(set_response_buttons, inputs=[button_dict, btn], outputs=[textbox]).then(model_chat,
373
+ inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
374
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox], outputs=[textbox]).then(
375
+ update_response_options, [button_options, button_dict], typical_responses
376
+ ).then(
377
+ make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
378
+ )
379
+
380
+
381
+
382
+
383
+ submit.click(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
384
+ inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
385
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox], outputs=[textbox]).then(
386
+ update_response_options, [button_options, button_dict], typical_responses
387
+ ).then(
388
+ make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
389
+ )
390
+ textbox.submit(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
391
+ inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
392
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox], outputs=[textbox]).then(
393
+ update_response_options, [button_options, button_dict], typical_responses
394
+ ).then(
395
+ make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
396
+ )
397
+
398
+
399
+ regen.click(set_regenerate_query, inputs=[textbox, current_section, current_lyrics, curr_tags, clip_to_continue], outputs=[textbox]).then(model_chat,
400
+ inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
401
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(reset_textbox, inputs=[textbox], outputs=[textbox]).then(
402
+ update_response_options, [button_options, button_dict], typical_responses
403
+ ).then(
404
+ make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
405
+ )
406
 
407
  with gr.Row():
408
+ # get_snippet_button = gr.Button("Get Audio Snippet", scale=2)
409
+ done = gr.Button("Finish Full Song 🎶", scale=4)
410
  #autoGPT_checkbox = gr.Checkbox(label="AutoGPT", value=True, info="Auto-generate responses from journal entry", interactive=True, scale=2)
411
  #journal_llm_creativity = gr.Slider(label="Journal LLM Temperature", minimum=0, maximum=1, step=0.01, value=1, interactive=True, scale=2)
412
  reset_button = gr.Button("Reset", scale=2)
 
414
  def reset_chat(messages, chatbot_history):
415
  messages = messages[:2]
416
  chatbot_history = messages_to_history(messages[:2])
417
+ return messages, chatbot_history, '', '', '', '', gr.HTML('<center>generating...</center>'), [], []
418
 
419
+ reset_button.click(reset_chat, inputs=[messages, chatbot_history], outputs=[messages, chatbot_history, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options])
420
 
421
 
422
+ done.click(set_finish_query, inputs=[textbox], outputs=[textbox]).then(model_chat,
423
+ inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
424
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options])
425
+
426
+ demo.load(reset_chat, inputs=[messages, chatbot_history], outputs=[messages, chatbot_history, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios])
427
+
428
+
429
+ # with gr.Row():
430
+ # song_link = gr.State(value="")
431
+ # song = gr.HTML()
432
 
 
433
 
434
+
435
+ def download_conversation(messages):
436
+ with open(f'data/conversation_history.json', 'w') as f:
437
+ json.dump(messages, f)
 
 
438
 
439
 
440
+ with gr.Accordion("Admin", open=False):
441
+ download_btn = gr.Button("Download Conversation")
442
+ download_btn.click(download_conversation, [messages], None)
443
  # story_textbox = gr.TextArea(label="Story to provide context to songwriter", value="", max_lines=3)
444
 
445
+
446
+ # get_snippet_button.click(set_snippet_query, inputs=[textbox], outputs=[textbox]).then(model_chat,
447
+ # inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
448
+ # outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios]).then(reset_textbox, inputs=[textbox], outputs=[textbox])
449
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
 
451
 
452
  demo.queue(api_open=False)
chat.py CHANGED
@@ -2,8 +2,17 @@ from typing import List, Optional, Tuple, Dict
2
  import os
3
  import json
4
  from openai import OpenAI
 
 
 
 
 
 
5
 
6
- from suno import make_song, concat_snippets
 
 
 
7
  from gpt_calls import AI_Songwriter
8
  from utils.song_utils import messages_to_history
9
 
@@ -11,17 +20,21 @@ History = List[Tuple[str, str]] # a type: pairs of (query, response), where quer
11
  Messages = List[Dict[str, str]] # a type: list of messages with role and content
12
 
13
  client_key = os.getenv("OPEN_AI_KEY")
 
14
  oai_client = OpenAI(
15
  api_key=client_key,
16
  )
17
 
18
- def model_chat(genre_input, query: Optional[str], history: Optional[History], messages: Optional [Messages], auto=False) -> Tuple[str, str, History, Messages]:
19
- if query is None:
20
- query = ''
 
 
21
 
22
- if not query.endswith('?'):
23
- query += " Use write_section when you have a large amount of story to pull from to write the next section! Alternatively ask me a follow up before moving to write."
24
 
 
 
 
25
  with open('ai_tools.json') as f:
26
  ai_tools = json.load(f)
27
 
@@ -32,14 +45,13 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
32
  else:
33
  messages = messages + [{'role': 'user', 'content': query}]
34
 
35
-
36
 
37
  messages_filtered = messages
38
  response_message = oai_client.chat.completions.create(
39
  model="gpt-4o",
40
  messages=messages_filtered,
41
  tools = ai_tools,
42
- tool_choice="auto",
43
  )
44
  print(response_message, "model chat response")
45
  current_response = ""
@@ -52,18 +64,124 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
52
  "tool_calls": tool_calls,
53
  "function_call": response_message.choices[0].message.function_call
54
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  # If true the model will return the name of the tool / function to call and the argument(s)
56
  for tool_call in tool_calls:
57
  print(tool_call)
58
  tool_call_id = tool_call.id
59
  tool_function_name = tool_call.function.name
60
  tool_query_args = eval(tool_call.function.arguments)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- # Step 3: Call the function and retrieve results. Append the results to the messages list.
63
- if tool_function_name == 'write_section':
64
  snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
65
  snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
66
  suggested_lyrics = songwriterAssistant.write_section(**tool_query_args)
 
67
 
68
  ## yield suggested lyrics in tool and assistant message
69
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': suggested_lyrics}
@@ -77,35 +195,36 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
77
  current_response = model_response_with_function_call.choices[0].message.content
78
 
79
  role = "assistant"
80
- new_messages = new_messages + [{'role': role, 'content': current_response}]
81
  # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
82
  history = messages_to_history(new_messages)
83
- yield '', history, new_messages, '[...]'
 
 
 
 
 
84
 
85
 
86
- # new_history = messages_to_history(new_messages)
87
- # yield '', new_history, new_messages, '[...]'
88
 
89
- # ### call make_song here with the snippet_lyrics, snippet_instrumental_tags, and snippet_clip_to_continue
90
- # song_link = make_song(suggested_lyrics, snippet_instrumental_tags, snippet_clip_to_continue)
91
 
92
- # ## filter out suno link from tool query arg
93
- # clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
94
 
95
- # ## add song link to tool and audio message
96
- # tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': suggested_lyrics + '\nclip id: ' + clip_id}
97
- # audio_message = {'role': 'assistant', 'content': "Here's what I've come up with:\n" + suggested_lyrics + '\n\n' + f'<audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio><p>clip id: {clip_id}</p><p>instrumental tags: {snippet_instrumental_tags}</p>'}
98
- # audio_message['content'] += f'<p>continued from clip: {snippet_clip_to_continue}</p>'
99
- # audio_message['content'] += f'\n\nWhat do you think?'
100
- # new_messages = messages + [tool_message, audio_message]
101
- # new_history = messages_to_history(new_messages)
102
- # yield '', new_history, new_messages, '[...]'
103
 
104
  elif tool_function_name == 'revise_section_lyrics':
105
- snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
106
- snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
107
  revised_lyrics = songwriterAssistant.revise_section_lyrics(**tool_query_args)
108
 
 
 
 
 
 
 
 
 
 
 
109
  # ## yield revised lyrics in tool and assistant message
110
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': revised_lyrics}
111
  # audio_message = {'role': 'assistant', 'content': "Here's my revised lyrics:\n" + revised_lyrics + "\n\nGenerating audio snippet..."}
@@ -117,45 +236,36 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
117
  ) # get a new response from the model where it can see the function response
118
  current_response = model_response_with_function_call.choices[0].message.content
119
 
 
 
120
  role = "assistant"
121
- new_messages = new_messages + [{'role': role, 'content': current_response}]
122
  # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
123
  history = messages_to_history(new_messages)
124
- yield '', history, new_messages, '[...]'
125
- # new_history = messages_to_history(new_messages)
126
- # yield '', new_history, new_messages, '[...]'
127
-
128
- # ### call make_song here with the snippet_lyrics, snippet_instrumental_tags, and snippet_clip_to_continue
129
- # song_link = make_song(revised_lyrics, snippet_instrumental_tags, snippet_clip_to_continue)
130
 
131
- # ## filter out suno link from tool query arg
132
- # clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
133
 
134
- # ## add song link to tool and audio message
135
- # tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': revised_lyrics + '\nclip id: ' + clip_id}
136
- # audio_message = {'role': 'assistant', 'content': "Here's what I've come up with:\n" + revised_lyrics + '\n\n' + f'<audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio><p>clip id: {clip_id}</p><p>instrumental tags: {snippet_instrumental_tags}</p>'}
137
- # audio_message['content'] += f'<p>continued from clip: {snippet_clip_to_continue}</p>'
138
- # audio_message['content'] += f'\n\nWhat do you think?'
139
- # new_messages = messages + [tool_message, audio_message]
140
- # new_history = messages_to_history(new_messages)
141
- # yield '', new_history, new_messages, '[...]'
142
 
143
  elif tool_function_name == 'revise_instrumental_tags':
144
  #detangle tool_query_args dict
145
  #snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
146
- snippet_instrumental_tags = tool_query_args['current_instrumental_tags']
147
- user_instrumental_feedback = tool_query_args['user_instrumental_feedback']
 
 
 
 
 
 
 
 
148
  # if 'snippet_clip_to_continue_from' not in tool_query_args:
149
  # tool_query_args['snippet_clip_to_continue_from'] = None
150
  # snippet_clip_to_continue_from = tool_query_args['snippet_clip_to_continue_from']
151
 
152
  new_instrumental_tags = songwriterAssistant.revise_instrumental_tags(snippet_instrumental_tags, user_instrumental_feedback)
153
- # yield new_instrumental_tags in tool and assistant message
154
- # tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'new instrumental tags: {new_instrumental_tags}'}
155
- # audio_message = {'role': 'assistant', 'content': f'Sure! I\'ve revised the instrumental tags: {new_instrumental_tags}\n\n Generating audio snippet...'}
156
- # new_messages = messages + [tool_message, audio_message]
157
- # new_history = messages_to_history(new_messages)
158
- # yield '', new_history, new_messages, '[...]'
159
 
160
  if isinstance(tool_query_args['sections_written'], str):
161
  current_lyrics = tool_query_args['sections_written']
@@ -164,28 +274,116 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
164
  else:
165
  current_lyrics = ""
166
 
 
 
 
167
  #current_lyrics = "\n".join(tool_query_args['sections_written'])
168
  song_link = make_song(current_lyrics, new_instrumental_tags)
169
  ## filter out suno link from tool query arg
 
 
 
 
 
170
  clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
171
 
172
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'new instrumental tags: {new_instrumental_tags}, clip id: {clip_id}'}
173
- audio_message = {'role': 'assistant', 'content': f'Sure! I\'ve revised the instrumental tags: {new_instrumental_tags}\nCurrent lyrics: {current_lyrics}\n\n <audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio><p>clip id: {clip_id}</p>'}
174
  audio_message['content'] += f'\n\nWhat do you think?'
175
  new_messages = messages + [tool_message, audio_message]
176
  new_history = messages_to_history(new_messages)
177
- yield '', new_history, new_messages, '[...]'
178
- elif tool_function_name == 'merge_all_snippets':
179
- updated_clip_url, updated_lyrics, clips_list = concat_snippets(tool_query_args['last_snippet_id'])
180
- updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
181
 
182
- #pass this info in new tool and assistant message
183
- tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'updated clip id: {updated_clip_id}\nupdated lyrics: {updated_lyrics}\nupdated clips path: {clips_list}'}
184
- audio_message = {'role': 'assistant', 'content': f'Sure! All the clips are now merged. <p>updated lyrics: {updated_lyrics}</p><audio controls autoplay><source src="{updated_clip_url}" type="audio/mp3"></audio><p>updated clip id: {updated_clip_id}</p><p>updated clips path: {clips_list}</p>'}
 
 
 
185
 
186
- new_messages = messages + [tool_message, audio_message]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  new_history = messages_to_history(new_messages)
188
- yield '', new_history, new_messages, '[...]'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  elif tool_function_name == 'finish_full_song':
190
  ## args are sections_to_be_written, relevant_ideas, last_snippet_id, sni
191
 
@@ -203,7 +401,10 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
203
  ## STEP 1: WRITE ALL LYRICS using songwriterAssistant
204
  remaining_lyrics = songwriterAssistant.write_all_lyrics(**tool_query_args)
205
  full_lyrics = current_lyrics + remaining_lyrics + "\n[End]"
206
- yield '', history, messages, full_lyrics
 
 
 
207
 
208
  ## STEP 2: MAKE SONG FOR REMAINING LYRICS
209
  song_link = make_song(remaining_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
@@ -214,11 +415,15 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
214
 
215
  new_messages = messages + [tool_message, audio_message]
216
  new_history = messages_to_history(new_messages)
217
- yield '', new_history, new_messages, full_lyrics
 
 
218
 
219
  ## STEP 3: MERGE FULL SONG
220
  if snippet_clip_to_continue_from not in [None, ""]:
221
- updated_clip_url, updated_lyrics, clips_list = concat_snippets(song_link.split("https://audiopipe.suno.ai/?item_id=")[1])
 
 
222
  else:
223
  updated_clip_url, updated_lyrics, clips_list = song_link, remaining_lyrics, []
224
  ## YIELD UPDATED CLIP URL, LYRICS, AND CLIPS LIST
@@ -226,42 +431,82 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
226
 
227
  #tool and assistant message
228
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'updated clip id: {updated_clip_id}\nupdated lyrics: {updated_lyrics}\nupdated clips path: {clips_list}'}
229
- audio_message = {'role': 'assistant', 'content': f'All done! Thank you for participating :) \nFinal Lyrics: {full_lyrics} \nFinal song: <audio controls autoplay><source src="{updated_clip_url}" type="audio/mp3"></audio><p>clip id: {updated_clip_id}</p>'}
230
 
231
  new_messages = messages + [tool_message, audio_message]
232
  new_history = messages_to_history(new_messages)
233
- yield '', new_history, new_messages, '[...]'
 
 
 
 
234
 
235
  elif tool_function_name == 'get_audio_snippet':
236
  #detangle tool_query_args dict
237
  snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
238
  snippet_instrumental_tags = tool_query_args['snippet_instrumental_tags']
239
- if 'snippet_clip_to_continue_from' not in tool_query_args:
240
- tool_query_args['snippet_clip_to_continue_from'] = None
241
- snippet_clip_to_continue_from = tool_query_args['snippet_clip_to_continue_from']
242
  song_link = make_song(snippet_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  ## filter out suno link from tool query arg
244
  clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
245
 
246
- tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'instrumental tags: {tool_query_args["snippet_instrumental_tags"]}, clip id: {clip_id}'}
247
- audio_message_content = "Here's what I've come up with:\n" + snippet_lyrics + '\n\n' + f'<audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio><p>instrumental tags: {tool_query_args["snippet_instrumental_tags"]}</p><p>clip id: {clip_id}</p>'
248
  audio_message_content += f'<p>continued from clip: {snippet_clip_to_continue_from}</p>'
 
249
  audio_message = {'role': 'assistant', 'content': audio_message_content}
 
 
 
 
 
 
250
  new_messages = messages + [tool_message, audio_message]
251
  new_history = messages_to_history(new_messages)
252
- yield '', new_history, new_messages
 
 
 
 
 
 
 
 
 
 
253
  else:
254
  print(f"Error: function {tool_function_name} does not exist")
255
-
256
- # messages.append({
257
- # "role":"tool",
258
- # "tool_call_id":tool_call_id,
259
- # "name": tool_function_name,
260
- # "content":results
261
- # })
262
-
263
- # Step 4: Invoke the chat completions API with the function response appended to the messages list
264
- # Note that messages with role 'tool' must be a response to a preceding message with 'tool_calls'
265
 
266
  else:
267
  # Model did not identify a function to call, result can be returned to the user
 
2
  import os
3
  import json
4
  from openai import OpenAI
5
+ from dotenv import load_dotenv
6
+ import asyncio
7
+ import regex as re
8
+ from gradio_modal import Modal
9
+ import gradio as gr
10
+ import time
11
 
12
+ # Load environment variables from .env file
13
+ load_dotenv()
14
+
15
+ from suno import make_song, concat_snippets, update_song_links
16
  from gpt_calls import AI_Songwriter
17
  from utils.song_utils import messages_to_history
18
 
 
20
  Messages = List[Dict[str, str]] # a type: list of messages with role and content
21
 
22
  client_key = os.getenv("OPEN_AI_KEY")
23
+ print(client_key)
24
  oai_client = OpenAI(
25
  api_key=client_key,
26
  )
27
 
28
+ def determine_title(section_name, generated_audios):
29
+ count = sum(1 for audio in generated_audios if audio[2].startswith(section_name))
30
+ if count > 0:
31
+ section_name = f"{section_name} {count + 1}"
32
+ return section_name
33
 
 
 
34
 
35
+ def model_chat(genre_input, query: Optional[str], history: Optional[History], messages: Optional[Messages], generated_audios: List[Tuple[str, str, str]], auto=False) -> Tuple[str, History, Messages, str, str, str, str, str, List]:
36
+ if query is None:
37
+ query = ''
38
  with open('ai_tools.json') as f:
39
  ai_tools = json.load(f)
40
 
 
45
  else:
46
  messages = messages + [{'role': 'user', 'content': query}]
47
 
 
48
 
49
  messages_filtered = messages
50
  response_message = oai_client.chat.completions.create(
51
  model="gpt-4o",
52
  messages=messages_filtered,
53
  tools = ai_tools,
54
+ tool_choice="required",
55
  )
56
  print(response_message, "model chat response")
57
  current_response = ""
 
64
  "tool_calls": tool_calls,
65
  "function_call": response_message.choices[0].message.function_call
66
  })
67
+
68
+ if len(tool_calls) > 1:
69
+ for tool_call in tool_calls:
70
+ tool_message = {
71
+ 'role': 'tool',
72
+ 'tool_call_id': tool_call.id,
73
+ 'name': tool_call.function.name,
74
+ 'content': "You called two different functions when you can only call one at a time. Did you mean to call revise_section_lyrics_and_instrumental but instead had two different calls for lyrics and instrumental? Communicate this failure to the user and clarify what they are asking for, then only call one tool next time."
75
+ }
76
+ messages.append(tool_message)
77
+
78
+ # Generate a response using GPT-4o and add it as a message
79
+ model_response_with_function_call = oai_client.chat.completions.create(
80
+ model="gpt-4o",
81
+ messages=messages,
82
+ )
83
+ current_response = model_response_with_function_call.choices[0].message.content
84
+
85
+ role = "assistant"
86
+ messages.append({'role': role, 'content': current_response})
87
+
88
+ yield '', messages_to_history(messages), messages, '', '', '', '', None, generated_audios, []
89
+ return
90
+
91
+
92
  # If true the model will return the name of the tool / function to call and the argument(s)
93
  for tool_call in tool_calls:
94
  print(tool_call)
95
  tool_call_id = tool_call.id
96
  tool_function_name = tool_call.function.name
97
  tool_query_args = eval(tool_call.function.arguments)
98
+
99
+ print(tool_function_name, tool_query_args)
100
+
101
+ with open('ai_tools.json') as f:
102
+ ai_tools = json.load(f)
103
+
104
+ for tool in ai_tools:
105
+ if tool['function']['name'] == tool_function_name:
106
+ valid_keys = tool['function']['parameters']['properties'].keys()
107
+ required_keys = tool['function']['parameters']['required']
108
+ break
109
+
110
+ print('query args before', tool_query_args)
111
+ tool_query_args = {k: v for k, v in tool_query_args.items() if k in valid_keys}
112
+ print('query args after', tool_query_args)
113
+ missing_keys = []
114
+ for key in required_keys:
115
+ if key not in tool_query_args:
116
+ missing_keys.append(key)
117
+ if len(missing_keys)>0:
118
+ missing_keys_str = ", ".join(missing_keys)
119
+ tool_message = {
120
+ 'role': 'tool',
121
+ 'tool_call_id': tool_call_id,
122
+ 'name': tool_function_name,
123
+ 'content': f"Sorry, the keys {missing_keys_str} from the function you called are missing, communicate this to the user and either get what these args should be or figure out which function to call."
124
+ }
125
+
126
+ new_messages = messages + [tool_message]
127
+
128
+ model_response_with_function_call = oai_client.chat.completions.create(
129
+ model="gpt-4o",
130
+ messages=new_messages,
131
+ ) # get a new response from the model where it can see the function response
132
+ current_response = model_response_with_function_call.choices[0].message.content
133
+
134
+ role = "assistant"
135
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
136
+ new_history = messages_to_history(new_messages)
137
+
138
+ generated_audios = update_song_links(generated_audios)
139
+ yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
140
+
141
+
142
+ # Step 3: Call the function and retrieve results. Append the results to the messages list.
143
+ if tool_function_name == 'ask_question':
144
+ question = songwriterAssistant.ask_question(messages)
145
+
146
+ question = question.replace("ask question:", "").replace("ask question ", "").replace("ask question\n", "").replace("ask question", "")
147
+
148
+ ## yield question in tool and assistant message
149
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': question}
150
+
151
+ new_messages = messages + [tool_message]
152
+
153
+ question_message = {'role': 'assistant', 'content': question}
154
+ new_messages = new_messages + [question_message]
155
+ new_history = messages_to_history(new_messages)
156
+
157
+ generated_audios = update_song_links(generated_audios)
158
+ yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
159
+
160
+ elif tool_function_name == 'clarify_arguments':
161
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': 'arguments to clarify: \n' + '\n'.join(tool_query_args['arguments_to_clarify'])}
162
+
163
+ new_messages = messages + [tool_message]
164
+
165
+ model_response_with_function_call = oai_client.chat.completions.create(
166
+ model="gpt-4o",
167
+ messages=new_messages,
168
+ ) # get a new response from the model where it can see the function response
169
+ current_response = model_response_with_function_call.choices[0].message.content
170
+
171
+ role = "assistant"
172
+ new_messages = new_messages + [{'role': role, 'content': current_response}] # + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
173
+ # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
174
+ new_history = messages_to_history(new_messages)
175
+
176
+ generated_audios = update_song_links(generated_audios)
177
+ yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
178
+
179
 
180
+ elif tool_function_name == 'write_section':
 
181
  snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
182
  snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
183
  suggested_lyrics = songwriterAssistant.write_section(**tool_query_args)
184
+ suggested_lyrics = suggested_lyrics.strip('`*-\n')
185
 
186
  ## yield suggested lyrics in tool and assistant message
187
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': suggested_lyrics}
 
195
  current_response = model_response_with_function_call.choices[0].message.content
196
 
197
  role = "assistant"
198
+ new_messages = new_messages + [{'role': role, 'content': current_response}] # + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
199
  # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
200
  history = messages_to_history(new_messages)
201
+
202
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
203
+ buttons = ["revise lyrics", "generate audio snippet", "continue to next section"]
204
+
205
+ generated_audios = update_song_links(generated_audios)
206
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = snippet_clip_to_continue_from, choices=[x[3] for x in generated_audios]+[""], interactive=True)
207
 
208
 
 
 
209
 
210
+ yield '', history, new_messages, tool_query_args['section_name'], suggested_lyrics.split(':')[-1], snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
 
211
 
212
+ ### DO SOMETHING TO UPDATE CURRENT GENERATION for write_section
 
213
 
 
 
 
 
 
 
 
 
214
 
215
  elif tool_function_name == 'revise_section_lyrics':
 
 
216
  revised_lyrics = songwriterAssistant.revise_section_lyrics(**tool_query_args)
217
 
218
+ # if isinstance(revised_lyrics, list):
219
+ # revised_lyrics = '\n'.join(revised_lyrics)
220
+ if isinstance(revised_lyrics, str) and revised_lyrics.startswith("[") and revised_lyrics.endswith("]"):
221
+ try:
222
+ revised_lyrics = eval(revised_lyrics)
223
+ if isinstance(revised_lyrics, list):
224
+ revised_lyrics = '\n'.join(revised_lyrics)
225
+ except:
226
+ pass
227
+
228
  # ## yield revised lyrics in tool and assistant message
229
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': revised_lyrics}
230
  # audio_message = {'role': 'assistant', 'content': "Here's my revised lyrics:\n" + revised_lyrics + "\n\nGenerating audio snippet..."}
 
236
  ) # get a new response from the model where it can see the function response
237
  current_response = model_response_with_function_call.choices[0].message.content
238
 
239
+ buttons = ["revise lyrics again", "generate audio snippet with new lyrics", "continue to next section"]
240
+
241
  role = "assistant"
242
+ new_messages = new_messages + [{'role': role, 'content': current_response + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
243
  # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
244
  history = messages_to_history(new_messages)
245
+ generated_audios = update_song_links(generated_audios)
246
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
 
 
 
 
247
 
248
+ yield '', history, new_messages, tool_query_args['section_name'], revised_lyrics, '', clips_to_continue, None, generated_audios, buttons
 
249
 
 
 
 
 
 
 
 
 
250
 
251
  elif tool_function_name == 'revise_instrumental_tags':
252
  #detangle tool_query_args dict
253
  #snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
254
+ snippet_instrumental_tags = tool_query_args.get('current_instrumental_tags', None)
255
+ user_instrumental_feedback = tool_query_args.get('user_instrumental_feedback', None)
256
+
257
+ if snippet_instrumental_tags is None or user_instrumental_feedback is None:
258
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': 'Arguments are missing. Please clarify your feedback on the instrumental. Note that you cannot revise the genre if you haven\'t generated a snippet.'}
259
+ audio_message = {'role': 'assistant', 'content': 'It seems like some information is missing. Could you please provide your feedback on the instrumental? Note that you cannot revise the genre if you haven\'t generated a snippet.'}
260
+ new_messages = messages + [tool_message, audio_message]
261
+ new_history = messages_to_history(new_messages)
262
+ yield '', new_history, new_messages, '', '', '', None, None, generated_audios, []
263
+ return
264
  # if 'snippet_clip_to_continue_from' not in tool_query_args:
265
  # tool_query_args['snippet_clip_to_continue_from'] = None
266
  # snippet_clip_to_continue_from = tool_query_args['snippet_clip_to_continue_from']
267
 
268
  new_instrumental_tags = songwriterAssistant.revise_instrumental_tags(snippet_instrumental_tags, user_instrumental_feedback)
 
 
 
 
 
 
269
 
270
  if isinstance(tool_query_args['sections_written'], str):
271
  current_lyrics = tool_query_args['sections_written']
 
274
  else:
275
  current_lyrics = ""
276
 
277
+ import re
278
+ sections_list = re.findall(r'\[.*?\]', current_lyrics)
279
+
280
  #current_lyrics = "\n".join(tool_query_args['sections_written'])
281
  song_link = make_song(current_lyrics, new_instrumental_tags)
282
  ## filter out suno link from tool query arg
283
+ while "https://audiopipe.suno.ai/?item_id=" not in song_link:
284
+ print("BUGGED OUT, trying again...")
285
+ time.sleep(5)
286
+ song_link = make_song(current_lyrics, new_instrumental_tags)
287
+
288
  clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
289
 
290
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'new instrumental tags: {new_instrumental_tags}, clip id: {clip_id}'}
291
+ audio_message = {'role': 'assistant', 'content': f'Sure! I\'ve revised the instrumental tags: {new_instrumental_tags}\nCurrent lyrics: {current_lyrics}\n\n <audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio>'}
292
  audio_message['content'] += f'\n\nWhat do you think?'
293
  new_messages = messages + [tool_message, audio_message]
294
  new_history = messages_to_history(new_messages)
 
 
 
 
295
 
296
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
297
+ if len(sections_list) > 0:
298
+ section_name = f"Up to {sections_list[-1]}"
299
+ else:
300
+ section_name = "Up to latest section"
301
+ section_name = determine_title(section_name, generated_audios)
302
 
303
+ generated_audios.append((song_link, current_lyrics, new_instrumental_tags, section_name, "streaming"))
304
+ generated_audios = update_song_links(generated_audios)
305
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
306
+
307
+ buttons = ["return to original instrumental", "re-revise genre", "revise lyrics", "merge snippets", "continue to next section"]
308
+
309
+ yield '', new_history, new_messages, ', '.join(sections_list), current_lyrics, new_instrumental_tags, clips_to_continue, f'<audio controls><source src="{song_link}" type="audio/mp3"></audio>', generated_audios, buttons
310
+ elif tool_function_name == 'revise_section_lyrics_and_instrumental':
311
+ snippet_instrumental_tags = tool_query_args.pop('current_instrumental_tags', None)
312
+ user_instrumental_feedback = tool_query_args.pop('user_instrumental_feedback', None)
313
+ snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
314
+
315
+ # Revise section lyrics
316
+ revised_lyrics = songwriterAssistant.revise_section_lyrics(**tool_query_args)
317
+
318
+ # Revise instrumental tags
319
+
320
+ new_instrumental_tags = songwriterAssistant.revise_instrumental_tags(snippet_instrumental_tags, user_instrumental_feedback)
321
+
322
+ song_link = make_song(revised_lyrics, new_instrumental_tags, snippet_clip_to_continue_from)
323
+ while "https://audiopipe.suno.ai/?item_id=" not in song_link:
324
+ print("BUGGED OUT, trying again...")
325
+ time.sleep(5)
326
+ song_link = make_song(revised_lyrics, new_instrumental_tags, snippet_clip_to_continue_from)
327
+
328
+ clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
329
+
330
+ tool_message_instrumental = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'revised lyrics: {revised_lyrics}\nrevised instrumental tags: {new_instrumental_tags}, clip id: {clip_id}'}
331
+ audio_message = {'role': 'assistant', 'content': f'Sure! I\'ve revised the lyrics and instrumental tags: {revised_lyrics}\nRevised lyrics: {revised_lyrics}\n\n <audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio>'}
332
+ audio_message['content'] += f'\n\nWhat do you think?'
333
+
334
+ new_messages = messages + [tool_message_instrumental, audio_message]
335
  new_history = messages_to_history(new_messages)
336
+
337
+ generated_audios.append((song_link, revised_lyrics, new_instrumental_tags, tool_query_args["section_name"], "streaming"))
338
+ generated_audios = update_song_links(generated_audios)
339
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = snippet_clip_to_continue_from, choices=[x[3] for x in generated_audios]+[""], interactive=True)
340
+
341
+ buttons = ["return to original instrumental", "re-revise genre", "revise lyrics", "merge snippets", "continue to next section"]
342
+
343
+ yield '', new_history, new_messages, tool_query_args["section_name"], revised_lyrics, new_instrumental_tags, clips_to_continue, f'<audio controls><source src="{song_link}" type="audio/mp3"></audio>', generated_audios, buttons
344
+
345
+ elif tool_function_name == 'merge_all_snippets':
346
+ updated_clip_url, updated_lyrics, updated_tags, clips_list = concat_snippets(tool_query_args['last_snippet_id'])
347
+
348
+ if "still streaming" in updated_clip_url:
349
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'still streaming, try again later'}
350
+ audio_message = {'role': 'assistant', 'content': f'Unfortunately the generated clip audio is still being streamed, so you can merge later when it is fully generated.'}
351
+
352
+ new_messages = messages + [tool_message, audio_message]
353
+ new_history = messages_to_history(new_messages)
354
+
355
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
356
+
357
+
358
+ yield '', new_history, new_messages, "", "", "", clips_to_continue, None, generated_audios, ["merge snippets", "continue to next section"]
359
+
360
+ else:
361
+ updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
362
+
363
+ #pass this info in new tool and assistant message
364
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'updated clip id: {updated_clip_id}\nupdated lyrics: {updated_lyrics}\nupdated clips path: {clips_list}'}
365
+ audio_message = {'role': 'assistant', 'content': f'Sure! All the clips are now merged. <p>updated lyrics: {updated_lyrics}</p><audio controls autoplay><source src="{updated_clip_url}" type="audio/mp3"></audio><p>updated clips path: {clips_list}</p>'}
366
+
367
+ sections_list = [line for line in current_lyrics.split('\n') if line.startswith('[') and line.endswith(']')]
368
+
369
+
370
+ new_messages = messages + [tool_message, audio_message]
371
+ new_history = messages_to_history(new_messages)
372
+
373
+ if len(sections_list) > 0:
374
+ section_name = "Merge up to " + sections_list[-1]
375
+ else:
376
+ section_name = "Merge up to latest section"
377
+ section_name = determine_title(section_name, generated_audios)
378
+
379
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
380
+ generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, section_name, "streaming"))
381
+
382
+ generated_audios = update_song_links(generated_audios)
383
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
384
+
385
+
386
+ yield '', new_history, new_messages, section_name, updated_lyrics, updated_tags, clips_to_continue, f'<audio controls><source src="{updated_clip_url}" type="audio/mp3"></audio>', generated_audios, []
387
  elif tool_function_name == 'finish_full_song':
388
  ## args are sections_to_be_written, relevant_ideas, last_snippet_id, sni
389
 
 
401
  ## STEP 1: WRITE ALL LYRICS using songwriterAssistant
402
  remaining_lyrics = songwriterAssistant.write_all_lyrics(**tool_query_args)
403
  full_lyrics = current_lyrics + remaining_lyrics + "\n[End]"
404
+
405
+
406
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
407
+ yield '', history, messages, "Full Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, None, generated_audios, []
408
 
409
  ## STEP 2: MAKE SONG FOR REMAINING LYRICS
410
  song_link = make_song(remaining_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
 
415
 
416
  new_messages = messages + [tool_message, audio_message]
417
  new_history = messages_to_history(new_messages)
418
+ generated_audios.append((song_link, remaining_lyrics, snippet_instrumental_tags, "Rest of Song", "streaming"))
419
+
420
+ yield '', new_history, new_messages, "Rest of Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, song_link, generated_audios, []
421
 
422
  ## STEP 3: MERGE FULL SONG
423
  if snippet_clip_to_continue_from not in [None, ""]:
424
+ updated_clip_url = "still streaming"
425
+ while "still streaming" in updated_clip_url:
426
+ updated_clip_url, updated_lyrics, updated_tags, clips_list = concat_snippets(song_link.split("https://audiopipe.suno.ai/?item_id=")[1])
427
  else:
428
  updated_clip_url, updated_lyrics, clips_list = song_link, remaining_lyrics, []
429
  ## YIELD UPDATED CLIP URL, LYRICS, AND CLIPS LIST
 
431
 
432
  #tool and assistant message
433
  tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'updated clip id: {updated_clip_id}\nupdated lyrics: {updated_lyrics}\nupdated clips path: {clips_list}'}
434
+ audio_message = {'role': 'assistant', 'content': f'All done! Thank you for participating :) \nFinal Lyrics: {full_lyrics} \nFinal song: <audio controls autoplay><source src="{updated_clip_url}" type="audio/mp3"></audio>'}
435
 
436
  new_messages = messages + [tool_message, audio_message]
437
  new_history = messages_to_history(new_messages)
438
+ generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, "Full Song", "streaming"))
439
+ generated_audios = update_song_links(generated_audios)
440
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
441
+
442
+ yield '', new_history, new_messages, "Full Song", full_lyrics, snippet_instrumental_tags, clips_to_continue, f'<audio controls><source src="{song_link}" type="audio/mp3"></audio>', generated_audios, []
443
 
444
  elif tool_function_name == 'get_audio_snippet':
445
  #detangle tool_query_args dict
446
  snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
447
  snippet_instrumental_tags = tool_query_args['snippet_instrumental_tags']
448
+
449
+ snippet_clip_to_continue_from = tool_query_args.get('snippet_clip_to_continue_from', None)
 
450
  song_link = make_song(snippet_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
451
+
452
+
453
+ if "still streaming" in song_link:
454
+ tool_message = {
455
+ 'role': 'tool',
456
+ 'tool_call_id': tool_call_id,
457
+ 'name': tool_function_name,
458
+ 'content': 'The snippet to extend is still streaming. Please try generating this audio snippet in a little bit.'
459
+ }
460
+
461
+ new_messages = messages + [tool_message]
462
+
463
+ model_response_with_function_call = oai_client.chat.completions.create(
464
+ model="gpt-4o",
465
+ messages=new_messages,
466
+ ) # get a new response from the model where it can see the function response
467
+ current_response = model_response_with_function_call.choices[0].message.content
468
+
469
+ role = "assistant"
470
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
471
+ new_history = messages_to_history(new_messages)
472
+
473
+ generated_audios = update_song_links(generated_audios)
474
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
475
+ buttons = ["generate audio snippet", "revise lyrics", "revise genre", "merge snippets", "continue to next section"]
476
+
477
+ yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
478
+
479
+ return
480
+ print("MAKE SONG IS DONE")
481
  ## filter out suno link from tool query arg
482
  clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
483
 
484
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'snippet lyrics: {snippet_lyrics}\ninstrumental tags: {tool_query_args["snippet_instrumental_tags"]}, clip id: {clip_id}'}
485
+ audio_message_content = "Here's what I've come up with:\n" + snippet_lyrics + '\n\n' + f'<audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio><p>instrumental tags: {tool_query_args["snippet_instrumental_tags"]}</p>'
486
  audio_message_content += f'<p>continued from clip: {snippet_clip_to_continue_from}</p>'
487
+ audio_message_content += "What do you think?"
488
  audio_message = {'role': 'assistant', 'content': audio_message_content}
489
+
490
+
491
+ section_name = snippet_lyrics.split("\n")[0].strip('[]* ')
492
+ section_name = determine_title(section_name, generated_audios)
493
+
494
+ #audio_message = {'role': 'assistant', 'content': gr.Audio(value=song_link, label=section_name, interactive=False, show_label=False, waveform_options={"show_controls": False})}
495
  new_messages = messages + [tool_message, audio_message]
496
  new_history = messages_to_history(new_messages)
497
+ print("AUDIO MESSAGE DONE")
498
+ generated_audios.append((song_link, snippet_lyrics, snippet_instrumental_tags, section_name, "streaming"))
499
+
500
+ generated_audios = update_song_links(generated_audios)
501
+
502
+ buttons = ["revise lyrics", "revise genre", "merge snippets", "continue to next section"]
503
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = snippet_clip_to_continue_from, choices=[x[3] for x in generated_audios]+[""], interactive=True)
504
+
505
+ yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, f'<audio controls><source src="{song_link}" type="audio/mp3"></audio>', generated_audios, buttons
506
+
507
+
508
  else:
509
  print(f"Error: function {tool_function_name} does not exist")
 
 
 
 
 
 
 
 
 
 
510
 
511
  else:
512
  # Model did not identify a function to call, result can be returned to the user
data/conversation_history.json ADDED
@@ -0,0 +1 @@
 
 
1
+ [{"role": "system", "content": "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."}, {"role": "user", "content": "The user has stated the following:\n I have a story that could make this concept work well. The title is Future Dreams , it's about A song about envisioning your ideal future where dreams come true, adventures unfold, and loved ones stand by your side. with a genre Pop and I think this should be the structure: \nVerse 1: 4 measures\nChorus 1: 8 measures\nVerse 2: 8 measures\nChorus 2: 8 measures\nVerse 3: 8 measures\nChorus 3: 8 measures\n Introduce yourself and kick-off the songwriting process with a question."}, {"role": "assistant", "content": "Hey there! I'm excited to help you bring \"Future Dreams\" to life. It sounds like an incredible concept. \n\nTo get started, can you tell me about a specific dream or adventure you've always envisioned for your future? This could be something big, like traveling the world, or something personal, like achieving a lifelong goal."}, {"role": "user", "content": "i've envisioned living in spain after 30"}, {"role": "assistant", "content": null, "tool_calls": [
gpt_calls.py CHANGED
@@ -31,6 +31,33 @@ class AI_Songwriter:
31
  {}"""
32
 
33
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  def write_section(self, section_name, section_description, relevant_ideas, section_length, sections_written=None, overall_song_description=None):
35
  instruction = f"Write a {section_name} of length {section_length} that that incorporates the following ideas"
36
  if sections_written is not None:
@@ -70,14 +97,20 @@ class AI_Songwriter:
70
  messages=convo,
71
  )
72
 
73
- return "Pass this back to the user: \n" + response.choices[0].message.content
74
 
75
  def revise_section_lyrics(self, section_name, current_section, lines_to_revise, relevant_ideas=None, relevant_words=None):
76
  lines_to_infill = ", ".join([str(x) for x in lines_to_revise])
77
 
78
  full_incomplete_verse = current_section.strip("\n ").split("\n")
 
 
 
 
 
79
  for line_num in lines_to_revise:
80
- full_incomplete_verse[line_num-1] = '___'
 
81
 
82
  line_phrase = "lines" if len(lines_to_infill) > 1 else "line"
83
  line_phrase = str(len(lines_to_infill)) + " " + line_phrase
@@ -173,37 +206,4 @@ class AI_Songwriter:
173
  )
174
 
175
  return response.choices[0].message.content
176
-
177
- # def get_relevant_ideas(self, section_name, section_description, conversation_history):
178
- # instruction = f"Identify the relevant ideas from the conversation history that can be used in the {section_name} given its description. Output your ideas as a bullet separated list (ie - idea 1, - idea 2) such that each idea is in the format 'I ...', 'I ...', etc."
179
-
180
- # input = f"""Section Description: {section_description}\nConversation History:{conversation_history}\nRelevant ideas:"""
181
-
182
- # prompt = self.alpaca_prompt.format(instruction, input, "")
183
-
184
- # convo = [
185
- # {
186
- # "role": "user",
187
- # "content": prompt,
188
- # },
189
- # ]
190
- # response = self.oai_client.chat.completions.create(
191
- # model="gpt-4o",
192
- # messages=convo,
193
- # )
194
-
195
- # return response.choices[0].message.content
196
-
197
- # def get_audio_snippet(self, snippet_lyrics, snippet_instrumental_tags, snippet_clip_to_continue):
198
- # # add a message of user asking for audio snippet
199
- # song_link = make_song(genre_input, lyrics, new_tags, last_clip)
200
- # # # Add the audio to the message and history
201
-
202
- # # audio_message = {'role': 'assistant', 'content': f'<audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio>'}
203
- # # new_messages = messages + [snippet_request, audio_message]
204
- # # new_history = messages_to_history(new_messages)
205
-
206
- # # return new_history, new_messages
207
-
208
- # pass
209
 
 
31
  {}"""
32
 
33
 
34
+
35
+ def ask_question(self, messages):
36
+ convo = messages[:-1]
37
+
38
+ instruction = "Based on this conversation history, respond to the user acknowledging their most recent response and ask a concise question to further learn more about the user's story."
39
+
40
+ ## iterate thru messages and format them into a single string where each message is separated by a newline (ie Assistant: ...\n User: ...\n)
41
+ convo_str = ""
42
+ for message in convo:
43
+ convo_str += f"{message['role']}: {message['content']}\n"
44
+ convo_str += "Assistant:"
45
+
46
+ input = f"{instruction}\nConversation History:\n{convo_str}"
47
+
48
+ response = self.oai_client.chat.completions.create(
49
+ model="gpt-4o",
50
+ messages=[
51
+ {
52
+ "role": "user",
53
+ "content": input
54
+ }
55
+ ],
56
+ )
57
+
58
+ return response.choices[0].message.content
59
+
60
+
61
  def write_section(self, section_name, section_description, relevant_ideas, section_length, sections_written=None, overall_song_description=None):
62
  instruction = f"Write a {section_name} of length {section_length} that that incorporates the following ideas"
63
  if sections_written is not None:
 
97
  messages=convo,
98
  )
99
 
100
+ return "Pass this back to the user and ask if they would like to receive an audio snippet or make any revisions before moving to the next section: \n" + response.choices[0].message.content
101
 
102
  def revise_section_lyrics(self, section_name, current_section, lines_to_revise, relevant_ideas=None, relevant_words=None):
103
  lines_to_infill = ", ".join([str(x) for x in lines_to_revise])
104
 
105
  full_incomplete_verse = current_section.strip("\n ").split("\n")
106
+
107
+ max_line_num = max(lines_to_revise)
108
+ if max_line_num > len(full_incomplete_verse):
109
+ full_incomplete_verse.extend([''] * (max_line_num - len(full_incomplete_verse)))
110
+
111
  for line_num in lines_to_revise:
112
+ if line_num <= len(full_incomplete_verse):
113
+ full_incomplete_verse[line_num-1] = '___'
114
 
115
  line_phrase = "lines" if len(lines_to_infill) > 1 else "line"
116
  line_phrase = str(len(lines_to_infill)) + " " + line_phrase
 
206
  )
207
 
208
  return response.choices[0].message.content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
suno.py CHANGED
@@ -1,6 +1,8 @@
1
  import requests
2
  import time
3
  import os
 
 
4
 
5
  base_url = "http://127.0.0.1:8000"
6
  api_endpoint_submit = f"{base_url}/generate/"
@@ -52,6 +54,25 @@ def generate_song(tags, prompt, save_path, clip_id=None, continue_at=30):
52
  data["continue_at"] = continue_at
53
  else:
54
  data["continue_at"] = 30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  response = requests.post(api_endpoint_submit, json=data) #,headers=headers)
57
  response_data = response.json()
@@ -107,7 +128,6 @@ def generate_song(tags, prompt, save_path, clip_id=None, continue_at=30):
107
 
108
  return url
109
 
110
-
111
  def concat_snippets(clip_id):
112
  concat_url = f"{api_endpoint_concat}?clip_id={clip_id}"
113
  feed_url = api_endpoint_info + clip_id
@@ -119,7 +139,10 @@ def concat_snippets(clip_id):
119
  print("No data in response, retrying", response_data)
120
  time.sleep(2)
121
  continue
122
- if response_data[0]["status"] == 'complete':
 
 
 
123
  break
124
  else:
125
  time.sleep(8)
@@ -137,6 +160,7 @@ def concat_snippets(clip_id):
137
  return url, lyrics, concatenated_clips
138
 
139
  lyrics = response_data["metadata"]["prompt"]
 
140
  concatenated_clips = [x["id"] for x in response_data["metadata"]["concat_history"]]
141
  song_id = response_data["id"]
142
 
@@ -144,12 +168,13 @@ def concat_snippets(clip_id):
144
  while True:
145
  response = requests.get(api_endpoint_info + song_id, headers=headers)
146
  response_data = response.json()
 
147
  if response.status_code != 200:
148
  print("No data in response, retrying", response_data)
149
  time.sleep(2)
150
  continue
151
  # print("Got response", response_data)
152
- if response_data[0]["status"] == 'streaming':
153
  break
154
  else:
155
  time.sleep(2)
@@ -160,6 +185,34 @@ def concat_snippets(clip_id):
160
  print("Got song", response_data[0]["audio_url"])
161
  url = response_data[0]["audio_url"]
162
 
163
- return url, lyrics, concatenated_clips
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
 
 
1
  import requests
2
  import time
3
  import os
4
+ import asyncio
5
+ import httpx
6
 
7
  base_url = "http://127.0.0.1:8000"
8
  api_endpoint_submit = f"{base_url}/generate/"
 
54
  data["continue_at"] = continue_at
55
  else:
56
  data["continue_at"] = 30
57
+
58
+ feed_url = api_endpoint_info + clip_id
59
+ response = requests.get(feed_url, headers=headers)
60
+ response_data = response.json()
61
+ while True:
62
+ if response.status_code != 200:
63
+ print("No data in response, retrying", response_data)
64
+ time.sleep(2)
65
+ continue
66
+ elif response_data[0]["status"] == 'streaming':
67
+ return "Snippet to extend is still streaming, please wait to request later."
68
+ if response_data[0]["status"] == 'complete':
69
+ break
70
+ else:
71
+ time.sleep(8)
72
+ continue
73
+
74
+
75
+
76
 
77
  response = requests.post(api_endpoint_submit, json=data) #,headers=headers)
78
  response_data = response.json()
 
128
 
129
  return url
130
 
 
131
  def concat_snippets(clip_id):
132
  concat_url = f"{api_endpoint_concat}?clip_id={clip_id}"
133
  feed_url = api_endpoint_info + clip_id
 
139
  print("No data in response, retrying", response_data)
140
  time.sleep(2)
141
  continue
142
+ ## CATCH THE CASE WHERE response_data a list of length 1 versus just a dictionary straight up
143
+ elif response_data["status"] == 'streaming':
144
+ return "Song is still streaming, please wait to request later.", None, None, []
145
+ if response_data["status"] == 'complete':
146
  break
147
  else:
148
  time.sleep(8)
 
160
  return url, lyrics, concatenated_clips
161
 
162
  lyrics = response_data["metadata"]["prompt"]
163
+ tags = response_data["metadata"]["tags"]
164
  concatenated_clips = [x["id"] for x in response_data["metadata"]["concat_history"]]
165
  song_id = response_data["id"]
166
 
 
168
  while True:
169
  response = requests.get(api_endpoint_info + song_id, headers=headers)
170
  response_data = response.json()
171
+ print("feed response for concatenated song", response_data)
172
  if response.status_code != 200:
173
  print("No data in response, retrying", response_data)
174
  time.sleep(2)
175
  continue
176
  # print("Got response", response_data)
177
+ if response_data[0]["status"] == 'streaming' or response_data[0]["audio_url"] != "" or response_data[0]["status"] == 'complete':
178
  break
179
  else:
180
  time.sleep(2)
 
185
  print("Got song", response_data[0]["audio_url"])
186
  url = response_data[0]["audio_url"]
187
 
188
+ return url, lyrics, tags, concatenated_clips
189
+
190
+ def update_song_links(generated_audios):
191
+ updated_generated_audios = generated_audios.copy()
192
+ for i, song_info in enumerate(generated_audios):
193
+ clip_path, lyrics, instrumental, title, status = song_info
194
+ if "audiopipe.suno.ai" in clip_path or status == "streaming":
195
+ clip_id = clip_path.split("?item_id=")[-1]
196
+ feed_url = api_endpoint_info + clip_id
197
+
198
+ response = requests.get(feed_url, headers=headers)
199
+ response_data = response.json()
200
+ if response.status_code != 200:
201
+ print("No data in response, retrying", response_data)
202
+ continue
203
+ elif response_data[0]["status"] == 'streaming':
204
+ print("still streaming, update later")
205
+ continue
206
+ if response_data[0]["status"] == 'complete':
207
+ updated_clip_path = response_data[0]["audio_url"]
208
+ print(updated_clip_path)
209
+ updated_generated_audios[i] = (updated_clip_path, lyrics, instrumental, title, "complete")
210
+
211
+ return updated_generated_audios
212
+
213
+
214
+
215
+
216
+
217
 
218
 
utils/song_utils.py CHANGED
@@ -1,8 +1,15 @@
1
  import os
2
  from openai import OpenAI
3
  from typing import Optional, Tuple, List, Dict
 
 
 
 
 
 
4
 
5
  client_key = os.getenv("OPEN_AI_KEY")
 
6
  oai_client = OpenAI(
7
  api_key=client_key,
8
  )
@@ -87,6 +94,7 @@ def messages_to_history(messages: Messages) -> Tuple[str, History]:
87
  """
88
  assert messages[0]['role'] == 'system' and messages[1]['role'] == 'user'
89
 
 
90
  # Filter out 'tool' messages and those containing 'tool_calls'
91
  messages_for_parsing = [msg for msg in messages if msg['role'] != 'tool' and 'tool_calls' not in msg]
92
 
@@ -98,8 +106,8 @@ def messages_to_history(messages: Messages) -> Tuple[str, History]:
98
 
99
  # Create history from user-assistant message pairs
100
  history = [
101
- (q['content'], r['content'])
102
- for q, r in zip(messages_for_parsing[1::2], messages_for_parsing[2::2])
103
  ]
104
 
105
  return history
@@ -120,8 +128,8 @@ def get_starting_messages(song_lengths: str, song_title: str, song_blurb: str, s
120
  """
121
  system_prompt = (
122
  "You are an expert at writing songs. You are with an everyday person, and you will write the lyrics of the song "
123
- "based on this person's life by asking questions about a story of theirs. Design your questions on your own, without "
124
- "using your tools, to help you understand the user's story, so you can write a song about the user's experience that "
125
  "resonates with them. We have equipped you with a set of tools to help you write this story; please use them. You are "
126
  "very good at making the user feel comfortable, understood, and ready to share their feelings and story. Occasionally "
127
  "(every 2 messages or so) you will suggest some lyrics, one section at a time, and see what the user thinks of them. "
@@ -145,7 +153,11 @@ def get_starting_messages(song_lengths: str, song_title: str, song_blurb: str, s
145
 
146
  first_message = first_msg_res.choices[0].message.content
147
  starting_messages = initial_messages + [{'role': 'assistant', 'content': first_message}]
148
- return starting_messages, messages_to_history(starting_messages)
 
 
 
 
149
 
150
  def update_song_details(instrumental_output: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
151
  """
 
1
  import os
2
  from openai import OpenAI
3
  from typing import Optional, Tuple, List, Dict
4
+ from dotenv import load_dotenv
5
+ from gradio import ChatMessage
6
+ import gradio as gr
7
+
8
+ # Load environment variables from .env file
9
+ load_dotenv()
10
 
11
  client_key = os.getenv("OPEN_AI_KEY")
12
+ print(client_key)
13
  oai_client = OpenAI(
14
  api_key=client_key,
15
  )
 
94
  """
95
  assert messages[0]['role'] == 'system' and messages[1]['role'] == 'user'
96
 
97
+
98
  # Filter out 'tool' messages and those containing 'tool_calls'
99
  messages_for_parsing = [msg for msg in messages if msg['role'] != 'tool' and 'tool_calls' not in msg]
100
 
 
106
 
107
  # Create history from user-assistant message pairs
108
  history = [
109
+ ChatMessage(role = q['role'], content = q['content'])
110
+ for q in messages_for_parsing[2:]
111
  ]
112
 
113
  return history
 
128
  """
129
  system_prompt = (
130
  "You are an expert at writing songs. You are with an everyday person, and you will write the lyrics of the song "
131
+ "based on this person's life by asking questions about a story of theirs. Design your questions using ask_question "
132
+ " to help you understand the user's story, so you can write a song about the user's experience that "
133
  "resonates with them. We have equipped you with a set of tools to help you write this story; please use them. You are "
134
  "very good at making the user feel comfortable, understood, and ready to share their feelings and story. Occasionally "
135
  "(every 2 messages or so) you will suggest some lyrics, one section at a time, and see what the user thinks of them. "
 
153
 
154
  first_message = first_msg_res.choices[0].message.content
155
  starting_messages = initial_messages + [{'role': 'assistant', 'content': first_message}]
156
+
157
+ history = [ChatMessage(role = x['role'], content = x['content']) for x in starting_messages]
158
+ history = history[2:]
159
+
160
+ return starting_messages, history
161
 
162
  def update_song_details(instrumental_output: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
163
  """