ajayarora1235 commited on
Commit
146352e
1 Parent(s): b9755ca

add catch all

Browse files
Files changed (3) hide show
  1. ai_tools.json +9 -11
  2. app.py +21 -16
  3. chat.py +474 -392
ai_tools.json CHANGED
@@ -65,24 +65,22 @@
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
  },
 
65
  {
66
  "type": "function",
67
  "function": {
68
+ "name": "edit_directly",
69
+ "description": "A function to be called when the user asks to directly edit either the lyrics or genre via the instrumental tags. Returns the current section or instrumental tags for the user to edit directly.",
70
  "parameters": {
71
  "type": "object",
72
  "properties": {
73
+ "edit_type": {
74
  "type": "string",
75
+ "enum": ["lyrics", "genre"],
76
+ "description": "Specifies whether the user wants to edit the lyrics or the genre."
77
  },
78
+ "current_item_to_edit": {
79
+ "type": "string",
80
+ "description": "The current lyrics section or instrumental tags that the user wants to edit."
 
 
 
81
  }
82
  },
83
+ "required": ["edit_type", "current_item_to_edit"]
84
  }
85
  }
86
  },
app.py CHANGED
@@ -17,7 +17,7 @@ css = """
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
 
@@ -28,14 +28,14 @@ with gr.Blocks(css=css) as demo:
28
  with gr.Tabs() as tabs:
29
  with gr.TabItem("Ideation", id=0): #index is 0
30
  gr.Markdown("""<center><font size=6>Let's write a song!</font></center>""")
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
 
38
- generate_seed_button = gr.Button("STEP 1: Generate Song Seed")
39
  concept_desc = 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=False)
40
  with gr.Row(visible=False) as concept_row:
41
  instrumental_output = gr.TextArea(label="Suggested Song Concept", value="", max_lines=3, scale=2)
@@ -58,7 +58,7 @@ with gr.Blocks(css=css) as demo:
58
  approve_button.click(open_accordion, inputs=[approve_button], outputs=[accordion])
59
 
60
  with gr.Row():
61
- continue_btn = gr.Button("Continue to Next Step", interactive=False)
62
 
63
 
64
  def clean_song_seed(song_seed):
@@ -71,8 +71,9 @@ with gr.Blocks(css=css) as demo:
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)
73
  def enable_button(x):
74
- return gr.Button("Continue to Next Step", interactive=True)
75
  generate_seed_button.click(make_row_visible, inputs=[generate_seed_button], outputs=[concept_row, concept_desc])
 
76
  approve_button.click(enable_button, inputs=[approve_button], outputs=[continue_btn])
77
 
78
  try_again_button.click(generate_song_seed, inputs=[feeling_input], outputs=[instrumental_output])
@@ -84,7 +85,7 @@ with gr.Blocks(css=css) as demo:
84
 
85
  with gr.TabItem("Generation", id=1): #index is 1
86
  start_song_gen = gr.State(value=False)
87
- 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>""")
88
 
89
  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")
90
 
@@ -100,7 +101,7 @@ with gr.Blocks(css=css) as demo:
100
 
101
  with gr.Row():
102
  with gr.Column(scale=2):
103
- chatbot_history = gr.Chatbot(type="messages", value=starting_history, label='SongChat', placeholder=None, layout='bubble', bubble_full_width=False, height=500)
104
  with gr.Row():
105
  typical_responses = [textbox, submit]
106
 
@@ -109,13 +110,16 @@ with gr.Blocks(css=css) as demo:
109
 
110
  button_options = gr.State([])
111
  button_dict = gr.State({
112
- "revise lyrics": "Can we revise the lyrics?",
 
 
113
  "generate audio snippet": "Can you generate an audio snippet?",
114
  "continue revising" : "Can we continue revising this section?",
115
  "generate audio snippet with new lyrics": "Can you generate an audio snippet with these new lyrics?",
116
  "return to original instrumental": "Can you use the original clip for this section instead?",
117
  "revise genre": "Can we revise the instrumental tags?",
118
  "re-revise genre": "Can we revise the instrumental tags?",
 
119
  "continue to next section": "Looks good! Let's move on to the next section.",
120
  "merge snippets": "Can you merge this snippet into its full song?"
121
  })
@@ -187,6 +191,7 @@ with gr.Blocks(css=css) as demo:
187
  return new_step_number, *modals
188
 
189
  def update_textbox(textbox, step_number):
 
190
  if step_number == 0:
191
  return textbox + "\nAsk me another question to inform the verse"
192
  elif step_number == 1:
@@ -214,7 +219,7 @@ with gr.Blocks(css=css) as demo:
214
  for btn in typical_responses[2:]:
215
  btn.click(set_response_buttons, inputs=[button_dict, btn], outputs=[textbox]).then(model_chat,
216
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
217
- 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(
218
  update_response_options, [button_options, button_dict], typical_responses
219
  ).then(
220
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
@@ -225,14 +230,14 @@ with gr.Blocks(css=css) as demo:
225
 
226
  submit.click(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
227
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
228
- 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(
229
  update_response_options, [button_options, button_dict], typical_responses
230
  ).then(
231
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
232
  )
233
  textbox.submit(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
234
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
235
- 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(
236
  update_response_options, [button_options, button_dict], typical_responses
237
  ).then(
238
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
@@ -241,15 +246,15 @@ with gr.Blocks(css=css) as demo:
241
 
242
  regen.click(set_regenerate_query, inputs=[textbox, current_section, current_lyrics, curr_tags, clip_to_continue], outputs=[textbox]).then(model_chat,
243
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
244
- 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(
245
  update_response_options, [button_options, button_dict], typical_responses
246
  ).then(
247
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
248
  )
249
 
250
- with gr.Row(visible=False):
251
  # get_snippet_button = gr.Button("Get Audio Snippet", scale=2)
252
- done = gr.Button("Finish Full Song 🎶", scale=4)
253
  #autoGPT_checkbox = gr.Checkbox(label="AutoGPT", value=True, info="Auto-generate responses from journal entry", interactive=True, scale=2)
254
  #journal_llm_creativity = gr.Slider(label="Journal LLM Temperature", minimum=0, maximum=1, step=0.01, value=1, interactive=True, scale=2)
255
  reset_button = gr.Button("Reset", scale=2)
 
17
  }
18
  """
19
 
20
+ textbox = gr.Textbox(lines=2, 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
 
 
28
  with gr.Tabs() as tabs:
29
  with gr.TabItem("Ideation", id=0): #index is 0
30
  gr.Markdown("""<center><font size=6>Let's write a song!</font></center>""")
31
+ gr.Markdown("""<center><font size=4>But first, let's generate a song seed to provide context to the AI Songwriter.</font></center>""")
32
+ gr.Markdown("""<center><font size=3>If you're stuck thinking of a song idea, 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 do you want the song to capture? What's an emotion(s) that you've personally felt that should be in the song? More vulnerable, better the song.", placeholder='Enter your story', scale=2)
35
  # audio_input = gr.Audio(sources=["upload"], type="numpy", label="Instrumental",
36
  # interactive=True, elem_id="instrumental-input")
37
 
38
+ generate_seed_button = gr.Button("Click to Generate Song Seed")
39
  concept_desc = 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=False)
40
  with gr.Row(visible=False) as concept_row:
41
  instrumental_output = gr.TextArea(label="Suggested Song Concept", value="", max_lines=3, scale=2)
 
58
  approve_button.click(open_accordion, inputs=[approve_button], outputs=[accordion])
59
 
60
  with gr.Row():
61
+ continue_btn = gr.Button("Ready to Create", interactive=False)
62
 
63
 
64
  def clean_song_seed(song_seed):
 
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)
73
  def enable_button(x):
74
+ return gr.Button("Ready to Create", interactive=True)
75
  generate_seed_button.click(make_row_visible, inputs=[generate_seed_button], outputs=[concept_row, concept_desc])
76
+ feeling_input.submit(make_row_visible, inputs=[generate_seed_button], outputs=[concept_row, concept_desc])
77
  approve_button.click(enable_button, inputs=[approve_button], outputs=[continue_btn])
78
 
79
  try_again_button.click(generate_song_seed, inputs=[feeling_input], outputs=[instrumental_output])
 
85
 
86
  with gr.TabItem("Generation", id=1): #index is 1
87
  start_song_gen = gr.State(value=False)
88
+ gr.Markdown("""<center><font size=4>Now, chat with an AI songwriter to make your song!</font></center>""")
89
 
90
  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")
91
 
 
101
 
102
  with gr.Row():
103
  with gr.Column(scale=2):
104
+ chatbot_history = gr.Chatbot(type="messages", value=starting_history, label='SongChat', placeholder=None, layout='bubble', bubble_full_width=False, height=500, show_copy_button=True)
105
  with gr.Row():
106
  typical_responses = [textbox, submit]
107
 
 
110
 
111
  button_options = gr.State([])
112
  button_dict = gr.State({
113
+ "revise lyrics": "Can we revise the lyrics?",
114
+ "re-revise lyrics": "Can we revise the lyrics?",
115
+ "edit lyrics directly": "Can I edit the lyrics directly for the whole section?",
116
  "generate audio snippet": "Can you generate an audio snippet?",
117
  "continue revising" : "Can we continue revising this section?",
118
  "generate audio snippet with new lyrics": "Can you generate an audio snippet with these new lyrics?",
119
  "return to original instrumental": "Can you use the original clip for this section instead?",
120
  "revise genre": "Can we revise the instrumental tags?",
121
  "re-revise genre": "Can we revise the instrumental tags?",
122
+ "revise genre directly": "Can I edit the genre directly for the whole song?",
123
  "continue to next section": "Looks good! Let's move on to the next section.",
124
  "merge snippets": "Can you merge this snippet into its full song?"
125
  })
 
191
  return new_step_number, *modals
192
 
193
  def update_textbox(textbox, step_number):
194
+ print("on step number", step_number)
195
  if step_number == 0:
196
  return textbox + "\nAsk me another question to inform the verse"
197
  elif step_number == 1:
 
219
  for btn in typical_responses[2:]:
220
  btn.click(set_response_buttons, inputs=[button_dict, btn], outputs=[textbox]).then(model_chat,
221
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
222
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(
223
  update_response_options, [button_options, button_dict], typical_responses
224
  ).then(
225
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
 
230
 
231
  submit.click(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
232
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
233
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(
234
  update_response_options, [button_options, button_dict], typical_responses
235
  ).then(
236
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
237
  )
238
  textbox.submit(update_textbox, [textbox, tutorial_step], [textbox]).then(model_chat,
239
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
240
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(
241
  update_response_options, [button_options, button_dict], typical_responses
242
  ).then(
243
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
 
246
 
247
  regen.click(set_regenerate_query, inputs=[textbox, current_section, current_lyrics, curr_tags, clip_to_continue], outputs=[textbox]).then(model_chat,
248
  inputs=[genre_input, textbox, chatbot_history, messages, generated_audios],
249
+ outputs=[textbox, chatbot_history, messages, current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio, generated_audios, button_options]).then(
250
  update_response_options, [button_options, button_dict], typical_responses
251
  ).then(
252
  make_modal_visible, [tutorial_step], [tutorial_step, modal, modal_1, modal_2]
253
  )
254
 
255
+ with gr.Row(visible=True):
256
  # get_snippet_button = gr.Button("Get Audio Snippet", scale=2)
257
+ done = gr.Button("Complete User Study 🎶", scale=4)
258
  #autoGPT_checkbox = gr.Checkbox(label="AutoGPT", value=True, info="Auto-generate responses from journal entry", interactive=True, scale=2)
259
  #journal_llm_creativity = gr.Slider(label="Journal LLM Temperature", minimum=0, maximum=1, step=0.01, value=1, interactive=True, scale=2)
260
  reset_button = gr.Button("Reset", scale=2)
chat.py CHANGED
@@ -31,6 +31,12 @@ def determine_title(section_name, generated_audios):
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:
@@ -58,306 +64,425 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
58
  # Step 2: determine if the response from the model includes a tool call.
59
  tool_calls = response_message.choices[0].message.tool_calls
60
  if tool_calls:
61
- messages.append({
62
- "role": response_message.choices[0].message.role,
63
- "content": response_message.choices[0].message.content,
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}
188
- # audio_message = {'role': 'assistant', 'content': "Here's what I've come up with:\n" + suggested_lyrics + "\n\nGenerating audio snippet..."}
189
- new_messages = messages + [tool_message] #, audio_message
190
 
191
- model_response_with_function_call = oai_client.chat.completions.create(
192
- model="gpt-4o",
193
- messages=new_messages,
194
- ) # get a new response from the model where it can see the function response
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..."}
231
- new_messages = messages + [tool_message] #, audio_message]
232
 
233
- model_response_with_function_call = oai_client.chat.completions.create(
234
- model="gpt-4o",
235
- messages=new_messages,
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']
272
- elif isinstance(tool_query_args['sections_written'], list):
273
- current_lyrics = "\n".join(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, []
 
359
 
360
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  if "https://audiopipe.suno.ai/?item_id=" in updated_clip_url:
362
  updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
363
  elif "https://cdn1.suno.ai/" in updated_clip_url:
@@ -365,192 +490,149 @@ def model_chat(genre_input, query: Optional[str], history: Optional[History], me
365
  else:
366
  updated_clip_id = "unknown"
367
 
368
- #pass this info in new tool and assistant message
369
  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}'}
370
- 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>'}
371
-
372
- sections_list = [line for line in current_lyrics.split('\n') if line.startswith('[') and line.endswith(']')]
373
-
374
 
375
  new_messages = messages + [tool_message, audio_message]
376
  new_history = messages_to_history(new_messages)
377
-
378
- if len(sections_list) > 0:
379
- section_name = "Merge up to " + sections_list[-1]
380
- else:
381
- section_name = "Merge up to latest section"
382
- section_name = determine_title(section_name, generated_audios)
383
-
384
- # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
385
- generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, section_name, "streaming"))
386
-
387
  generated_audios = update_song_links(generated_audios)
388
  clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
389
 
 
390
 
391
- 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, []
392
- elif tool_function_name == 'finish_full_song':
393
- ## args are sections_to_be_written, relevant_ideas, last_snippet_id, sni
394
-
395
- ## STEP 0: POP out instrumental args
396
- snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
397
- snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
398
-
399
- if isinstance(tool_query_args['sections_written'], str):
400
- current_lyrics = tool_query_args['sections_written']
401
- elif isinstance(tool_query_args['sections_written'], list):
402
- current_lyrics = "\n".join(tool_query_args['sections_written'])
403
- else:
404
- current_lyrics = ""
405
 
406
- ## STEP 1: WRITE ALL LYRICS using songwriterAssistant
407
- remaining_lyrics = songwriterAssistant.write_all_lyrics(**tool_query_args)
408
- full_lyrics = current_lyrics + remaining_lyrics + "\n[End]"
409
 
 
 
 
 
 
 
 
410
 
411
- # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
412
- yield '', history, messages, "Full Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, None, generated_audios, []
413
 
414
- ## STEP 2: MAKE SONG FOR REMAINING LYRICS
415
- song_link = make_song(remaining_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
 
 
 
416
 
417
- #tool and assistant message
418
- tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'{full_lyrics}'}
419
- audio_message = {'role': 'assistant', 'content': f'New snippet: \n <audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio>'}
420
 
421
- new_messages = messages + [tool_message, audio_message]
422
- new_history = messages_to_history(new_messages)
423
- generated_audios.append((song_link, remaining_lyrics, snippet_instrumental_tags, "Rest of Song", "streaming"))
424
 
425
- yield '', new_history, new_messages, "Rest of Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, song_link, generated_audios, []
 
 
426
 
427
- ## STEP 3: MERGE FULL SONG
428
- if snippet_clip_to_continue_from not in [None, ""]:
429
- updated_clip_url = "still streaming"
430
- while "still streaming" in updated_clip_url:
431
- if "https://audiopipe.suno.ai/?item_id=" in song_link:
432
- clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
433
- else:
434
- clip_id = updated_clip_url.split("https://cdn1.suno.ai/")[1].split(".mp3")[0]
435
- updated_clip_url, updated_lyrics, updated_tags, clips_list = concat_snippets(clip_id)
436
- else:
437
- updated_clip_url, updated_lyrics, clips_list = song_link, remaining_lyrics, []
438
- ## YIELD UPDATED CLIP URL, LYRICS, AND CLIPS LIST
439
- if "https://audiopipe.suno.ai/?item_id=" in updated_clip_url:
440
- updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
441
- elif "https://cdn1.suno.ai/" in updated_clip_url:
442
- updated_clip_id = updated_clip_url.split("https://cdn1.suno.ai/")[1].split(".mp3")[0]
443
- else:
444
- updated_clip_id = "unknown"
445
-
446
- #tool and assistant message
447
- 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}'}
448
- 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>'}
449
-
450
- new_messages = messages + [tool_message, audio_message]
451
- new_history = messages_to_history(new_messages)
452
- generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, "Full Song", "streaming"))
453
- generated_audios = update_song_links(generated_audios)
454
- clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
455
-
456
- 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, []
457
-
458
- elif tool_function_name == 'get_audio_snippet':
459
- #detangle tool_query_args dict
460
- snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
461
- snippet_instrumental_tags = tool_query_args['snippet_instrumental_tags']
462
-
463
- snippet_clip_to_continue_from = tool_query_args.get('snippet_clip_to_continue_from', None)
464
- song_link = make_song(snippet_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
465
-
466
-
467
- if "still streaming" in song_link:
468
- tool_message = {
469
- 'role': 'tool',
470
- 'tool_call_id': tool_call_id,
471
- 'name': tool_function_name,
472
- 'content': 'The snippet to extend is still streaming. Please try generating this audio snippet in a little bit.'
473
- }
474
 
475
- new_messages = messages + [tool_message]
476
 
477
- model_response_with_function_call = oai_client.chat.completions.create(
478
- model="gpt-4o",
479
- messages=new_messages,
480
- ) # get a new response from the model where it can see the function response
481
- current_response = model_response_with_function_call.choices[0].message.content
482
 
483
- role = "assistant"
484
- new_messages = new_messages + [{'role': role, 'content': current_response}]
485
- new_history = messages_to_history(new_messages)
486
 
487
- generated_audios = update_song_links(generated_audios)
488
- clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
489
- buttons = []
490
 
491
- yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
 
 
492
 
493
- return
494
-
495
 
496
- if "no clip with that ID" in song_link:
497
- tool_message = {
498
- 'role': 'tool',
499
- 'tool_call_id': tool_call_id,
500
- 'name': tool_function_name,
501
- 'content': 'The clip ID was incorrect, maybe clarify with the user.'
502
- }
503
 
504
- new_messages = messages + [tool_message]
505
 
506
- model_response_with_function_call = oai_client.chat.completions.create(
507
- model="gpt-4o",
508
- messages=new_messages,
509
- ) # get a new response from the model where it can see the function response
510
- current_response = model_response_with_function_call.choices[0].message.content
511
 
512
- role = "assistant"
513
- new_messages = new_messages + [{'role': role, 'content': current_response}]
514
- new_history = messages_to_history(new_messages)
515
 
516
- generated_audios = update_song_links(generated_audios)
517
- clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
518
- buttons = []
519
 
520
- yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
 
 
 
 
 
 
 
 
 
 
 
521
 
522
- return
523
- print("MAKE SONG IS DONE")
524
- ## filter out suno link from tool query arg
525
- clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
526
-
527
- 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}'}
528
- 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>'
529
- audio_message_content += f'<p>continued from clip: {snippet_clip_to_continue_from}</p>'
530
- audio_message_content += "What do you think?"
531
- audio_message = {'role': 'assistant', 'content': audio_message_content}
532
-
533
 
534
- section_name = snippet_lyrics.split("\n")[0].strip('[]* ')
535
- section_name = determine_title(section_name, generated_audios)
536
 
537
- #audio_message = {'role': 'assistant', 'content': gr.Audio(value=song_link, label=section_name, interactive=False, show_label=False, waveform_options={"show_controls": False})}
538
- new_messages = messages + [tool_message, audio_message]
539
- new_history = messages_to_history(new_messages)
540
- print("AUDIO MESSAGE DONE")
541
- generated_audios.append((song_link, snippet_lyrics, snippet_instrumental_tags, section_name, "streaming"))
542
 
543
- generated_audios = update_song_links(generated_audios)
544
 
545
- buttons = ["revise lyrics", "revise genre", "merge snippets", "continue to next section"]
546
- 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)
547
 
548
- 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
549
 
550
 
551
- else:
552
- print(f"Error: function {tool_function_name} does not exist")
553
-
 
 
 
 
 
 
 
 
554
  else:
555
  # Model did not identify a function to call, result can be returned to the user
556
  current_response = response_message.choices[0].message.content
 
31
  section_name = f"{section_name} {count + 1}"
32
  return section_name
33
 
34
+ async def call_with_timeout(coro, timeout):
35
+ try:
36
+ return await asyncio.wait_for(coro, timeout)
37
+ except asyncio.TimeoutError:
38
+ return "Timeout"
39
+
40
 
41
  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]:
42
  if query is None:
 
64
  # Step 2: determine if the response from the model includes a tool call.
65
  tool_calls = response_message.choices[0].message.tool_calls
66
  if tool_calls:
67
+ try:
68
+
69
+ messages.append({
70
+ "role": response_message.choices[0].message.role,
71
+ "content": response_message.choices[0].message.content,
72
+ "tool_calls": tool_calls,
73
+ "function_call": response_message.choices[0].message.function_call
74
+ })
75
+
76
+ if len(tool_calls) > 1:
77
+ for tool_call in tool_calls:
78
+ tool_message = {
79
+ 'role': 'tool',
80
+ 'tool_call_id': tool_call.id,
81
+ 'name': tool_call.function.name,
82
+ '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."
83
+ }
84
+ messages.append(tool_message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
+ # Generate a response using GPT-4o and add it as a message
87
  model_response_with_function_call = oai_client.chat.completions.create(
88
  model="gpt-4o",
89
+ messages=messages,
90
+ )
91
  current_response = model_response_with_function_call.choices[0].message.content
92
 
93
  role = "assistant"
94
+ messages.append({'role': role, 'content': current_response})
 
95
 
96
+ yield '', messages_to_history(messages), messages, '', '', '', '', None, generated_audios, []
97
+ return
 
 
 
 
 
98
 
 
99
 
100
+ # If true the model will return the name of the tool / function to call and the argument(s)
101
+ for tool_call in tool_calls:
102
+ print(tool_call)
103
+ tool_call_id = tool_call.id
104
+ tool_function_name = tool_call.function.name
105
+ tool_query_args = eval(tool_call.function.arguments)
106
 
107
+ print(tool_function_name, tool_query_args)
108
 
109
+ with open('ai_tools.json') as f:
110
+ ai_tools = json.load(f)
111
+
112
+ for tool in ai_tools:
113
+ if tool['function']['name'] == tool_function_name:
114
+ valid_keys = tool['function']['parameters']['properties'].keys()
115
+ required_keys = tool['function']['parameters']['required']
116
+ break
117
+
118
+ print('query args before', tool_query_args)
119
+ tool_query_args = {k: v for k, v in tool_query_args.items() if k in valid_keys}
120
+ print('query args after', tool_query_args)
121
+ missing_keys = []
122
+ for key in required_keys:
123
+ if key not in tool_query_args:
124
+ missing_keys.append(key)
125
+ if len(missing_keys)>0:
126
+ missing_keys_str = ", ".join(missing_keys)
127
+ tool_message = {
128
+ 'role': 'tool',
129
+ 'tool_call_id': tool_call_id,
130
+ 'name': tool_function_name,
131
+ '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."
132
+ }
133
 
134
+ new_messages = messages + [tool_message]
 
 
 
 
135
 
136
+ model_response_with_function_call = oai_client.chat.completions.create(
137
+ model="gpt-4o",
138
+ messages=new_messages,
139
+ ) # get a new response from the model where it can see the function response
140
+ current_response = model_response_with_function_call.choices[0].message.content
141
 
142
+ role = "assistant"
143
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
144
+ new_history = messages_to_history(new_messages)
 
 
145
 
146
+ generated_audios = update_song_links(generated_audios)
147
+ yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
148
+
149
+
150
+ # Step 3: Call the function and retrieve results. Append the results to the messages list.
151
+ if tool_function_name == 'ask_question':
152
+ question = songwriterAssistant.ask_question(messages)
153
 
154
+ question = question.replace("ask question:", "").replace("ask question ", "").replace("ask question\n", "").replace("ask question", "")
 
155
 
156
+ ## yield question in tool and assistant message
157
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': question}
 
 
 
 
158
 
159
+ new_messages = messages + [tool_message]
 
 
 
160
 
161
+ question_message = {'role': 'assistant', 'content': question}
162
+ new_messages = new_messages + [question_message]
163
+ new_history = messages_to_history(new_messages)
 
 
164
 
165
+ generated_audios = update_song_links(generated_audios)
166
+ yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
167
+
168
+ elif tool_function_name == 'edit_directly':
169
+ edit_type = tool_query_args.get('edit_type', 'item')
170
+ current_item_to_edit = tool_query_args.get('current_item_to_edit', '')
171
+
172
+ content_message = f"Sure, you can edit the {edit_type}. Here you go, edit and submit your new version in the chat textbox!"
173
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': content_message}
174
+
175
+ new_messages = messages + [tool_message]
176
 
177
+ assistant_message = {'role': 'assistant', 'content': content_message}
178
+ new_messages = new_messages + [assistant_message]
179
+ new_history = messages_to_history(new_messages)
180
 
181
+ generated_audios = update_song_links(generated_audios)
182
+ yield f"Here are my edits:\n{current_item_to_edit}", new_history, new_messages, '', '', '', '', None, generated_audios, []
183
+
184
+ # elif tool_function_name == 'clarify_arguments':
185
+ # 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'])}
186
 
187
+ # new_messages = messages + [tool_message]
188
 
189
+ # model_response_with_function_call = oai_client.chat.completions.create(
190
+ # model="gpt-4o",
191
+ # messages=new_messages,
192
+ # ) # get a new response from the model where it can see the function response
193
+ # current_response = model_response_with_function_call.choices[0].message.content
194
 
195
+ # role = "assistant"
196
+ # new_messages = new_messages + [{'role': role, 'content': current_response}] # + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
197
+ # # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
198
+ # new_history = messages_to_history(new_messages)
199
 
200
+ # generated_audios = update_song_links(generated_audios)
201
+ # yield '', new_history, new_messages, '', '', '', '', None, generated_audios, []
202
 
203
+
204
+ elif tool_function_name == 'write_section':
205
+ snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
206
+ snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
207
+ suggested_lyrics = songwriterAssistant.write_section(**tool_query_args)
208
+ suggested_lyrics = suggested_lyrics.strip('`*-\n')
209
 
210
+ ## yield suggested lyrics in tool and assistant message
211
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': suggested_lyrics}
212
+ # audio_message = {'role': 'assistant', 'content': "Here's what I've come up with:\n" + suggested_lyrics + "\n\nGenerating audio snippet..."}
213
+ new_messages = messages + [tool_message] #, audio_message
214
 
215
+ model_response_with_function_call = oai_client.chat.completions.create(
216
+ model="gpt-4o",
217
+ messages=new_messages,
218
+ ) # get a new response from the model where it can see the function response
219
+ current_response = model_response_with_function_call.choices[0].message.content
 
 
 
 
220
 
221
+ role = "assistant"
222
+ new_messages = new_messages + [{'role': role, 'content': current_response}] # + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
223
+ # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
224
+ history = messages_to_history(new_messages)
225
 
226
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
227
+ buttons = ["revise lyrics", "edit lyrics directly", "generate audio snippet", "continue to next section"]
 
 
 
228
 
229
+ generated_audios = update_song_links(generated_audios)
230
+ 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)
231
 
 
 
 
 
 
 
232
 
 
233
 
234
+ yield '', history, new_messages, tool_query_args['section_name'], suggested_lyrics.split(':')[-1], snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
235
 
236
+ ### DO SOMETHING TO UPDATE CURRENT GENERATION for write_section
 
 
 
 
237
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
+ elif tool_function_name == 'revise_section_lyrics':
240
+ revised_lyrics = songwriterAssistant.revise_section_lyrics(**tool_query_args)
241
 
242
+ # if isinstance(revised_lyrics, list):
243
+ # revised_lyrics = '\n'.join(revised_lyrics)
244
+ if isinstance(revised_lyrics, str) and revised_lyrics.startswith("[") and revised_lyrics.endswith("]"):
245
+ try:
246
+ revised_lyrics = eval(revised_lyrics)
247
+ if isinstance(revised_lyrics, list):
248
+ revised_lyrics = '\n'.join(revised_lyrics)
249
+ except:
250
+ pass
251
 
252
+ # ## yield revised lyrics in tool and assistant message
253
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': revised_lyrics}
254
+ # audio_message = {'role': 'assistant', 'content': "Here's my revised lyrics:\n" + revised_lyrics + "\n\nGenerating audio snippet..."}
255
+ new_messages = messages + [tool_message] #, audio_message]
 
 
256
 
257
+ model_response_with_function_call = oai_client.chat.completions.create(
258
+ model="gpt-4o",
259
+ messages=new_messages,
260
+ ) # get a new response from the model where it can see the function response
261
+ current_response = model_response_with_function_call.choices[0].message.content
262
 
263
+ buttons = ["re-revise lyrics", "edit lyrics directly", "generate audio snippet with new lyrics", "continue to next section"]
264
 
265
+ role = "assistant"
266
+ new_messages = new_messages + [{'role': role, 'content': current_response + "\n\nWould you like to get an audio snippet? Or continue writing?"}]
267
+ # new_messages = [msg for msg in new_messages if msg['content'] is not None and msg['role'] in ['user', 'assistant']]
268
+ history = messages_to_history(new_messages)
269
+ generated_audios = update_song_links(generated_audios)
270
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
 
 
 
 
 
 
271
 
272
+ yield '', history, new_messages, tool_query_args['section_name'], revised_lyrics, '', clips_to_continue, None, generated_audios, buttons
 
 
 
 
273
 
 
274
 
275
+ elif tool_function_name == 'revise_instrumental_tags':
276
+ #detangle tool_query_args dict
277
+ #snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
278
+ snippet_instrumental_tags = tool_query_args.get('current_instrumental_tags', None)
279
+ user_instrumental_feedback = tool_query_args.get('user_instrumental_feedback', None)
 
280
 
281
+ if snippet_instrumental_tags is None or user_instrumental_feedback is None:
282
+ 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.'}
283
+ 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.'}
284
+ new_messages = messages + [tool_message, audio_message]
285
+ new_history = messages_to_history(new_messages)
286
+ yield '', new_history, new_messages, '', '', '', None, None, generated_audios, []
287
+ return
288
+ # if 'snippet_clip_to_continue_from' not in tool_query_args:
289
+ # tool_query_args['snippet_clip_to_continue_from'] = None
290
+ # snippet_clip_to_continue_from = tool_query_args['snippet_clip_to_continue_from']
291
 
292
+ new_instrumental_tags = songwriterAssistant.revise_instrumental_tags(snippet_instrumental_tags, user_instrumental_feedback)
293
 
294
+ if isinstance(tool_query_args['sections_written'], str):
295
+ current_lyrics = tool_query_args['sections_written']
296
+ elif isinstance(tool_query_args['sections_written'], list):
297
+ current_lyrics = "\n".join(tool_query_args['sections_written'])
298
+ else:
299
+ current_lyrics = ""
300
 
301
+ import re
302
+ sections_list = re.findall(r'\[.*?\]', current_lyrics)
 
303
 
304
+ #current_lyrics = "\n".join(tool_query_args['sections_written'])
305
+ song_link = make_song(current_lyrics, new_instrumental_tags)
306
+ ## filter out suno link from tool query arg
307
+ while "https://audiopipe.suno.ai/?item_id=" not in song_link:
308
+ print("BUGGED OUT, trying again...")
309
+ if song_link == "Timeout":
310
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': 'The request to generate the song timed out. Please try again.'}
311
+ audio_message = {'role': 'assistant', 'content': 'The request to generate the song timed out. Try again later or rephrase your request.'}
312
+ new_messages = messages + [tool_message, audio_message]
313
+ new_history = messages_to_history(new_messages)
314
+ yield '', new_history, new_messages, '', '', '', None, None, generated_audios, []
315
+ return
316
+ time.sleep(5)
317
+ song_link = make_song(current_lyrics, new_instrumental_tags)
318
+
319
+ clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
320
+
321
+ 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}'}
322
+ 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>'}
323
+ audio_message['content'] += f'\n\nWhat do you think?'
324
  new_messages = messages + [tool_message, audio_message]
325
  new_history = messages_to_history(new_messages)
326
 
327
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
328
+ if len(sections_list) > 0:
329
+ section_name = f"Up to {sections_list[-1]}"
330
+ else:
331
+ section_name = "Up to latest section"
332
+ section_name = determine_title(section_name, generated_audios)
333
+
334
+ generated_audios.append((song_link, current_lyrics, new_instrumental_tags, section_name, "streaming"))
335
+ generated_audios = update_song_links(generated_audios)
336
  clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
337
 
338
+ buttons = ["return to original instrumental", "re-revise genre", "edit genre directly", "revise lyrics", "merge snippets", "continue to next section"]
339
+
340
+ 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
341
+ elif tool_function_name == 'revise_section_lyrics_and_instrumental':
342
+ snippet_instrumental_tags = tool_query_args.pop('current_instrumental_tags', None)
343
+ user_instrumental_feedback = tool_query_args.pop('user_instrumental_feedback', None)
344
+ snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
345
+
346
+ # Revise section lyrics
347
+ revised_lyrics = songwriterAssistant.revise_section_lyrics(**tool_query_args)
348
+
349
+ # Revise instrumental tags
350
+
351
+ new_instrumental_tags = songwriterAssistant.revise_instrumental_tags(snippet_instrumental_tags, user_instrumental_feedback)
352
+
353
+ song_link = make_song(current_lyrics, new_instrumental_tags)
354
+ ## filter out suno link from tool query arg
355
+ while "https://audiopipe.suno.ai/?item_id=" not in song_link:
356
+ print("BUGGED OUT, trying again...")
357
+ if song_link == "Timeout":
358
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': 'The request to generate the song timed out. Please try again.'}
359
+ audio_message = {'role': 'assistant', 'content': 'The request to generate the song timed out. Try again later or rephrase your request.'}
360
+ new_messages = messages + [tool_message, audio_message]
361
+ new_history = messages_to_history(new_messages)
362
+ yield '', new_history, new_messages, '', '', '', None, None, generated_audios, []
363
+ return
364
+ time.sleep(5)
365
+ song_link = make_song(current_lyrics, new_instrumental_tags)
366
+ clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
367
+
368
+ 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}'}
369
+ 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>'}
370
+ audio_message['content'] += f'\n\nWhat do you think?'
371
+
372
+ new_messages = messages + [tool_message_instrumental, audio_message]
373
+ new_history = messages_to_history(new_messages)
374
+
375
+ generated_audios.append((song_link, revised_lyrics, new_instrumental_tags, tool_query_args["section_name"], "streaming"))
376
+ generated_audios = update_song_links(generated_audios)
377
+ 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)
378
+
379
+ buttons = ["return to original instrumental", "re-revise genre", "edit genre directly", "revise lyrics", "edit lyrics directly", "merge snippets", "continue to next section"]
380
+
381
+ 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
382
+
383
+ elif tool_function_name == 'merge_all_snippets':
384
+ updated_clip_url, updated_lyrics, updated_tags, clips_list = concat_snippets(tool_query_args['last_snippet_id'])
385
+
386
+ if updated_clip_url == "Timeout":
387
+ # Handle the timeout case
388
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': 'The operation timed out. Please try again later.'}
389
+ audio_message = {'role': 'assistant', 'content': 'The concatenation operation took too long and was stopped. Please try again later or rephrase your request.'}
390
+ new_messages = messages + [tool_message, audio_message]
391
+ new_history = messages_to_history(new_messages)
392
+ yield '', new_history, new_messages, '', '', '', None, None, generated_audios, []
393
+ return
394
+
395
+ if "still streaming" in updated_clip_url:
396
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'still streaming, try again later'}
397
+ 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.'}
398
+
399
+ new_messages = messages + [tool_message, audio_message]
400
+ new_history = messages_to_history(new_messages)
401
+
402
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
403
+
404
+
405
+ yield '', new_history, new_messages, "", "", "", clips_to_continue, None, generated_audios, []
406
+
407
+ else:
408
+ if "https://audiopipe.suno.ai/?item_id=" in updated_clip_url:
409
+ updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
410
+ elif "https://cdn1.suno.ai/" in updated_clip_url:
411
+ updated_clip_id = updated_clip_url.split("https://cdn1.suno.ai/")[1].split(".mp3")[0]
412
+ else:
413
+ updated_clip_id = "unknown"
414
+
415
+ #pass this info in new tool and assistant message
416
+ 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}'}
417
+ 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>'}
418
+
419
+ sections_list = [line for line in current_lyrics.split('\n') if line.startswith('[') and line.endswith(']')]
420
+
421
+
422
+ new_messages = messages + [tool_message, audio_message]
423
+ new_history = messages_to_history(new_messages)
424
+
425
+ if len(sections_list) > 0:
426
+ section_name = "Merge up to " + sections_list[-1]
427
+ else:
428
+ section_name = "Merge up to latest section"
429
+ section_name = determine_title(section_name, generated_audios)
430
+
431
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
432
+ generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, section_name, "streaming"))
433
 
434
+ generated_audios = update_song_links(generated_audios)
435
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
436
 
437
+
438
+ 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, []
439
+ elif tool_function_name == 'finish_full_song':
440
+ ## args are sections_to_be_written, relevant_ideas, last_snippet_id, sni
441
+
442
+ ## STEP 0: POP out instrumental args
443
+ snippet_instrumental_tags = tool_query_args.pop('snippet_instrumental_tags', None)
444
+ snippet_clip_to_continue_from = tool_query_args.pop('snippet_clip_to_continue_from', None)
445
+
446
+ if isinstance(tool_query_args['sections_written'], str):
447
+ current_lyrics = tool_query_args['sections_written']
448
+ elif isinstance(tool_query_args['sections_written'], list):
449
+ current_lyrics = "\n".join(tool_query_args['sections_written'])
450
+ else:
451
+ current_lyrics = ""
452
+
453
+ ## STEP 1: WRITE ALL LYRICS using songwriterAssistant
454
+ remaining_lyrics = songwriterAssistant.write_all_lyrics(**tool_query_args)
455
+ full_lyrics = current_lyrics + remaining_lyrics + "\n[End]"
456
+
457
+
458
+ # current_section, current_lyrics, curr_tags, clip_to_continue, curr_audio
459
+ # yield '', history, messages, "Full Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, None, generated_audios, []
460
+
461
+ ## STEP 2: MAKE SONG FOR REMAINING LYRICS
462
+ song_link = make_song(remaining_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
463
+
464
+ #tool and assistant message
465
+ tool_message = {'role': 'tool', 'tool_call_id': tool_call_id, 'name': tool_function_name, 'content': f'{full_lyrics}'}
466
+ audio_message = {'role': 'assistant', 'content': f'New snippet: \n <audio controls autoplay><source src="{song_link}" type="audio/mp3"></audio>'}
467
+
468
+ new_messages = messages + [tool_message, audio_message]
469
+ new_history = messages_to_history(new_messages)
470
+ generated_audios.append((song_link, remaining_lyrics, snippet_instrumental_tags, "Rest of Song", "streaming"))
471
+
472
+ yield '', new_history, new_messages, "Rest of Song", full_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from, song_link, generated_audios, []
473
+
474
+ ## STEP 3: MERGE FULL SONG
475
+ if snippet_clip_to_continue_from not in [None, ""]:
476
+ updated_clip_url = "still streaming"
477
+ while "still streaming" in updated_clip_url:
478
+ if "https://audiopipe.suno.ai/?item_id=" in song_link:
479
+ clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
480
+ else:
481
+ clip_id = updated_clip_url.split("https://cdn1.suno.ai/")[1].split(".mp3")[0]
482
+ updated_clip_url, updated_lyrics, updated_tags, clips_list = concat_snippets(clip_id)
483
+ else:
484
+ updated_clip_url, updated_lyrics, clips_list = song_link, remaining_lyrics, []
485
+ ## YIELD UPDATED CLIP URL, LYRICS, AND CLIPS LIST
486
  if "https://audiopipe.suno.ai/?item_id=" in updated_clip_url:
487
  updated_clip_id = updated_clip_url.split("https://audiopipe.suno.ai/?item_id=")[1]
488
  elif "https://cdn1.suno.ai/" in updated_clip_url:
 
490
  else:
491
  updated_clip_id = "unknown"
492
 
493
+ #tool and assistant message
494
  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}'}
495
+ 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>'}
 
 
 
496
 
497
  new_messages = messages + [tool_message, audio_message]
498
  new_history = messages_to_history(new_messages)
499
+ generated_audios.append((updated_clip_url, updated_lyrics, updated_tags, "Full Song", "streaming"))
 
 
 
 
 
 
 
 
 
500
  generated_audios = update_song_links(generated_audios)
501
  clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
502
 
503
+ 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, []
504
 
505
+ elif tool_function_name == 'get_audio_snippet':
506
+ #detangle tool_query_args dict
507
+ snippet_lyrics = tool_query_args['snippet_lyrics'] + "\n[End]"
508
+ snippet_instrumental_tags = tool_query_args['snippet_instrumental_tags']
 
 
 
 
 
 
 
 
 
 
509
 
510
+ snippet_clip_to_continue_from = tool_query_args.get('snippet_clip_to_continue_from', None)
511
+ song_link = make_song(snippet_lyrics, snippet_instrumental_tags, snippet_clip_to_continue_from)
 
512
 
513
+ if song_link == "Timeout":
514
+ tool_message = {
515
+ 'role': 'tool',
516
+ 'tool_call_id': tool_call_id,
517
+ 'name': tool_function_name,
518
+ 'content': 'The request to generate the audio snippet timed out. Please try again later.'
519
+ }
520
 
521
+ new_messages = messages + [tool_message]
 
522
 
523
+ model_response_with_function_call = oai_client.chat.completions.create(
524
+ model="gpt-4o",
525
+ messages=new_messages,
526
+ ) # get a new response from the model where it can see the function response
527
+ current_response = model_response_with_function_call.choices[0].message.content
528
 
529
+ role = "assistant"
530
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
531
+ new_history = messages_to_history(new_messages)
532
 
533
+ generated_audios = update_song_links(generated_audios)
534
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
535
+ buttons = []
536
 
537
+ yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
538
+
539
+ return
540
 
541
+ if "still streaming" in song_link:
542
+ tool_message = {
543
+ 'role': 'tool',
544
+ 'tool_call_id': tool_call_id,
545
+ 'name': tool_function_name,
546
+ 'content': 'The snippet to extend is still streaming. Please try generating this audio snippet in a little bit.'
547
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
+ new_messages = messages + [tool_message]
550
 
551
+ model_response_with_function_call = oai_client.chat.completions.create(
552
+ model="gpt-4o",
553
+ messages=new_messages,
554
+ ) # get a new response from the model where it can see the function response
555
+ current_response = model_response_with_function_call.choices[0].message.content
556
 
557
+ role = "assistant"
558
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
559
+ new_history = messages_to_history(new_messages)
560
 
561
+ generated_audios = update_song_links(generated_audios)
562
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
563
+ buttons = []
564
 
565
+ yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
566
+
567
+ return
568
 
 
 
569
 
570
+ if "no clip with that ID" in song_link:
571
+ tool_message = {
572
+ 'role': 'tool',
573
+ 'tool_call_id': tool_call_id,
574
+ 'name': tool_function_name,
575
+ 'content': 'The clip ID was incorrect, maybe clarify with the user.'
576
+ }
577
 
578
+ new_messages = messages + [tool_message]
579
 
580
+ model_response_with_function_call = oai_client.chat.completions.create(
581
+ model="gpt-4o",
582
+ messages=new_messages,
583
+ ) # get a new response from the model where it can see the function response
584
+ current_response = model_response_with_function_call.choices[0].message.content
585
 
586
+ role = "assistant"
587
+ new_messages = new_messages + [{'role': role, 'content': current_response}]
588
+ new_history = messages_to_history(new_messages)
589
 
590
+ generated_audios = update_song_links(generated_audios)
591
+ clips_to_continue = gr.Dropdown(label='Clip to continue', value = "", choices=[x[3] for x in generated_audios]+[""], interactive=True)
592
+ buttons = []
593
 
594
+ yield '', new_history, new_messages, snippet_lyrics.split("\n")[0], snippet_lyrics, snippet_instrumental_tags, clips_to_continue, None, generated_audios, buttons
595
+
596
+ return
597
+ print("MAKE SONG IS DONE")
598
+ ## filter out suno link from tool query arg
599
+ clip_id = song_link.split("https://audiopipe.suno.ai/?item_id=")[1]
600
+
601
+ 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}'}
602
+ 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>'
603
+ audio_message_content += f'<p>continued from clip: {snippet_clip_to_continue_from}</p>'
604
+ audio_message_content += "What do you think?"
605
+ audio_message = {'role': 'assistant', 'content': audio_message_content}
606
 
 
 
 
 
 
 
 
 
 
 
 
607
 
608
+ section_name = snippet_lyrics.split("\n")[0].strip('[]* ')
609
+ section_name = determine_title(section_name, generated_audios)
610
 
611
+ #audio_message = {'role': 'assistant', 'content': gr.Audio(value=song_link, label=section_name, interactive=False, show_label=False, waveform_options={"show_controls": False})}
612
+ new_messages = messages + [tool_message, audio_message]
613
+ new_history = messages_to_history(new_messages)
614
+ print("AUDIO MESSAGE DONE")
615
+ generated_audios.append((song_link, snippet_lyrics, snippet_instrumental_tags, section_name, "streaming"))
616
 
617
+ generated_audios = update_song_links(generated_audios)
618
 
619
+ buttons = ["revise lyrics", "edit lyrics directly", "revise genre", "edit genre directly", "merge snippets", "continue to next section"]
620
+ 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)
621
 
622
+ 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
623
 
624
 
625
+ else:
626
+ print(f"Error: function {tool_function_name} does not exist")
627
+ except Exception as e:
628
+ error_message = {
629
+ 'role': 'assistant',
630
+ 'content': f"An error occurred while processing your request: {str(e)}. Please re-phrase your request and try again."
631
+ }
632
+ messages_filtered.append(error_message)
633
+ yield '', messages_to_history(messages), messages_filtered, '', '', '', '', None, generated_audios, []
634
+
635
+
636
  else:
637
  # Model did not identify a function to call, result can be returned to the user
638
  current_response = response_message.choices[0].message.content