from smolagents import CodeAgent, HfApiModel, tool import datetime import requests import yaml import os import json import gradio as gr from tools.final_answer import FinalAnswerTool import logging # Configurar logging para depuración logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger('music_recognition_app') # File to save the history of recognized songs HISTORY_FILE = "song_history.json" # Supported languages LANGUAGES = { "English": "en", "Español": "es", "Français": "fr" } # UI messages for different languages UI_MESSAGES = { "en": { "title": "Music Recognition & Fun Facts", "subtitle": "Identify songs, learn interesting facts about artists, and chat about music", "choose_language": "Choose your language", "rec_button": " REC MUSIC", "please_record": "Please record or upload an audio clip", "recording": "Recording... Click Stop when done", "audio_loaded": " Audio loaded! Processing song recognition...", "loading_audio": "Please wait while the audio is loading", "searching_song": "Searching for your song in the sound multiverse, please wait to discover incredible facts about your artist.", "song_recognized": "Song successfully recognized! Ask questions about the artist in the chat below", "about_artist": "About", "ask_more": "Ask me more about this artist or music in general", "chat_placeholder": "Ask about the artist, song, or anything music related...", "send_button": "Send", "history_tab": "Song History", "history_title": "History of recognized songs", "songs_to_show": "Number of songs to show", "view_history": " View History", "recognized_song": "Recognized Song", "artist": "Artist", "album": "Album", "listen_spotify": "Listen on Spotify", "listen_apple": "Listen on Apple Music", "no_audio_error": "Please record an audio clip first", "processing_error": "Error processing audio" }, "es": { "title": "Reconocimiento de Música y Datos Curiosos", "subtitle": "Identifica canciones, aprende datos interesantes sobre artistas y chatea sobre música", "choose_language": "Elige tu idioma", "rec_button": " GRABAR MÚSICA", "please_record": "Por favor graba o sube un clip de audio", "recording": "Grabando... Haz clic en Detener cuando termines", "audio_loaded": " ¡Audio cargado! Procesando el reconocimiento de la canción...", "loading_audio": "Por favor espera mientras se carga el audio", "searching_song": "Buscando tu canción en el multiverso sonoro, espera para descubrir datos increíbles sobre tu artista.", "song_recognized": "¡Canción reconocida con éxito! Haz preguntas sobre el artista en el chat de abajo", "about_artist": "Sobre", "ask_more": "Pregúntame más sobre este artista o música en general", "chat_placeholder": "Pregunta sobre el artista, canción o cualquier tema relacionado con la música...", "send_button": "Enviar", "history_tab": "Historial de Canciones", "history_title": "Historial de canciones reconocidas", "songs_to_show": "Número de canciones para mostrar", "view_history": " Ver Historial", "recognized_song": "Canción Reconocida", "artist": "Artista", "album": "Álbum", "listen_spotify": "Escuchar en Spotify", "listen_apple": "Escuchar en Apple Music", "no_audio_error": "Por favor graba un clip de audio primero", "processing_error": "Error al procesar el audio" }, "fr": { "title": "Reconnaissance de Musique et Anecdotes", "subtitle": "Identifiez des chansons, découvrez des faits intéressants sur les artistes et discutez de musique", "choose_language": "Choisissez votre langue", "rec_button": " ENREGISTRER MUSIQUE", "please_record": "Veuillez enregistrer ou télécharger un clip audio", "recording": "Enregistrement... Cliquez sur Arrêter quand vous avez terminé", "audio_loaded": " Audio chargé ! Traitement de la reconnaissance de la chanson...", "loading_audio": "Veuillez patienter pendant le chargement de l'audio", "searching_song": "Recherche de votre chanson dans le multivers sonore, patientez pour découvrir des faits incroyables sur votre artiste.", "song_recognized": "Chanson reconnue avec succès ! Posez des questions sur l'artiste dans le chat ci-dessous", "about_artist": "À propos de", "ask_more": "Demandez-moi plus d'informations sur cet artiste ou sur la musique en général", "chat_placeholder": "Posez une question sur l'artiste, la chanson ou tout ce qui concerne la musique...", "send_button": "Envoyer", "history_tab": "Historique des Chansons", "history_title": "Historique des chansons reconnues", "songs_to_show": "Nombre de chansons à afficher", "view_history": " Voir l'historique", "recognized_song": "Chanson Reconnue", "artist": "Artiste", "album": "Album", "listen_spotify": "Écouter sur Spotify", "listen_apple": "Écouter sur Apple Music", "no_audio_error": "Veuillez d'abord enregistrer un clip audio", "processing_error": "Erreur lors du traitement de l'audio" } } # Tool to recognize songs using AudD @tool def recognize_song(audio_path: str) -> dict: """Recognizes a song from an audio file Args: audio_path: path to the audio file to recognize """ AUDD_API_TOKEN = os.getenv("AUDD_API_TOKEN") if not os.path.exists(audio_path): return {"error": "The audio file does not exist"} try: with open(audio_path, 'rb') as file: data = { 'api_token': AUDD_API_TOKEN, 'return': 'spotify,apple_music' } files = {'file': file} response = requests.post('https://api.audd.io/', data=data, files=files) if response.status_code != 200: return {"error": f"API Error: {response.status_code}"} result = response.json() if result['status'] == 'error': return {"error": result['error']['error_message']} if not result.get('result'): return {"error": "Could not recognize the song"} song_info = result['result'] song_data = { "Song": song_info.get('title', 'Unknown'), "Artist": song_info.get('artist', 'Unknown'), "Album": song_info.get('album', 'Unknown'), "Spotify": song_info.get('spotify', {}).get('external_urls', {}).get('spotify', 'Not available'), "Apple Music": song_info.get('apple_music', {}).get('url', 'Not available'), "Recognition Date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") } save_to_history(song_data) return song_data except Exception as e: return {"error": f"Error processing audio: {str(e)}"} # Function to save song to history def save_to_history(song_data): """Saves a song to the history""" try: if os.path.exists(HISTORY_FILE): with open(HISTORY_FILE, 'r') as f: try: history = json.load(f) except json.JSONDecodeError: history = [] else: history = [] history.insert(0, song_data) if len(history) > 50: history = history[:50] with open(HISTORY_FILE, 'w') as f: json.dump(history, f, indent=2) except Exception as e: print(f"Error saving to history: {str(e)}") # Tool to view song history @tool def view_song_history(limit: int = 10) -> str: """Shows the history of recognized songs Args: limit: maximum number of songs to display (default 10) """ try: if not os.path.exists(HISTORY_FILE): return " No song recognition history yet." with open(HISTORY_FILE, 'r') as f: try: history = json.load(f) except json.JSONDecodeError: return "📋 Error loading song history." if not history: return "📋 Song history is empty." history = history[:min(limit, len(history))] result = "📋 **Song Recognition History:**\n\n" for i, song in enumerate(history, 1): result += f"{i}. **{song.get('Song', 'Unknown')}** - *{song.get('Artist', 'Unknown')}*\n" result += f" 📀 Album: {song.get('Album', 'Unknown')}\n" if song.get('Spotify', 'Not available') != 'Not available': result += f" 🎧 [Spotify]({song.get('Spotify')})\n" if song.get('Apple Music', 'Not available') != 'Not available': result += f" 🍏 [Apple Music]({song.get('Apple Music')})\n" result += f" 🕒 Recognized on: {song.get('Recognition Date', 'Unknown date')}\n\n" return result except Exception as e: return f"❌ Error getting history: {str(e)}" # Tool to get artist information in selected language with enhanced details @tool def get_artist_info(artist_name: str, song_name: str = "", language: str = "en") -> str: """Gets detailed background information, fun facts about a music artist and the specific song in the specified language Args: artist_name: name of the artist to get information about song_name: name of the song (if available) language: language code (en, es, fr) """ prompts = { "en": f"Provide a comprehensive overview about the music artist '{artist_name}'. Include the following sections clearly marked with headers:\n\n1. General Biography: Brief overview of their career and significance in music.\n\n2. Fun Facts: 4-5 surprising or interesting facts about the artist or group that fans would find fascinating.\n\n3. Behind-the-scenes Anecdotes: 2-3 memorable stories about the artist's career, performances, or personal life that showcase their character or impact.\n\n" + (f'4. About the Song "{song_name}": If you know anything specific about how this song was created, its meaning, its impact, or interesting trivia related to it, please share those details.' if song_name else '') + "\n\nKeep the tone engaging and enthusiastic, highlighting what makes this artist unique and beloved by fans. Limit your response to around 500 words total.", "es": f"Proporciona una visión completa sobre el artista musical '{artist_name}'. Incluye las siguientes secciones claramente marcadas con encabezados:\n\n1. Biografía General: Breve resumen de su carrera y significado en la música.\n\n2. Datos Curiosos: 4-5 hechos sorprendentes o interesantes sobre el artista o grupo que los fans encontrarían fascinantes.\n\n3. Anécdotas entre bastidores: 2-3 historias memorables sobre la carrera del artista, actuaciones o vida personal que muestren su carácter o impacto.\n\n" + (f'4. Sobre la canción "{song_name}": Si conoces algo específico sobre cómo se creó esta canción, su significado, su impacto o curiosidades interesantes relacionadas con ella, por favor comparte esos detalles.' if song_name else '') + "\n\nMantén un tono atractivo y entusiasta, destacando lo que hace que este artista sea único y querido por los fans. Limita tu respuesta a alrededor de 500 palabras en total.", "fr": f"Fournissez un aperçu complet de l'artiste musical '{artist_name}'. Incluez les sections suivantes clairement marquées avec des en-têtes:\n\n1. Biographie générale: Bref aperçu de sa carrière et de son importance dans la musique.\n\n2. Anecdotes intéressantes: 4-5 faits surprenants ou intéressants sur l'artiste ou le groupe que les fans trouveraient fascinants.\n\n3. Anecdotes en coulisses: 2-3 histoires mémorables sur la carrière de l'artiste, ses performances ou sa vie personnelle qui mettent en valeur son caractère ou son impact.\n\n" + (f'4. À propos de la chanson "{song_name}": Si vous connaissez quelque chose de spécifique sur la façon dont cette chanson a été créée, sa signification, son impact ou des anecdotes intéressantes liées à elle, veuillez partager ces détails.' if song_name else '') + "\n\nGardez un ton engageant et enthousiaste, mettant en évidence ce qui rend cet artiste unique et apprécié des fans. Limitez votre réponse à environ 500 mots au total." } if language not in prompts: language = "en" messages = [{"role": "user", "content": prompts[language]}] try: response = model(messages) return response.content except Exception as e: return f"Could not retrieve information about {artist_name}: {str(e)}" # Chat with LLM tool @tool def chat_with_assistant(query: str, artist_info: str = "", language: str = "en") -> str: """Chat with the AI assistant about any music related topic in the specified language Args: query: user's question or request artist_info: previous artist info to provide context language: language code (en, es, fr) """ system_messages = { "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.", "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.", "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." } if language not in system_messages: language = "en" context = f"Previous context about the artist: {artist_info}\n\n" if artist_info else "" messages = [ {"role": "system", "content": system_messages[language]}, {"role": "user", "content": context + query} ] try: response = model(messages) return response.content except Exception as e: return f"Sorry, I couldn't process your request: {str(e)}" # Agent configuration final_answer = FinalAnswerTool() model = HfApiModel( max_tokens=2096, temperature=0.5, model_id='Qwen/Qwen2.5-Coder-32B-Instruct', custom_role_conversions=None, ) with open("prompts.yaml", 'r') as stream: prompt_templates = yaml.safe_load(stream) agent = CodeAgent( model=model, tools=[final_answer, recognize_song, view_song_history, get_artist_info, chat_with_assistant], max_steps=8, verbosity_level=1, grammar=None, planning_interval=None, name=None, description=None, prompt_templates=prompt_templates ) # Gradio user interface con CSS personalizado para mejor experiencia móvil with gr.Blocks(title="Music Recognition & Fun Facts", css=""" .large-button {min-height: 60px !important; font-size: 18px !important; padding: 15px !important;} @media (max-width: 600px) { .large-button {width: 100% !important; margin-bottom: 10px !important;} #audio-input {width: 100% !important; margin-top: 10px !important; margin-bottom: 10px !important;} button.record, button.stop {width: 60px !important; height: 60px !important; border-radius: 50% !important;} .error-text {color: red !important; font-weight: bold !important; font-size: 16px !important;} } #mic-status {margin-top: 5px; margin-bottom: 15px; font-style: italic;} """) as demo: # State variables selected_language = gr.State("en") lang_code = gr.State("en") song_info_state = gr.State(None) artist_info_state = gr.State("") song_name_state = gr.State("") audio_status = gr.State("no_audio") # Function to get UI messages based on language def get_ui_message(message_key, language_code="en"): return UI_MESSAGES.get(language_code, UI_MESSAGES["en"]).get(message_key, UI_MESSAGES["en"][message_key]) # Initial UI language is English current_lang = "en" # Title and intro title_component = gr.Markdown(f"# 🎵 {get_ui_message('title', current_lang)}") subtitle_component = gr.Markdown(get_ui_message('subtitle', current_lang)) # Language selection with gr.Row(): language_dropdown = gr.Dropdown( choices=list(LANGUAGES.keys()), value="English", label=get_ui_message('choose_language', current_lang) ) # Combined recognition and chat interface with gr.Tab("Song Recognition & Chat"): audio_status_msg = gr.Markdown(f"*{get_ui_message('please_record', current_lang)}*") with gr.Row(): with gr.Column(scale=1): audio_input = gr.Audio( label="Audio Recording", type="filepath", visible=False, sources=["microphone", "upload"], streaming=False, elem_id="audio-input", format="mp3", autoplay=False, show_download_button=False, interactive=True ) record_btn = gr.Button( get_ui_message('rec_button', current_lang), size="lg", variant="primary", scale=2, elem_id="record-button", elem_classes="large-button" ) microphone_status = gr.Markdown("Please click 'REC MUSIC' to start recording.", elem_id="mic-status") # Song title display (nuevo componente para mostrar el título inmediatamente) song_title_display = gr.Markdown("") # Processing indicator with gr.Row(): processing_indicator = gr.Markdown("") # Recognition results section with gr.Row(): recognition_results = gr.Markdown("") # Artist info section with gr.Row(): artist_facts = gr.Markdown("") # Chat section with gr.Row(): chat_section_title = gr.Markdown(f"### {get_ui_message('ask_more', current_lang)}") chat_history = gr.Chatbot(label="Music Chat", type="messages") with gr.Row(): chat_input = gr.Textbox( placeholder=get_ui_message('chat_placeholder', current_lang), label="Your question" ) chat_btn = gr.Button( get_ui_message('send_button', current_lang), variant="primary" ) # Function to handle language changes def update_ui_language(language_name): lang_code = LANGUAGES.get(language_name, "en") return ( f"# 🎵 {get_ui_message('title', lang_code)}", get_ui_message('subtitle', lang_code), gr.update(label=get_ui_message('choose_language', lang_code)), gr.update(value=get_ui_message('rec_button', lang_code)), f"*{get_ui_message('please_record', lang_code)}*", gr.update(value=get_ui_message('send_button', lang_code)), f"### {get_ui_message('ask_more', lang_code)}", gr.update(placeholder=get_ui_message('chat_placeholder', lang_code)), lang_code ) # Function to handle audio recording def toggle_audio_widget(lang_code): mobile_guidance = { "en": "Recording... Click Stop when done\n\nIf you're having trouble with the microphone:\n- Ensure your browser has microphone permission\n- Use a secure connection (HTTPS)\n- Check if another app is using the microphone\n- Refresh the page and allow microphone access\n- On mobile, use Chrome or Safari", "es": "Grabando... Haz clic en Detener cuando termines\n\nSi tienes problemas con el micrófono:\n- Asegúrate de que tu navegador tenga permiso para el micrófono\n- Usa una conexión segura (HTTPS)\n- Verifica si otra app está usando el micrófono\n- Refresca la página y permite el acceso\n- En móviles, usa Chrome o Safari", "fr": "Enregistrement... Cliquez sur Arrêter quand vous avez terminé\n\nSi vous avez des problèmes avec le microphone:\n- Assurez-vous que votre navigateur a l'autorisation\n- Utilisez une connexion sécurisée (HTTPS)\n- Vérifiez si une autre application utilise le micro\n- Rafraîchissez la page et autorisez l'accès\n- Sur mobile, utilisez Chrome ou Safari" } return ( gr.update(visible=True), "loading", mobile_guidance.get(lang_code, mobile_guidance["en"]), "" # Clear processing indicator ) # Function to update status and trigger recognition def update_audio_status(audio_path, lang_code): if audio_path: return ( "ready", f"{get_ui_message('audio_loaded', lang_code)}", "" # Clear processing indicator ) else: troubleshooting = { "en": "No audio recorded. Please try again.\n\nMicrophone troubleshooting:\n- Check browser microphone permissions\n- Use Chrome or Safari\n- Ensure no other app is using the microphone", "es": "No se grabó audio. Intenta de nuevo.\n\nSolución de problemas:\n- Verifica los permisos del micrófono en el navegador\n- Usa Chrome o Safari\n- Asegúrate de que ninguna otra app use el micrófono", "fr": "Aucun audio enregistré. Réessayez.\n\nDépannage:\n- Vérifiez les autorisations du microphone\n- Utilisez Chrome ou Safari\n- Assurez-vous qu'aucune autre app n'utilise le micro" } return ( "no_audio", troubleshooting.get(lang_code, troubleshooting["en"]), "" # Clear processing indicator ) # Recognition function with immediate song title display def process_audio(audio_path, language_name, status, lang_code): if not audio_path or status != "ready": return ( None, "", "", "", f"{get_ui_message('no_audio_error', lang_code)}", "", "", "" ) try: # First step: Recognize the song and show the title immediately result = recognize_song(audio_path) if "error" in result: return ( None, "", "", "", f"Error recognizing the song: {result['error']}", "", "", "" ) song_name = result['Song'] artist_name = result['Artist'] song_title_msg = f"🎵 **{song_name}** by {artist_name}" # Second step: Prepare detailed recognition info recognition_msg = f"### {get_ui_message('recognized_song', lang_code)}: {song_name}\n\n" recognition_msg += f"{get_ui_message('artist', lang_code)}: {artist_name}\n" recognition_msg += f"{get_ui_message('album', lang_code)}: {result['Album']}\n\n" if result['Spotify'] != "Not available": recognition_msg += f"{get_ui_message('listen_spotify', lang_code)} ([Spotify]({result['Spotify']}))\n" if result['Apple Music'] != "Not available": recognition_msg += f"[{get_ui_message('listen_apple', lang_code)}]({result['Apple Music']})\n" # Third step: Get artist info artist_info = get_artist_info(artist_name, song_name, lang_code) artist_facts_title = f"### {get_ui_message('about_artist', lang_code)} {artist_name}\n\n" artist_facts_content = artist_facts_title + artist_info status_msg = f"{get_ui_message('song_recognized', lang_code)}" return ( result, song_name, song_title_msg, recognition_msg, artist_facts_content, status_msg, "", artist_name ) except Exception as e: return ( None, "", "", "", f"{get_ui_message('processing_error', lang_code)}: {str(e)}", "", "", "" ) # Chat function def process_chat(query, language_name, song_info, artist_info, lang_code): if not query: return [] try: response = chat_with_assistant(query, artist_info, lang_code) return [{"role": "user", "content": query}, {"role": "assistant", "content": response}] except Exception as e: return [{"role": "user", "content": query}, {"role": "assistant", "content": f"Sorry, I couldn't process your request: {str(e)}"}] # Language selection event handler language_dropdown.change( fn=update_ui_language, inputs=[language_dropdown], outputs=[ title_component, subtitle_component, language_dropdown, record_btn, audio_status_msg, chat_btn, chat_section_title, chat_input, lang_code ] ) # Event handlers record_btn.click( fn=toggle_audio_widget, inputs=[lang_code], outputs=[ audio_input, audio_status, audio_status_msg, processing_indicator ] ).then( fn=lambda: "Microphone activated. Please speak or sing the song you want to recognize.", inputs=[], outputs=[microphone_status] ) audio_input.change( fn=update_audio_status, inputs=[audio_input, lang_code], outputs=[audio_status, audio_status_msg, processing_indicator] ).then( fn=process_audio, inputs=[audio_input, language_dropdown, audio_status, lang_code], outputs=[ song_info_state, song_name_state, song_title_display, recognition_results, artist_facts, audio_status_msg, processing_indicator, artist_info_state ] ) chat_btn.click( fn=process_chat, inputs=[chat_input, language_dropdown, song_info_state, artist_info_state, lang_code], outputs=[chat_history] ).then( fn=lambda: "", inputs=[], outputs=[chat_input] ) with gr.Tab("Song History"): history_tab_title = gr.Markdown(f"### {get_ui_message('history_title', current_lang)}") with gr.Row(): history_limit = gr.Slider(minimum=5, maximum=50, value=10, step=5, label=get_ui_message('songs_to_show', current_lang)) view_history_btn = gr.Button(get_ui_message('view_history', current_lang)) history_output = gr.Markdown() def view_history_process(limit): return view_song_history(int(limit)) view_history_btn.click( fn=view_history_process, inputs=[history_limit], outputs=[history_output] ) def update_history_tab_language(lang_name): lang_code = LANGUAGES.get(lang_name, 'en') return ( f"### {get_ui_message('history_title', lang_code)}", gr.update(label=get_ui_message('songs_to_show', lang_code)), gr.update(value=get_ui_message('view_history', lang_code)) ) language_dropdown.change( fn=update_history_tab_language, inputs=[language_dropdown], outputs=[history_tab_title, history_limit, view_history_btn] ) demo.launch( show_error=True, share=True, inbrowser=True, debug=True, allowed_paths=[], server_name="0.0.0.0" )