Spaces:
Sleeping
Sleeping
| from smolagents import CodeAgent, HfApiModel, tool | |
| import datetime | |
| import requests | |
| import pytz | |
| import yaml | |
| import os | |
| import pickle | |
| import time | |
| import gradio as gr | |
| from tools.final_answer import FinalAnswerTool | |
| # Herramienta para obtener la hora actual en una zona horaria | |
| def get_current_time_in_timezone(timezone: str) -> str: | |
| """A tool that fetches the current local time in a specified timezone. | |
| Args: | |
| timezone: A string representing a valid timezone (e.g., 'America/New_York'). | |
| """ | |
| try: | |
| # Create timezone object | |
| tz = pytz.timezone(timezone) | |
| # Get current time in that timezone | |
| local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") | |
| return f"The current local time in {timezone} is: {local_time}" | |
| except Exception as e: | |
| return f"Error fetching time for timezone '{timezone}': {str(e)}" | |
| # Herramienta para reconocer canciones usando AudD | |
| def recognize_song(audio_path: str) -> dict: | |
| """Reconoce una canción a partir de un archivo de audio | |
| Args: | |
| audio_path: ruta al archivo de audio a reconocer | |
| """ | |
| AUDD_API_TOKEN = os.getenv("AUDD_API_TOKEN") | |
| if not os.path.exists(audio_path): | |
| return {"error": "El archivo de audio no existe"} | |
| 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"Error en la API: {response.status_code}"} | |
| result = response.json() | |
| if result['status'] == 'error': | |
| return {"error": result['error']['error_message']} | |
| if not result.get('result'): | |
| return {"error": "No se pudo reconocer la canción"} | |
| song_info = result['result'] | |
| return { | |
| "🎶 Canción": song_info.get('title', 'Desconocido'), | |
| "🎤 Artista": song_info.get('artist', 'Desconocido'), | |
| "📀 Álbum": song_info.get('album', 'Desconocido'), | |
| "🎧 Spotify": song_info.get('spotify', {}).get('external_urls', {}).get('spotify', 'No disponible'), | |
| "🍏 Apple Music": song_info.get('apple_music', {}).get('url', 'No disponible') | |
| } | |
| except Exception as e: | |
| return {"error": f"Error al procesar el audio: {str(e)}"} | |
| # Herramienta para iniciar sesión en Soundeo | |
| def @tool | |
| def login_soundeo() -> str: | |
| """Inicia sesión en Soundeo usando las credenciales almacenadas en secrets""" | |
| from selenium import webdriver | |
| from selenium.webdriver.chrome.options import Options | |
| from selenium.webdriver.chrome.service import Service | |
| from webdriver_manager.chrome import ChromeDriverManager | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| # Obtener credenciales de los secrets | |
| username = os.getenv("SOUNDEO_USERNAME") | |
| password = os.getenv("SOUNDEO_PASSWORD") | |
| if not username or not password: | |
| return "❌ No se encontraron las credenciales en los secrets" | |
| # Configurar opciones de Chrome | |
| chrome_options = Options() | |
| chrome_options.add_argument("--headless") | |
| chrome_options.add_argument("--no-sandbox") | |
| chrome_options.add_argument("--disable-dev-shm-usage") | |
| chrome_options.add_argument("--disable-gpu") | |
| try: | |
| # Inicializar el driver con WebDriverManager | |
| service = Service(ChromeDriverManager().install()) | |
| driver = webdriver.Chrome(service=service, options=chrome_options) | |
| # Resto del código igual... | |
| # Navegar a Soundeo | |
| driver.get("https://soundeo.com/login") | |
| time.sleep(3) # Esperar a que cargue la página | |
| # Capturar screenshot para debug | |
| driver.save_screenshot("pre_login.png") | |
| # Completar formulario de login | |
| username_field = WebDriverWait(driver, 10).until( | |
| EC.presence_of_element_located((By.ID, "username")) | |
| ) | |
| username_field.send_keys(username) | |
| password_field = driver.find_element(By.ID, "password") | |
| password_field.send_keys(password) | |
| # Hacer clic en el botón de login | |
| login_button = driver.find_element(By.XPATH, "//button[@type='submit']") | |
| login_button.click() | |
| # Esperar a que se complete el login | |
| time.sleep(5) | |
| # Capturar screenshot para verificar | |
| driver.save_screenshot("post_login.png") | |
| # Guardar cookies para uso futuro | |
| pickle.dump(driver.get_cookies(), open("soundeo_cookies.pkl", "wb")) | |
| # Verificar si el login fue exitoso | |
| if "dashboard" in driver.current_url or "account" in driver.current_url: | |
| result = "✅ Login exitoso en Soundeo" | |
| else: | |
| result = "❌ Error al iniciar sesión. Verifica tus credenciales." | |
| driver.quit() | |
| return result | |
| except Exception as e: | |
| return f"❌ Error con Selenium: {str(e)}" | |
| # Herramienta para buscar y añadir una canción a la lista de descarga | |
| def add_song_to_download_list(song_title: str, artist: str) -> str: | |
| """Busca una canción en Soundeo y la añade a la lista de descarga | |
| Args: | |
| song_title: título de la canción | |
| artist: nombre del artista | |
| """ | |
| from selenium import webdriver | |
| from selenium.webdriver.chrome.options import Options | |
| from selenium.webdriver.chrome.service import Service | |
| from webdriver_manager.chrome import ChromeDriverManager | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| # Obtener credenciales de los secrets | |
| username = os.getenv("SOUNDEO_USERNAME") | |
| password = os.getenv("SOUNDEO_PASSWORD") | |
| if not username or not password: | |
| return "❌ No se encontraron las credenciales en los secrets" | |
| # Configurar opciones de Chrome | |
| chrome_options = Options() | |
| chrome_options.add_argument("--headless") | |
| chrome_options.add_argument("--no-sandbox") | |
| chrome_options.add_argument("--disable-dev-shm-usage") | |
| chrome_options.add_argument("--disable-gpu") | |
| try: | |
| # Inicializar el driver con WebDriverManager | |
| service = Service(ChromeDriverManager().install()) | |
| driver = webdriver.Chrome(service=service, options=chrome_options) | |
| # Verificar si hay sesión activa | |
| cookies_file = "soundeo_cookies.pkl" | |
| if not os.path.exists(cookies_file): | |
| driver.quit() | |
| return "❌ No hay sesión activa. Primero debes iniciar sesión en Soundeo." | |
| # Cargar cookies para mantener la sesión | |
| driver.get("https://soundeo.com") | |
| cookies = pickle.load(open(cookies_file, "rb")) | |
| for cookie in cookies: | |
| try: | |
| driver.add_cookie(cookie) | |
| except Exception: | |
| # Ignorar cookies problemáticas | |
| pass | |
| # Refrescar para aplicar cookies | |
| driver.refresh() | |
| time.sleep(2) | |
| # Verificar si la sesión sigue activa después de cargar cookies | |
| if "login" in driver.current_url: | |
| driver.save_screenshot("session_expired.png") | |
| driver.quit() | |
| return "❌ La sesión ha expirado. Por favor, inicia sesión nuevamente." | |
| # Construir una consulta de búsqueda más precisa | |
| # Usar comillas para búsqueda exacta de título y artista juntos | |
| search_query = f'"{song_title}" "{artist}"' | |
| search_query = search_query.replace(" ", "+") | |
| # Navegar a la búsqueda con la consulta | |
| search_url = f"https://soundeo.com/search?q={search_query}" | |
| driver.get(search_url) | |
| time.sleep(5) # Esperar a que carguen los resultados | |
| # Capturar screenshot de resultados para debugging | |
| driver.save_screenshot("search_results.png") | |
| # Buscar si hay resultados | |
| try: | |
| # Verificar primero si hay mensaje de "no se encontraron resultados" | |
| no_results = driver.find_elements(By.XPATH, "//div[contains(text(), 'No results found')]") | |
| if no_results: | |
| # Intentar con una búsqueda menos restrictiva | |
| simple_query = f"{song_title} {artist}".replace(" ", "+") | |
| driver.get(f"https://soundeo.com/search?q={simple_query}") | |
| time.sleep(5) | |
| driver.save_screenshot("broader_search_results.png") | |
| # Verificar nuevamente si hay resultados | |
| no_results = driver.find_elements(By.XPATH, "//div[contains(text(), 'No results found')]") | |
| if no_results: | |
| driver.quit() | |
| return f"❌ No se encontraron resultados para '{song_title}' por '{artist}'" | |
| # Encontrar todos los resultados para elegir la mejor coincidencia | |
| track_items = WebDriverWait(driver, 10).until( | |
| EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".track-item")) | |
| ) | |
| if not track_items: | |
| driver.quit() | |
| return f"❌ No se encontraron resultados para '{song_title}' por '{artist}'" | |
| # Buscar la mejor coincidencia entre los resultados | |
| best_match = None | |
| exact_match = False | |
| for item in track_items[:5]: # Limitar a los primeros 5 resultados | |
| try: | |
| title_elem = item.find_element(By.CSS_SELECTOR, ".track-title") | |
| artist_elem = item.find_element(By.CSS_SELECTOR, ".track-artist") | |
| item_title = title_elem.text.lower() | |
| item_artist = artist_elem.text.lower() | |
| # Verificar si es una coincidencia exacta | |
| if song_title.lower() in item_title and artist.lower() in item_artist: | |
| best_match = item | |
| exact_match = True | |
| break | |
| # Si no hay coincidencia exacta, tomar el primer resultado | |
| if best_match is None: | |
| best_match = item | |
| except Exception: | |
| continue | |
| if best_match is None: | |
| driver.quit() | |
| return f"❌ No se pudieron procesar los resultados para '{song_title}' por '{artist}'" | |
| # Hacer clic en "Add to Download List" | |
| try: | |
| download_button = best_match.find_element(By.CSS_SELECTOR, ".download-button") | |
| download_button.click() | |
| time.sleep(3) | |
| driver.save_screenshot("added_to_list.png") | |
| # Obtener información del track añadido | |
| track_info = best_match.find_element(By.CSS_SELECTOR, ".track-title").text | |
| artist_info = best_match.find_element(By.CSS_SELECTOR, ".track-artist").text | |
| match_type = "exacta" if exact_match else "aproximada" | |
| driver.quit() | |
| return f"✅ Añadida a la lista de descarga (coincidencia {match_type}): {track_info} - {artist_info}" | |
| except Exception as e: | |
| driver.save_screenshot("download_button_error.png") | |
| driver.quit() | |
| return f"❌ Error al añadir a la lista de descarga: {str(e)}" | |
| except Exception as e: | |
| driver.save_screenshot("search_error.png") | |
| driver.quit() | |
| return f"❌ Error al buscar resultados: {str(e)}" | |
| except Exception as e: | |
| return f"❌ Error con Selenium: {str(e)}" | |
| def view_download_list() -> str: | |
| """Muestra la lista actual de canciones en la lista de descargas de Soundeo""" | |
| from selenium import webdriver | |
| from selenium.webdriver.chrome.options import Options | |
| from selenium.webdriver.chrome.service import Service | |
| from webdriver_manager.chrome import ChromeDriverManager | |
| from selenium.webdriver.common.by import By | |
| from selenium.webdriver.support.ui import WebDriverWait | |
| from selenium.webdriver.support import expected_conditions as EC | |
| # Obtener credenciales de los secrets | |
| username = os.getenv("SOUNDEO_USERNAME") | |
| password = os.getenv("SOUNDEO_PASSWORD") | |
| if not username or not password: | |
| return "❌ No se encontraron las credenciales en los secrets" | |
| # Configurar opciones de Chrome | |
| chrome_options = Options() | |
| chrome_options.add_argument("--headless") | |
| chrome_options.add_argument("--no-sandbox") | |
| chrome_options.add_argument("--disable-dev-shm-usage") | |
| chrome_options.add_argument("--disable-gpu") | |
| try: | |
| # Inicializar el driver con WebDriverManager | |
| service = Service(ChromeDriverManager().install()) | |
| driver = webdriver.Chrome(service=service, options=chrome_options) | |
| # Verificar si hay sesión activa | |
| cookies_file = "soundeo_cookies.pkl" | |
| if not os.path.exists(cookies_file): | |
| driver.quit() | |
| return "❌ No hay sesión activa. Primero debes iniciar sesión en Soundeo." | |
| # Cargar cookies para mantener la sesión | |
| driver.get("https://soundeo.com") | |
| cookies = pickle.load(open(cookies_file, "rb")) | |
| for cookie in cookies: | |
| try: | |
| driver.add_cookie(cookie) | |
| except Exception: | |
| # Ignorar cookies problemáticas | |
| pass | |
| # Refrescar para aplicar cookies | |
| driver.refresh() | |
| time.sleep(2) | |
| # Verificar si la sesión sigue activa después de cargar cookies | |
| if "login" in driver.current_url: | |
| driver.save_screenshot("session_expired.png") | |
| driver.quit() | |
| return "❌ La sesión ha expirado. Por favor, inicia sesión nuevamente." | |
| # Navegar a la página de la lista de descargas | |
| driver.get("https://soundeo.com/download-list") | |
| time.sleep(5) | |
| # Capturar screenshot para debugging | |
| driver.save_screenshot("download_list.png") | |
| # Buscar las canciones en la lista | |
| try: | |
| track_items = driver.find_elements(By.CSS_SELECTOR, ".track-item") | |
| if not track_items: | |
| driver.quit() | |
| return "📋 Tu lista de descargas está vacía." | |
| # Crear una lista de las canciones | |
| download_list = [] | |
| for i, item in enumerate(track_items, 1): | |
| try: | |
| title = item.find_element(By.CSS_SELECTOR, ".track-title").text | |
| artist = item.find_element(By.CSS_SELECTOR, ".track-artist").text | |
| download_list.append(f"{i}. **{title}** - *{artist}*") | |
| except Exception: | |
| download_list.append(f"{i}. *[Error al obtener detalles]*") | |
| driver.quit() | |
| # Formatear la salida | |
| result = "📋 **Lista de descargas actual:**\n\n" | |
| result += "\n".join(download_list) | |
| result += f"\n\n**Total: {len(download_list)} canciones**" | |
| return result | |
| except Exception as e: | |
| driver.save_screenshot("list_error.png") | |
| driver.quit() | |
| return f"❌ Error al obtener la lista de descargas: {str(e)}" | |
| except Exception as e: | |
| return f"❌ Error con Selenium: {str(e)}" | |
| def recognize_and_download(audio_path: str) -> str: | |
| """Reconoce una canción desde un archivo de audio y la añade a la lista de descargas | |
| Args: | |
| audio_path: ruta al archivo de audio a reconocer | |
| """ | |
| # Primero reconocer la canción | |
| recognition_result = recognize_song(audio_path) | |
| if "error" in recognition_result: | |
| return f"❌ Error en el reconocimiento: {recognition_result['error']}" | |
| # Obtener título y artista | |
| song_title = recognition_result["🎶 Canción"] | |
| artist = recognition_result["🎤 Artista"] | |
| # Añadir a la lista de descargas | |
| download_result = add_song_to_download_list(song_title, artist) | |
| # Formatear la respuesta | |
| response = f"🎵 **Canción reconocida:** {song_title}\n" | |
| response += f"🎤 **Artista:** {artist}\n" | |
| response += f"📀 **Álbum:** {recognition_result['📀 Álbum']}\n\n" | |
| if "✅" in download_result: | |
| response += f"✅ **{download_result}**\n\n" | |
| else: | |
| response += f"⚠️ **{download_result}**\n\n" | |
| if recognition_result['🎧 Spotify'] != "No disponible": | |
| response += f"🎧 [Escuchar en Spotify]({recognition_result['🎧 Spotify']})\n" | |
| if recognition_result['🍏 Apple Music'] != "No disponible": | |
| response += f"🍏 [Escuchar en Apple Music]({recognition_result['🍏 Apple Music']})\n" | |
| return response | |
| def process_audio_query(query: str) -> str: | |
| """Procesa una consulta relacionada con reconocimiento de audio y busca el archivo más reciente | |
| Args: | |
| query: la consulta del usuario sobre reconocimiento de audio | |
| """ | |
| # Buscar el archivo de audio más reciente en la carpeta de uploads | |
| import os | |
| import glob | |
| # Normalmente Gradio guarda los archivos en una carpeta como esta | |
| audio_files = glob.glob('/tmp/gradio/*') | |
| if not audio_files: | |
| return "No se encontraron archivos de audio recientes." | |
| # Ordenar por fecha de modificación (el más reciente primero) | |
| latest_audio = max(audio_files, key=os.path.getmtime) | |
| # Usar la herramienta de reconocimiento con el archivo encontrado | |
| result = recognize_song(latest_audio) | |
| if "error" in result: | |
| return f"Error al reconocer la canción: {result['error']}" | |
| # Formatear la respuesta | |
| response = f"🎵 **Canción reconocida:** {result['🎶 Canción']}\n" | |
| response += f"🎤 **Artista:** {result['🎤 Artista']}\n" | |
| response += f"📀 **Álbum:** {result['📀 Álbum']}\n" | |
| if result['🎧 Spotify'] != "No disponible": | |
| response += f"🎧 [Escuchar en Spotify]({result['🎧 Spotify']})\n" | |
| if result['🍏 Apple Music'] != "No disponible": | |
| response += f"🍏 [Escuchar en Apple Music]({result['🍏 Apple Music']})\n" | |
| return response | |
| def test_recognition_with_samples() -> str: | |
| """Genera un archivo de audio de prueba y lo reconoce""" | |
| import numpy as np | |
| import tempfile | |
| import os | |
| from scipy.io import wavfile | |
| try: | |
| # Generar un tono simple (440 Hz, nota La) | |
| sample_rate = 44100 # 44.1 kHz | |
| duration = 5 # 5 segundos | |
| t = np.linspace(0, duration, int(sample_rate * duration), False) | |
| # Crear una melodía simple con algunas frecuencias | |
| note_a = np.sin(2 * np.pi * 440 * t) # La (A4) | |
| note_c = np.sin(2 * np.pi * 523.25 * t) # Do (C5) | |
| note_e = np.sin(2 * np.pi * 659.25 * t) # Mi (E5) | |
| # Combinar en una melodía simple | |
| melody = np.concatenate([ | |
| note_a[:sample_rate], | |
| note_c[:sample_rate], | |
| note_e[:sample_rate], | |
| note_c[:sample_rate], | |
| note_a[:sample_rate] | |
| ]) | |
| # Normalizar para evitar clipping | |
| melody = melody * 0.3 | |
| # Guardar como archivo WAV | |
| fd, temp_path = tempfile.mkstemp(suffix=".wav") | |
| os.close(fd) | |
| wavfile.write(temp_path, sample_rate, melody.astype(np.float32)) | |
| # Verificar que el archivo existe y tiene contenido | |
| file_size = os.path.getsize(temp_path) | |
| # Informar el resultado (en un caso real intentaríamos reconocer, | |
| # pero como es un audio generado, probablemente no lo reconocería) | |
| return f"✅ Archivo de audio de prueba generado correctamente, tamaño: {file_size} bytes.\n\nNota: Este es un audio sintético generado para pruebas, no una canción real." | |
| except Exception as e: | |
| return f"❌ Error: {str(e)}" | |
| # Configuración del agente | |
| 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) | |
| # REEMPLAZA ESTE BLOQUE COMPLETO | |
| agent = CodeAgent( | |
| model=model, | |
| tools=[ | |
| final_answer, | |
| recognize_song, | |
| login_soundeo, | |
| add_song_to_download_list, | |
| get_current_time_in_timezone, | |
| test_recognition_with_samples, | |
| view_download_list, | |
| recognize_and_download | |
| ], | |
| max_steps=8, | |
| verbosity_level=1, | |
| grammar=None, | |
| planning_interval=None, | |
| name=None, | |
| description=None, | |
| prompt_templates=prompt_templates | |
| ) | |
| # Después de este bloque suele venir la configuración de la interfaz Gradio | |
| # Interfaz de usuario con Gradio | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🎵 Asistente Musical - Reconocimiento y Descarga") | |
| with gr.Tab("Reconocimiento de Música"): | |
| with gr.Row(): | |
| audio_input = gr.Audio(type="filepath", label="Sube o graba un fragmento de audio") | |
| with gr.Row(): | |
| recognize_btn = gr.Button("🔍 Reconocer Canción") | |
| recognize_download_btn = gr.Button("🔍+📥 Reconocer y Añadir a Descargas") | |
| output = gr.Markdown() | |
| # Función para procesar el reconocimiento | |
| def process_audio(audio_path): | |
| if not audio_path: | |
| return "❌ Por favor sube o graba un fragmento de audio." | |
| try: | |
| result = recognize_song(audio_path) | |
| if "error" in result: | |
| return f"Error al reconocer la canción: {result['error']}" | |
| # Formatear la respuesta | |
| response = f"🎵 **Canción reconocida:** {result['🎶 Canción']}\n" | |
| response += f"🎤 **Artista:** {result['🎤 Artista']}\n" | |
| response += f"📀 **Álbum:** {result['📀 Álbum']}\n" | |
| if result['🎧 Spotify'] != "No disponible": | |
| response += f"🎧 [Escuchar en Spotify]({result['🎧 Spotify']})\n" | |
| if result['🍏 Apple Music'] != "No disponible": | |
| response += f"🍏 [Escuchar en Apple Music]({result['🍏 Apple Music']})\n" | |
| return response | |
| except Exception as e: | |
| return f"❌ Error al procesar el audio: {str(e)}" | |
| # Función para reconocer y añadir a descargas | |
| def process_audio_and_download(audio_path): | |
| if not audio_path: | |
| return "❌ Por favor sube o graba un fragmento de audio." | |
| try: | |
| return recognize_and_download(audio_path) | |
| except Exception as e: | |
| return f"❌ Error al procesar el audio: {str(e)}" | |
| recognize_btn.click( | |
| fn=process_audio, | |
| inputs=[audio_input], | |
| outputs=output | |
| ) | |
| recognize_download_btn.click( | |
| fn=process_audio_and_download, | |
| inputs=[audio_input], | |
| outputs=output | |
| ) | |
| with gr.Tab("Gestión de Soundeo"): | |
| gr.Markdown("### Inicia sesión y gestiona tu lista de descargas") | |
| with gr.Row(): | |
| login_btn = gr.Button("🔑 Iniciar Sesión") | |
| view_list_btn = gr.Button("📋 Ver Lista de Descargas") | |
| with gr.Row(): | |
| song_title = gr.Textbox(label="Título de la canción") | |
| artist = gr.Textbox(label="Artista") | |
| add_btn = gr.Button("➕ Añadir a Descargas") | |
| soundeo_output = gr.Markdown() | |
| def login_process(): | |
| return login_soundeo() | |
| def view_list_process(): | |
| return view_download_list() | |
| def add_to_list_process(title, artist): | |
| if not title or not artist: | |
| return "❌ Por favor ingresa tanto el título como el artista." | |
| return add_song_to_download_list(title, artist) | |
| login_btn.click( | |
| fn=login_process, | |
| inputs=[], | |
| outputs=soundeo_output | |
| ) | |
| view_list_btn.click( | |
| fn=view_list_process, | |
| inputs=[], | |
| outputs=soundeo_output | |
| ) | |
| add_btn.click( | |
| fn=add_to_list_process, | |
| inputs=[song_title, artist], | |
| outputs=soundeo_output | |
| ) | |
| with gr.Tab("Pruebas con Muestras"): | |
| gr.Markdown("### Probar con muestras de música") | |
| test_btn = gr.Button("Descargar Muestra de Prueba") | |
| test_results = gr.Markdown() | |
| def direct_test(): | |
| # Ejecuta la función directamente en lugar de usar el agente | |
| return test_recognition_with_samples() | |
| test_btn.click(fn=direct_test, inputs=[], outputs=test_results) | |
| demo.launch() |