Spaces:
Sleeping
Sleeping
Manuel Zafra
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -9,17 +9,15 @@ from tools.final_answer import FinalAnswerTool
|
|
9 |
import tempfile
|
10 |
import shutil
|
11 |
|
12 |
-
#
|
13 |
HISTORY_FILE = "song_history.json"
|
14 |
|
15 |
-
# Supported languages
|
16 |
LANGUAGES = {
|
17 |
"English": "en",
|
18 |
"Español": "es",
|
19 |
"Français": "fr"
|
20 |
}
|
21 |
|
22 |
-
# Language-specific messages
|
23 |
MESSAGES = {
|
24 |
"en": {
|
25 |
"title": "# 🎵 Music Recognition & Fun Facts",
|
@@ -95,43 +93,25 @@ MESSAGES = {
|
|
95 |
}
|
96 |
}
|
97 |
|
98 |
-
#
|
99 |
@tool
|
100 |
def recognize_song(audio_path: str) -> dict:
|
101 |
-
"""Recognizes a song from an audio file
|
102 |
-
Args:
|
103 |
-
audio_path: path to the audio file to recognize
|
104 |
-
"""
|
105 |
AUDD_API_TOKEN = os.getenv("AUDD_API_TOKEN")
|
106 |
-
|
107 |
if not os.path.exists(audio_path):
|
108 |
return {"error": "The audio file does not exist"}
|
109 |
-
|
110 |
try:
|
111 |
with open(audio_path, 'rb') as file:
|
112 |
-
data = {
|
113 |
-
|
114 |
-
'return': 'spotify,apple_music'
|
115 |
-
}
|
116 |
-
files = {
|
117 |
-
'file': file
|
118 |
-
}
|
119 |
response = requests.post('https://api.audd.io/', data=data, files=files)
|
120 |
-
|
121 |
if response.status_code != 200:
|
122 |
return {"error": f"API Error: {response.status_code}"}
|
123 |
-
|
124 |
result = response.json()
|
125 |
-
|
126 |
if result['status'] == 'error':
|
127 |
return {"error": result['error']['error_message']}
|
128 |
-
|
129 |
if not result.get('result'):
|
130 |
return {"error": "Could not recognize the song"}
|
131 |
-
|
132 |
song_info = result['result']
|
133 |
-
|
134 |
-
# Create object with song information
|
135 |
song_data = {
|
136 |
"Song": song_info.get('title', 'Unknown'),
|
137 |
"Artist": song_info.get('artist', 'Unknown'),
|
@@ -140,115 +120,60 @@ def recognize_song(audio_path: str) -> dict:
|
|
140 |
"Apple Music": song_info.get('apple_music', {}).get('url', 'Not available'),
|
141 |
"Recognition Date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
142 |
}
|
143 |
-
|
144 |
-
# Save to history
|
145 |
save_to_history(song_data)
|
146 |
-
|
147 |
return song_data
|
148 |
-
|
149 |
except Exception as e:
|
150 |
return {"error": f"Error processing audio: {str(e)}"}
|
151 |
|
152 |
-
# Function to save song to history
|
153 |
def save_to_history(song_data):
|
154 |
-
"""Saves a song to the history"""
|
155 |
try:
|
156 |
-
# Load existing history or create new
|
157 |
if os.path.exists(HISTORY_FILE):
|
158 |
with open(HISTORY_FILE, 'r') as f:
|
159 |
-
|
160 |
-
history = json.load(f)
|
161 |
-
except json.JSONDecodeError:
|
162 |
-
history = []
|
163 |
else:
|
164 |
history = []
|
165 |
-
|
166 |
-
# Add song to the beginning of history (most recent first)
|
167 |
history.insert(0, song_data)
|
168 |
-
|
169 |
-
# Limit history to the 50 most recent songs
|
170 |
if len(history) > 50:
|
171 |
history = history[:50]
|
172 |
-
|
173 |
-
# Save updated history
|
174 |
with open(HISTORY_FILE, 'w') as f:
|
175 |
json.dump(history, f, indent=2)
|
176 |
-
|
177 |
except Exception as e:
|
178 |
print(f"Error saving to history: {str(e)}")
|
179 |
|
180 |
-
# Tool to view song history
|
181 |
@tool
|
182 |
def view_song_history(limit: int = 10, language: str = "en") -> str:
|
183 |
-
"""Shows the history of recognized songs
|
184 |
-
Args:
|
185 |
-
limit: maximum number of songs to display (default 10)
|
186 |
-
language: language code (en, es, fr)
|
187 |
-
"""
|
188 |
lang = LANGUAGES.get(language, "en")
|
189 |
try:
|
190 |
if not os.path.exists(HISTORY_FILE):
|
191 |
return f"📋 {MESSAGES[lang]['no_history']}"
|
192 |
-
|
193 |
with open(HISTORY_FILE, 'r') as f:
|
194 |
-
|
195 |
-
history = json.load(f)
|
196 |
-
except json.JSONDecodeError:
|
197 |
-
return "📋 Error loading song history."
|
198 |
-
|
199 |
if not history:
|
200 |
return f"📋 {MESSAGES[lang]['empty_history']}"
|
201 |
-
|
202 |
-
# Limit the number of songs to display
|
203 |
history = history[:min(limit, len(history))]
|
204 |
-
|
205 |
-
# Format output
|
206 |
result = f"📋 **{MESSAGES[lang]['history_title']}:**\n\n"
|
207 |
-
|
208 |
for i, song in enumerate(history, 1):
|
209 |
result += f"{i}. **{song.get('Song', 'Unknown')}** - *{song.get('Artist', 'Unknown')}*\n"
|
210 |
result += f" 📀 Album: {song.get('Album', 'Unknown')}\n"
|
211 |
-
|
212 |
if song.get('Spotify', 'Not available') != 'Not available':
|
213 |
result += f" 🎧 [Spotify]({song.get('Spotify')})\n"
|
214 |
-
|
215 |
if song.get('Apple Music', 'Not available') != 'Not available':
|
216 |
result += f" 🍏 [Apple Music]({song.get('Apple Music')})\n"
|
217 |
-
|
218 |
result += f" 🕒 Recognized on: {song.get('Recognition Date', 'Unknown date')}\n\n"
|
219 |
-
|
220 |
return result
|
221 |
-
|
222 |
except Exception as e:
|
223 |
return f"❌ {MESSAGES[lang]['history_error'].format(error=str(e))}"
|
224 |
|
225 |
-
# Tool to get artist information in selected language
|
226 |
@tool
|
227 |
def get_artist_info(artist_name: str, song_title: str, language: str = "en") -> dict:
|
228 |
-
"""Gets background information and fun facts about a music artist in the specified language
|
229 |
-
Args:
|
230 |
-
artist_name: name of the artist to get information about
|
231 |
-
song_title: title of the song
|
232 |
-
language: language code (en, es, fr)
|
233 |
-
"""
|
234 |
-
# Language-specific prompts
|
235 |
prompts = {
|
236 |
"en": f"Provide interesting and fun facts about the music artist '{artist_name}' that fans would love to know. Include stories about their career, personal anecdotes, how their song '{song_title}' was created if known, or surprising facts about their life. Include an image URL if possible. Focus on highlighting their contributions to music and anything that makes them unique as an artist. Keep it engaging and positive.",
|
237 |
"es": f"Proporciona datos interesantes y curiosos sobre el artista musical '{artist_name}' que a los fans les encantaría conocer. Incluye historias sobre su carrera, anécdotas personales, cómo se creó su canción '{song_title}' si se sabe, o datos sorprendentes sobre su vida. Incluye una URL de imagen si es posible. Céntrate en destacar sus contribuciones a la música y cualquier cosa que les haga únicos como artistas. Mantenlo atractivo y positivo.",
|
238 |
"fr": f"Fournissez des faits intéressants et amusants sur l'artiste musical '{artist_name}' que les fans aimeraient connaître. Incluez des histoires sur leur carrière, des anecdotes personnelles, comment leur chanson '{song_title}' a été créée si connu, ou des faits surprenants sur leur vie. Incluez une URL d'image si possible. Concentrez-vous sur la mise en valeur de leurs contributions à la musique et tout ce qui les rend uniques en tant qu'artiste. Gardez-le engageant et positif.",
|
239 |
}
|
240 |
-
|
241 |
-
|
242 |
-
if language not in prompts:
|
243 |
-
language = "en"
|
244 |
-
|
245 |
-
# Create messages for the model
|
246 |
-
messages = [
|
247 |
-
{"role": "user", "content": prompts[language]}
|
248 |
-
]
|
249 |
-
|
250 |
try:
|
251 |
-
# Using the same model that powers our agent
|
252 |
response = model(messages)
|
253 |
content = response.content
|
254 |
image_url = None
|
@@ -261,45 +186,26 @@ def get_artist_info(artist_name: str, song_title: str, language: str = "en") ->
|
|
261 |
except Exception as e:
|
262 |
return {"text": f"Could not retrieve information about {artist_name}: {str(e)}", "image": None}
|
263 |
|
264 |
-
# Chat with LLM tool
|
265 |
@tool
|
266 |
def chat_with_assistant(query: str, artist_info: str = "", language: str = "en") -> str:
|
267 |
-
"""Chat with the AI assistant about any music related topic in the specified language
|
268 |
-
Args:
|
269 |
-
query: user's question or request
|
270 |
-
artist_info: previous artist info to provide context
|
271 |
-
language: language code (en, es, fr)
|
272 |
-
"""
|
273 |
-
# Language-specific system messages
|
274 |
system_messages = {
|
275 |
"en": "You are a music expert assistant who specializes in providing engaging information about artists, songs, and music history. Focus on sharing interesting, fun facts that highlight the artist's unique contributions and stories.",
|
276 |
"es": "Eres un asistente experto en música que se especializa en proporcionar información interesante sobre artistas, canciones e historia musical. Céntrate en compartir datos curiosos e interesantes que destaquen las contribuciones únicas y las historias del artista.",
|
277 |
"fr": "Vous êtes un assistant expert en musique qui se spécialise dans la fourniture d'informations intéressantes sur les artistes, les chansons et l'histoire de la musique. Concentrez-vous sur le partage de faits intéressants et amusants qui mettent en évidence les contributions uniques et les histoires de l'artiste.",
|
278 |
}
|
279 |
-
|
280 |
-
|
281 |
-
if language not in system_messages:
|
282 |
-
language = "en"
|
283 |
-
|
284 |
-
# Create context
|
285 |
-
context = ""
|
286 |
-
if artist_info:
|
287 |
-
context = f"Previous context about the artist: {artist_info}\n\n"
|
288 |
-
|
289 |
-
# Create messages for the model
|
290 |
messages = [
|
291 |
{"role": "system", "content": system_messages[language]},
|
292 |
{"role": "user", "content": context + query}
|
293 |
]
|
294 |
-
|
295 |
try:
|
296 |
-
# Using the model to generate a response
|
297 |
response = model(messages)
|
298 |
return response.content
|
299 |
except Exception as e:
|
300 |
return f"Sorry, I couldn't process your request: {str(e)}"
|
301 |
|
302 |
-
#
|
303 |
final_answer = FinalAnswerTool()
|
304 |
model = HfApiModel(
|
305 |
max_tokens=2096,
|
@@ -311,16 +217,9 @@ model = HfApiModel(
|
|
311 |
with open("prompts.yaml", 'r') as stream:
|
312 |
prompt_templates = yaml.safe_load(stream)
|
313 |
|
314 |
-
# Agent definition
|
315 |
agent = CodeAgent(
|
316 |
model=model,
|
317 |
-
tools=[
|
318 |
-
final_answer,
|
319 |
-
recognize_song,
|
320 |
-
view_song_history,
|
321 |
-
get_artist_info,
|
322 |
-
chat_with_assistant
|
323 |
-
],
|
324 |
max_steps=8,
|
325 |
verbosity_level=1,
|
326 |
grammar=None,
|
@@ -330,240 +229,177 @@ agent = CodeAgent(
|
|
330 |
prompt_templates=prompt_templates
|
331 |
)
|
332 |
|
333 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
334 |
with gr.Blocks(title="Music Recognition & Fun Facts", css=".large-text {font-size: 1.5em !important;} .big-button {font-size: 1.2em; padding: 15px 30px;}") as demo:
|
335 |
-
# State variables
|
336 |
selected_language = gr.State("en")
|
337 |
song_info_state = gr.State(None)
|
338 |
artist_info_state = gr.State("")
|
339 |
-
audio_status = gr.State("no_audio")
|
340 |
-
|
341 |
-
|
342 |
-
gr.Markdown(value=
|
343 |
-
|
344 |
-
|
345 |
-
# Language selection
|
346 |
with gr.Row():
|
347 |
language_dropdown = gr.Dropdown(
|
348 |
choices=[("English", "en"), ("Español", "es"), ("Français", "fr")],
|
349 |
value="en",
|
350 |
-
label=
|
351 |
)
|
352 |
-
|
353 |
-
# Combined recognition and chat interface
|
354 |
with gr.Tab("Song Recognition & Chat"):
|
355 |
-
|
356 |
-
audio_status_msg = gr.Markdown(value=lambda: MESSAGES[selected_language.value]["no_audio"])
|
357 |
-
|
358 |
-
# Song recognition section
|
359 |
with gr.Row():
|
360 |
with gr.Column(scale=1):
|
361 |
-
|
362 |
-
|
363 |
-
audio_input = gr.Audio(type="filepath", visible=False) # Removed source="microphone"
|
364 |
-
|
365 |
-
# Add a big, prominent record button
|
366 |
-
record_btn = gr.Button(value=lambda: MESSAGES[selected_language.value]["rec_button"], elem_classes="big-button", variant="primary")
|
367 |
audio_loaded_msg = gr.Markdown("", elem_classes="large-text")
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
# Recognition results section (separate from chat)
|
372 |
with gr.Row():
|
373 |
recognition_results = gr.Markdown("")
|
374 |
-
|
375 |
-
# Artist info section
|
376 |
with gr.Row():
|
377 |
artist_info_text = gr.Markdown("")
|
378 |
-
artist_info_image = gr.Image(type="filepath")
|
|
|
|
|
|
|
379 |
|
380 |
-
gr.Markdown(value=lambda: MESSAGES[selected_language.value]["chat_title"])
|
381 |
-
chat_history = gr.Chatbot(label="Music Chat")
|
382 |
-
|
383 |
with gr.Row():
|
384 |
chat_input = gr.Textbox(
|
385 |
-
placeholder=
|
386 |
-
label="Your question"
|
387 |
-
|
388 |
-
chat_btn = gr.Button(value=lambda: MESSAGES[selected_language.value]["chat_button"], variant="primary")
|
389 |
-
|
390 |
-
# Function to handle audio recording/uploading
|
391 |
-
def toggle_audio_widget(language_name):
|
392 |
-
lang = LANGUAGES.get(language_name, "en")
|
393 |
-
# This function triggers the audio widget's recording functionality
|
394 |
-
return gr.update(visible=True), "loading", MESSAGES[lang]["loading"]
|
395 |
-
|
396 |
-
# Function to update status when audio is uploaded
|
397 |
-
def update_audio_status(audio_path, language_name):
|
398 |
-
lang = LANGUAGES.get(language_name, "en")
|
399 |
-
if audio_path:
|
400 |
-
return "ready", MESSAGES[lang]["audio_loaded"]
|
401 |
-
else:
|
402 |
-
return "no_audio", MESSAGES[lang]["no_audio"]
|
403 |
-
|
404 |
-
# Function to download image from URL and return a local filepath
|
405 |
-
def download_image(url):
|
406 |
-
if not url:
|
407 |
-
return None
|
408 |
-
try:
|
409 |
-
response = requests.get(url, stream=True)
|
410 |
-
response.raise_for_status()
|
411 |
-
# Create a temporary file
|
412 |
-
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
|
413 |
-
shutil.copyfileobj(response.raw, temp_file)
|
414 |
-
temp_file.close()
|
415 |
-
return temp_file.name
|
416 |
-
except Exception as e:
|
417 |
-
print(f"Error downloading image: {str(e)}")
|
418 |
-
return None
|
419 |
-
|
420 |
-
# Recognition function
|
421 |
-
def process_audio(audio_path, language_name, status):
|
422 |
-
lang = LANGUAGES.get(language_name, "en")
|
423 |
-
if not audio_path or status != "ready":
|
424 |
-
return None, "", "", MESSAGES[lang]["no_audio"], ""
|
425 |
-
|
426 |
-
yield None, "", "", MESSAGES[lang]["uploading"], ""
|
427 |
-
yield None, "", "", MESSAGES[lang]["searching"], ""
|
428 |
-
try:
|
429 |
-
result = recognize_song(audio_path)
|
430 |
-
|
431 |
-
if "error" in result:
|
432 |
-
return None, "", "", MESSAGES[lang]["error"].format(error=result['error']), ""
|
433 |
-
|
434 |
-
# Get artist information in selected language
|
435 |
-
artist_info = get_artist_info(result['Artist'], result['Song'], lang)
|
436 |
-
|
437 |
-
# Format recognition result based on language
|
438 |
-
if lang == "en":
|
439 |
-
recognition_msg = f"### 🎵 Recognized Song: {result['Song']}\n\n"
|
440 |
-
recognition_msg += f"**🎤 Artist:** {result['Artist']}\n"
|
441 |
-
recognition_msg += f"**📀 Album:** {result['Album']}\n\n"
|
442 |
-
|
443 |
-
if result['Spotify'] != "Not available":
|
444 |
-
recognition_msg += f"**🎧 [Listen on Spotify]({result['Spotify']})**\n"
|
445 |
-
if result['Apple Music'] != "Not available":
|
446 |
-
recognition_msg += f"**🍏 [Listen on Apple Music]({result['Apple Music']})**\n"
|
447 |
-
|
448 |
-
elif lang == "es":
|
449 |
-
recognition_msg = f"### 🎵 Canción Reconocida: {result['Song']}\n\n"
|
450 |
-
recognition_msg += f"**🎤 Artista:** {result['Artist']}\n"
|
451 |
-
recognition_msg += f"**📀 Álbum:** {result['Album']}\n\n"
|
452 |
-
|
453 |
-
if result['Spotify'] != "Not available":
|
454 |
-
recognition_msg += f"**🎧 [Escuchar en Spotify]({result['Spotify']})**\n"
|
455 |
-
if result['Apple Music'] != "Not available":
|
456 |
-
recognition_msg += f"**🍏 [Escuchar en Apple Music]({result['Apple Music']})**\n"
|
457 |
-
|
458 |
-
elif lang == "fr":
|
459 |
-
recognition_msg = f"### 🎵 Chanson Reconnue: {result['Song']}\n\n"
|
460 |
-
recognition_msg += f"**🎤 Artiste:** {result['Artist']}\n"
|
461 |
-
recognition_msg += f"**📀 Album:** {result['Album']}\n\n"
|
462 |
-
|
463 |
-
if result['Spotify'] != "Not available":
|
464 |
-
recognition_msg += f"**🎧 [Écouter sur Spotify]({result['Spotify']})**\n"
|
465 |
-
if result['Apple Music'] != "Not available":
|
466 |
-
recognition_msg += f"**🍏 [Écouter sur Apple Music]({result['Apple Music']})**\n"
|
467 |
-
|
468 |
-
# Artist info section title based on language
|
469 |
-
artist_facts_content = f"{MESSAGES[lang]['artist_info_title']}\n\n{artist_info['text']}"
|
470 |
-
|
471 |
-
# Download the image if URL exists
|
472 |
-
image_filepath = download_image(artist_info['image']) if artist_info['image'] else ""
|
473 |
-
|
474 |
-
# Update status message
|
475 |
-
status_msg = MESSAGES[lang]["recognized"]
|
476 |
-
|
477 |
-
return result, recognition_msg, artist_facts_content, status_msg, image_filepath
|
478 |
-
|
479 |
-
except Exception as e:
|
480 |
-
return None, "", "", MESSAGES[lang]["error"].format(error=str(e)), ""
|
481 |
-
|
482 |
-
# Chat function
|
483 |
-
def process_chat(query, language_name, song_info, artist_info):
|
484 |
-
lang = LANGUAGES.get(language_name, "en")
|
485 |
-
if not query:
|
486 |
-
return []
|
487 |
-
|
488 |
-
try:
|
489 |
-
# Get response
|
490 |
-
response = chat_with_assistant(query, artist_info, lang)
|
491 |
-
|
492 |
-
return [(query, response)]
|
493 |
-
except Exception as e:
|
494 |
-
return [(query, f"Sorry, I couldn't process your request: {str(e)}")]
|
495 |
-
|
496 |
-
def view_history_process(limit, language_name):
|
497 |
-
lang = LANGUAGES.get(language_name, "en")
|
498 |
-
return view_song_history(int(limit), lang)
|
499 |
-
|
500 |
-
def update_ui(language_name):
|
501 |
-
lang = LANGUAGES.get(language_name, "en")
|
502 |
-
return (
|
503 |
-
MESSAGES[lang]["title"],
|
504 |
-
MESSAGES[lang]["subtitle"],
|
505 |
-
gr.Markdown(value=MESSAGES[lang]["no_audio"]),
|
506 |
-
gr.Button(value=MESSAGES[lang]["rec_button"], elem_classes="big-button", variant="primary"),
|
507 |
-
gr.Button(value=MESSAGES[lang]["recognize_button"], variant="secondary"),
|
508 |
-
gr.Markdown(value=MESSAGES[lang]["chat_title"]),
|
509 |
-
gr.Textbox(placeholder=MESSAGES[lang]["chat_placeholder"], label="Your question"),
|
510 |
-
gr.Button(value=MESSAGES[lang]["chat_button"], variant="primary"),
|
511 |
-
gr.Slider(minimum=5, maximum=50, value=10, step=5, label=MESSAGES[lang]["history_limit_label"]),
|
512 |
-
gr.Button(value=MESSAGES[lang]["history_button"])
|
513 |
)
|
|
|
514 |
|
515 |
-
# Event handlers
|
516 |
-
language_dropdown.change(
|
517 |
-
fn=update_ui,
|
518 |
-
inputs=[language_dropdown],
|
519 |
-
outputs=[demo.get_component(0), demo.get_component(1), audio_status_msg, record_btn, recognize_btn, demo.get_component(8), chat_input, chat_btn, history_limit, view_history_btn]
|
520 |
-
)
|
521 |
-
|
522 |
-
record_btn.click(
|
523 |
-
fn=toggle_audio_widget,
|
524 |
-
inputs=[language_dropdown],
|
525 |
-
outputs=[audio_input, audio_status, audio_status_msg]
|
526 |
-
)
|
527 |
-
|
528 |
-
audio_input.change(
|
529 |
-
fn=update_audio_status,
|
530 |
-
inputs=[audio_input, language_dropdown],
|
531 |
-
outputs=[audio_status, audio_loaded_msg]
|
532 |
-
)
|
533 |
-
|
534 |
-
recognize_btn.click(
|
535 |
-
fn=process_audio,
|
536 |
-
inputs=[audio_input, language_dropdown, audio_status],
|
537 |
-
outputs=[song_info_state, recognition_results, artist_info_text, audio_status_msg, artist_info_image]
|
538 |
-
).then(
|
539 |
-
fn=lambda a: a,
|
540 |
-
inputs=[artist_info_text],
|
541 |
-
outputs=[artist_info_state]
|
542 |
-
)
|
543 |
-
|
544 |
-
chat_btn.click(
|
545 |
-
fn=process_chat,
|
546 |
-
inputs=[chat_input, language_dropdown, song_info_state, artist_info_state],
|
547 |
-
outputs=[chat_history]
|
548 |
-
).then(
|
549 |
-
fn=lambda: "", # Clear input after sending
|
550 |
-
inputs=[],
|
551 |
-
outputs=[chat_input]
|
552 |
-
)
|
553 |
-
|
554 |
with gr.Tab("Song History"):
|
555 |
-
gr.Markdown(value=
|
556 |
-
|
557 |
with gr.Row():
|
558 |
-
history_limit = gr.Slider(minimum=5, maximum=50, value=10, step=5, label=
|
559 |
-
view_history_btn = gr.Button(value=
|
560 |
-
|
561 |
history_output = gr.Markdown()
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
567 |
)
|
568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
569 |
demo.launch()
|
|
|
9 |
import tempfile
|
10 |
import shutil
|
11 |
|
12 |
+
# Constantes
|
13 |
HISTORY_FILE = "song_history.json"
|
14 |
|
|
|
15 |
LANGUAGES = {
|
16 |
"English": "en",
|
17 |
"Español": "es",
|
18 |
"Français": "fr"
|
19 |
}
|
20 |
|
|
|
21 |
MESSAGES = {
|
22 |
"en": {
|
23 |
"title": "# 🎵 Music Recognition & Fun Facts",
|
|
|
93 |
}
|
94 |
}
|
95 |
|
96 |
+
# Herramientas
|
97 |
@tool
|
98 |
def recognize_song(audio_path: str) -> dict:
|
|
|
|
|
|
|
|
|
99 |
AUDD_API_TOKEN = os.getenv("AUDD_API_TOKEN")
|
|
|
100 |
if not os.path.exists(audio_path):
|
101 |
return {"error": "The audio file does not exist"}
|
|
|
102 |
try:
|
103 |
with open(audio_path, 'rb') as file:
|
104 |
+
data = {'api_token': AUDD_API_TOKEN, 'return': 'spotify,apple_music'}
|
105 |
+
files = {'file': file}
|
|
|
|
|
|
|
|
|
|
|
106 |
response = requests.post('https://api.audd.io/', data=data, files=files)
|
|
|
107 |
if response.status_code != 200:
|
108 |
return {"error": f"API Error: {response.status_code}"}
|
|
|
109 |
result = response.json()
|
|
|
110 |
if result['status'] == 'error':
|
111 |
return {"error": result['error']['error_message']}
|
|
|
112 |
if not result.get('result'):
|
113 |
return {"error": "Could not recognize the song"}
|
|
|
114 |
song_info = result['result']
|
|
|
|
|
115 |
song_data = {
|
116 |
"Song": song_info.get('title', 'Unknown'),
|
117 |
"Artist": song_info.get('artist', 'Unknown'),
|
|
|
120 |
"Apple Music": song_info.get('apple_music', {}).get('url', 'Not available'),
|
121 |
"Recognition Date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
122 |
}
|
|
|
|
|
123 |
save_to_history(song_data)
|
|
|
124 |
return song_data
|
|
|
125 |
except Exception as e:
|
126 |
return {"error": f"Error processing audio: {str(e)}"}
|
127 |
|
|
|
128 |
def save_to_history(song_data):
|
|
|
129 |
try:
|
|
|
130 |
if os.path.exists(HISTORY_FILE):
|
131 |
with open(HISTORY_FILE, 'r') as f:
|
132 |
+
history = json.load(f) if f.read().strip() else []
|
|
|
|
|
|
|
133 |
else:
|
134 |
history = []
|
|
|
|
|
135 |
history.insert(0, song_data)
|
|
|
|
|
136 |
if len(history) > 50:
|
137 |
history = history[:50]
|
|
|
|
|
138 |
with open(HISTORY_FILE, 'w') as f:
|
139 |
json.dump(history, f, indent=2)
|
|
|
140 |
except Exception as e:
|
141 |
print(f"Error saving to history: {str(e)}")
|
142 |
|
|
|
143 |
@tool
|
144 |
def view_song_history(limit: int = 10, language: str = "en") -> str:
|
|
|
|
|
|
|
|
|
|
|
145 |
lang = LANGUAGES.get(language, "en")
|
146 |
try:
|
147 |
if not os.path.exists(HISTORY_FILE):
|
148 |
return f"📋 {MESSAGES[lang]['no_history']}"
|
|
|
149 |
with open(HISTORY_FILE, 'r') as f:
|
150 |
+
history = json.load(f)
|
|
|
|
|
|
|
|
|
151 |
if not history:
|
152 |
return f"📋 {MESSAGES[lang]['empty_history']}"
|
|
|
|
|
153 |
history = history[:min(limit, len(history))]
|
|
|
|
|
154 |
result = f"📋 **{MESSAGES[lang]['history_title']}:**\n\n"
|
|
|
155 |
for i, song in enumerate(history, 1):
|
156 |
result += f"{i}. **{song.get('Song', 'Unknown')}** - *{song.get('Artist', 'Unknown')}*\n"
|
157 |
result += f" 📀 Album: {song.get('Album', 'Unknown')}\n"
|
|
|
158 |
if song.get('Spotify', 'Not available') != 'Not available':
|
159 |
result += f" 🎧 [Spotify]({song.get('Spotify')})\n"
|
|
|
160 |
if song.get('Apple Music', 'Not available') != 'Not available':
|
161 |
result += f" 🍏 [Apple Music]({song.get('Apple Music')})\n"
|
|
|
162 |
result += f" 🕒 Recognized on: {song.get('Recognition Date', 'Unknown date')}\n\n"
|
|
|
163 |
return result
|
|
|
164 |
except Exception as e:
|
165 |
return f"❌ {MESSAGES[lang]['history_error'].format(error=str(e))}"
|
166 |
|
|
|
167 |
@tool
|
168 |
def get_artist_info(artist_name: str, song_title: str, language: str = "en") -> dict:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
prompts = {
|
170 |
"en": f"Provide interesting and fun facts about the music artist '{artist_name}' that fans would love to know. Include stories about their career, personal anecdotes, how their song '{song_title}' was created if known, or surprising facts about their life. Include an image URL if possible. Focus on highlighting their contributions to music and anything that makes them unique as an artist. Keep it engaging and positive.",
|
171 |
"es": f"Proporciona datos interesantes y curiosos sobre el artista musical '{artist_name}' que a los fans les encantaría conocer. Incluye historias sobre su carrera, anécdotas personales, cómo se creó su canción '{song_title}' si se sabe, o datos sorprendentes sobre su vida. Incluye una URL de imagen si es posible. Céntrate en destacar sus contribuciones a la música y cualquier cosa que les haga únicos como artistas. Mantenlo atractivo y positivo.",
|
172 |
"fr": f"Fournissez des faits intéressants et amusants sur l'artiste musical '{artist_name}' que les fans aimeraient connaître. Incluez des histoires sur leur carrière, des anecdotes personnelles, comment leur chanson '{song_title}' a été créée si connu, ou des faits surprenants sur leur vie. Incluez une URL d'image si possible. Concentrez-vous sur la mise en valeur de leurs contributions à la musique et tout ce qui les rend uniques en tant qu'artiste. Gardez-le engageant et positif.",
|
173 |
}
|
174 |
+
language = language if language in prompts else "en"
|
175 |
+
messages = [{"role": "user", "content": prompts[language]}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
176 |
try:
|
|
|
177 |
response = model(messages)
|
178 |
content = response.content
|
179 |
image_url = None
|
|
|
186 |
except Exception as e:
|
187 |
return {"text": f"Could not retrieve information about {artist_name}: {str(e)}", "image": None}
|
188 |
|
|
|
189 |
@tool
|
190 |
def chat_with_assistant(query: str, artist_info: str = "", language: str = "en") -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
system_messages = {
|
192 |
"en": "You are a music expert assistant who specializes in providing engaging information about artists, songs, and music history. Focus on sharing interesting, fun facts that highlight the artist's unique contributions and stories.",
|
193 |
"es": "Eres un asistente experto en música que se especializa en proporcionar información interesante sobre artistas, canciones e historia musical. Céntrate en compartir datos curiosos e interesantes que destaquen las contribuciones únicas y las historias del artista.",
|
194 |
"fr": "Vous êtes un assistant expert en musique qui se spécialise dans la fourniture d'informations intéressantes sur les artistes, les chansons et l'histoire de la musique. Concentrez-vous sur le partage de faits intéressants et amusants qui mettent en évidence les contributions uniques et les histoires de l'artiste.",
|
195 |
}
|
196 |
+
language = language if language in system_messages else "en"
|
197 |
+
context = f"Previous context about the artist: {artist_info}\n\n" if artist_info else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
messages = [
|
199 |
{"role": "system", "content": system_messages[language]},
|
200 |
{"role": "user", "content": context + query}
|
201 |
]
|
|
|
202 |
try:
|
|
|
203 |
response = model(messages)
|
204 |
return response.content
|
205 |
except Exception as e:
|
206 |
return f"Sorry, I couldn't process your request: {str(e)}"
|
207 |
|
208 |
+
# Configuración del agente
|
209 |
final_answer = FinalAnswerTool()
|
210 |
model = HfApiModel(
|
211 |
max_tokens=2096,
|
|
|
217 |
with open("prompts.yaml", 'r') as stream:
|
218 |
prompt_templates = yaml.safe_load(stream)
|
219 |
|
|
|
220 |
agent = CodeAgent(
|
221 |
model=model,
|
222 |
+
tools=[final_answer, recognize_song, view_song_history, get_artist_info, chat_with_assistant],
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
max_steps=8,
|
224 |
verbosity_level=1,
|
225 |
grammar=None,
|
|
|
229 |
prompt_templates=prompt_templates
|
230 |
)
|
231 |
|
232 |
+
# Descargar imagen (función auxiliar)
|
233 |
+
def download_image(image_url):
|
234 |
+
if not image_url:
|
235 |
+
return ""
|
236 |
+
try:
|
237 |
+
response = requests.get(image_url, stream=True)
|
238 |
+
if response.status_code == 200:
|
239 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.jpg')
|
240 |
+
shutil.copyfileobj(response.raw, temp_file)
|
241 |
+
temp_file.close()
|
242 |
+
return temp_file.name
|
243 |
+
return ""
|
244 |
+
except Exception:
|
245 |
+
return ""
|
246 |
+
|
247 |
+
# Interfaz de usuario con Gradio
|
248 |
with gr.Blocks(title="Music Recognition & Fun Facts", css=".large-text {font-size: 1.5em !important;} .big-button {font-size: 1.2em; padding: 15px 30px;}") as demo:
|
|
|
249 |
selected_language = gr.State("en")
|
250 |
song_info_state = gr.State(None)
|
251 |
artist_info_state = gr.State("")
|
252 |
+
audio_status = gr.State("no_audio")
|
253 |
+
|
254 |
+
title = gr.Markdown(value=MESSAGES["en"]["title"], elem_id="title")
|
255 |
+
subtitle = gr.Markdown(value=MESSAGES["en"]["subtitle"], elem_id="subtitle")
|
256 |
+
|
|
|
|
|
257 |
with gr.Row():
|
258 |
language_dropdown = gr.Dropdown(
|
259 |
choices=[("English", "en"), ("Español", "es"), ("Français", "fr")],
|
260 |
value="en",
|
261 |
+
label=MESSAGES["en"]["language_label"]
|
262 |
)
|
263 |
+
|
|
|
264 |
with gr.Tab("Song Recognition & Chat"):
|
265 |
+
audio_status_msg = gr.Markdown(value=MESSAGES["en"]["no_audio"], elem_id="audio_status_msg")
|
|
|
|
|
|
|
266 |
with gr.Row():
|
267 |
with gr.Column(scale=1):
|
268 |
+
audio_input = gr.Audio(type="filepath", visible=False)
|
269 |
+
record_btn = gr.Button(value=MESSAGES["en"]["rec_button"], elem_classes="big-button", variant="primary", elem_id="record_btn")
|
|
|
|
|
|
|
|
|
270 |
audio_loaded_msg = gr.Markdown("", elem_classes="large-text")
|
271 |
+
recognize_btn = gr.Button(value=MESSAGES["en"]["recognize_button"], variant="secondary", elem_id="recognize_btn")
|
272 |
+
|
|
|
|
|
273 |
with gr.Row():
|
274 |
recognition_results = gr.Markdown("")
|
275 |
+
|
|
|
276 |
with gr.Row():
|
277 |
artist_info_text = gr.Markdown("")
|
278 |
+
artist_info_image = gr.Image(type="filepath")
|
279 |
+
|
280 |
+
chat_title = gr.Markdown(value=MESSAGES["en"]["chat_title"], elem_id="chat_title")
|
281 |
+
chat_history = gr.Chatbot(label="Music Chat", type="messages")
|
282 |
|
|
|
|
|
|
|
283 |
with gr.Row():
|
284 |
chat_input = gr.Textbox(
|
285 |
+
placeholder=MESSAGES["en"]["chat_placeholder"],
|
286 |
+
label="Your question",
|
287 |
+
elem_id="chat_input"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
288 |
)
|
289 |
+
chat_btn = gr.Button(value=MESSAGES["en"]["chat_button"], variant="primary", elem_id="chat_btn")
|
290 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
291 |
with gr.Tab("Song History"):
|
292 |
+
gr.Markdown(value=MESSAGES["en"]["history_title"])
|
|
|
293 |
with gr.Row():
|
294 |
+
history_limit = gr.Slider(minimum=5, maximum=50, value=10, step=5, label=MESSAGES["en"]["history_limit_label"], elem_id="history_limit")
|
295 |
+
view_history_btn = gr.Button(value=MESSAGES["en"]["history_button"], elem_id="view_history_btn")
|
|
|
296 |
history_output = gr.Markdown()
|
297 |
+
|
298 |
+
def update_ui(language_name):
|
299 |
+
lang = LANGUAGES.get(language_name, "en")
|
300 |
+
return (
|
301 |
+
MESSAGES[lang]["title"],
|
302 |
+
MESSAGES[lang]["subtitle"],
|
303 |
+
MESSAGES[lang]["no_audio"],
|
304 |
+
MESSAGES[lang]["rec_button"],
|
305 |
+
MESSAGES[lang]["recognize_button"],
|
306 |
+
MESSAGES[lang]["chat_title"],
|
307 |
+
MESSAGES[lang]["chat_placeholder"],
|
308 |
+
MESSAGES[lang]["chat_button"],
|
309 |
+
MESSAGES[lang]["history_limit_label"],
|
310 |
+
MESSAGES[lang]["history_button"]
|
311 |
)
|
312 |
|
313 |
+
language_dropdown.change(
|
314 |
+
fn=update_ui,
|
315 |
+
inputs=[language_dropdown],
|
316 |
+
outputs=[title, subtitle, audio_status_msg, record_btn, recognize_btn, chat_title, chat_input, chat_btn, history_limit, view_history_btn]
|
317 |
+
)
|
318 |
+
|
319 |
+
def toggle_audio_widget(language_name):
|
320 |
+
lang = LANGUAGES.get(language_name, "en")
|
321 |
+
return gr.update(visible=True), "loading", MESSAGES[lang]["loading"]
|
322 |
+
|
323 |
+
record_btn.click(
|
324 |
+
fn=toggle_audio_widget,
|
325 |
+
inputs=[language_dropdown],
|
326 |
+
outputs=[audio_input, audio_status, audio_status_msg]
|
327 |
+
)
|
328 |
+
|
329 |
+
def update_audio_status(audio_path, language_name):
|
330 |
+
lang = LANGUAGES.get(language_name, "en")
|
331 |
+
if audio_path:
|
332 |
+
return "ready", MESSAGES[lang]["audio_loaded"]
|
333 |
+
else:
|
334 |
+
return "no_audio", MESSAGES[lang]["no_audio"]
|
335 |
+
|
336 |
+
audio_input.change(
|
337 |
+
fn=update_audio_status,
|
338 |
+
inputs=[audio_input, language_dropdown],
|
339 |
+
outputs=[audio_status, audio_loaded_msg]
|
340 |
+
)
|
341 |
+
|
342 |
+
def process_audio(audio_path, language_name, status):
|
343 |
+
lang = LANGUAGES.get(language_name, "en")
|
344 |
+
if not audio_path or status != "ready":
|
345 |
+
return None, "", "", MESSAGES[lang]["no_audio"], ""
|
346 |
+
yield None, "", "", MESSAGES[lang]["uploading"], ""
|
347 |
+
yield None, "", "", MESSAGES[lang]["searching"], ""
|
348 |
+
try:
|
349 |
+
result = recognize_song(audio_path)
|
350 |
+
if "error" in result:
|
351 |
+
return None, "", "", MESSAGES[lang]["error"].format(error=result['error']), ""
|
352 |
+
artist_info = get_artist_info(result['Artist'], result['Song'], lang)
|
353 |
+
recognition_msg = f"### 🎵 Recognized Song: {result['Song']}\n\n**🎤 Artist:** {result['Artist']}\n**📀 Album:** {result['Album']}\n\n"
|
354 |
+
if result['Spotify'] != "Not available":
|
355 |
+
recognition_msg += f"**🎧 [Listen on Spotify]({result['Spotify']})**\n"
|
356 |
+
if result['Apple Music'] != "Not available":
|
357 |
+
recognition_msg += f"**🍏 [Listen on Apple Music]({result['Apple Music']})**\n"
|
358 |
+
artist_facts_content = f"{MESSAGES[lang]['artist_info_title']}\n\n{artist_info['text']}"
|
359 |
+
image_filepath = download_image(artist_info['image']) if artist_info['image'] else ""
|
360 |
+
status_msg = MESSAGES[lang]["recognized"]
|
361 |
+
return result, recognition_msg, artist_facts_content, status_msg, image_filepath
|
362 |
+
except Exception as e:
|
363 |
+
return None, "", "", MESSAGES[lang]["error"].format(error=str(e)), ""
|
364 |
+
|
365 |
+
recognize_btn.click(
|
366 |
+
fn=process_audio,
|
367 |
+
inputs=[audio_input, language_dropdown, audio_status],
|
368 |
+
outputs=[song_info_state, recognition_results, artist_info_text, audio_status_msg, artist_info_image]
|
369 |
+
).then(
|
370 |
+
fn=lambda a: a,
|
371 |
+
inputs=[artist_info_text],
|
372 |
+
outputs=[artist_info_state]
|
373 |
+
)
|
374 |
+
|
375 |
+
def process_chat(query, language_name, song_info, artist_info):
|
376 |
+
lang = LANGUAGES.get(language_name, "en")
|
377 |
+
if not query:
|
378 |
+
return []
|
379 |
+
try:
|
380 |
+
response = chat_with_assistant(query, artist_info, lang)
|
381 |
+
return [(query, response)]
|
382 |
+
except Exception as e:
|
383 |
+
return [(query, f"Sorry, I couldn't process your request: {str(e)}")]
|
384 |
+
|
385 |
+
chat_btn.click(
|
386 |
+
fn=process_chat,
|
387 |
+
inputs=[chat_input, language_dropdown, song_info_state, artist_info_state],
|
388 |
+
outputs=[chat_history]
|
389 |
+
).then(
|
390 |
+
fn=lambda: "",
|
391 |
+
inputs=[],
|
392 |
+
outputs=[chat_input]
|
393 |
+
)
|
394 |
+
|
395 |
+
def view_history_process(limit, language_name):
|
396 |
+
lang = LANGUAGES.get(language_name, "en")
|
397 |
+
return view_song_history(int(limit), lang)
|
398 |
+
|
399 |
+
view_history_btn.click(
|
400 |
+
fn=view_history_process,
|
401 |
+
inputs=[history_limit, language_dropdown],
|
402 |
+
outputs=[history_output]
|
403 |
+
)
|
404 |
+
|
405 |
demo.launch()
|