""" Gradio UI for the A11y Expert Agent with lazy initialization. This module creates a Gradio ChatInterface that starts FAST, then initializes the agent in the background. """ import sys import os # Suppress asyncio cleanup warnings by setting environment variable os.environ['PYTHONUNBUFFERED'] = '1' # Suppress all asyncio warnings at the earliest possible point import warnings warnings.filterwarnings('ignore', category=ResourceWarning) import gradio as gr from loguru import logger import atexit import threading from agent.a11y_agent import create_agent, A11yExpertAgent from config import get_settings # --- Setup --- # Configure logger logger.remove() logger.add(sys.stderr, level=get_settings().log_level) # Global agent instance agent_instance: A11yExpertAgent = None agent_ready = False agent_error = None # --- Agent Initialization --- def initialize_agent_background(): """Initialize the agent in background thread.""" global agent_instance, agent_ready, agent_error try: logger.info("🔄 Starting agent initialization in background...") import time logger.info("⏱️ Sleeping 2 seconds to avoid race condition...") time.sleep(2) logger.info("📦 Calling create_agent()...") agent_instance = create_agent() logger.info("✓ Agent instance created, setting ready flag...") agent_ready = True logger.success("✅ A11y Expert Agent is ready!") except Exception as e: logger.error(f"❌ Failed to initialize agent: {e}") import traceback logger.error(traceback.format_exc()) agent_error = str(e) agent_instance = None def cleanup_resources(): """Clean up resources on app shutdown.""" global agent_instance logger.info("Cleaning up resources...") try: # Close agent and all its resources if agent_instance: agent_instance.close() # Close embeddings client singleton if it exists from models.embeddings import get_embeddings_client if hasattr(get_embeddings_client, '_instance'): get_embeddings_client._instance.close() logger.success("✅ Resources cleaned up successfully") except Exception as e: logger.warning(f"Error during cleanup: {e}") # --- Gradio Chat Logic --- def respond(message: str, history: list[list[str]]): """ Main function for the Gradio ChatInterface. Receives a user message and chat history, then uses the agent to generate a streaming response. Args: message: The user's input message. history: The conversation history provided by Gradio. Yields: A stream of response chunks to update the UI. """ global agent_instance, agent_ready, agent_error # Initialize agent on first request if not already initialized if not agent_ready and not agent_error and agent_instance is None: yield "⏳ Initializing agent for first use, please wait..." try: logger.info("🔄 Initializing agent on first request...") agent_instance = create_agent() agent_ready = True logger.success("✅ A11y Expert Agent is ready!") except Exception as e: logger.error(f"❌ Failed to initialize agent: {e}") import traceback logger.error(traceback.format_exc()) agent_error = str(e) agent_instance = None yield f"❌ Agent initialization failed: {agent_error}" return # Check if agent failed to initialize if agent_error: yield f"❌ Agent initialization failed: {agent_error}" return if not agent_instance: yield "❌ Agent not available. Please check logs for errors." return logger.info(f"User query: '{message}'") full_response = "" try: for chunk in agent_instance.ask(message): full_response += chunk yield full_response except Exception as e: logger.error(f"Error during response generation: {e}") yield f"An error occurred: {e}" # --- Gradio UI Definition --- # Two-column layout: Chat on left, Markdown content on right with gr.Blocks(title="A11y Expert") as demo: gr.Markdown("# 🤖 A11y Expert") gr.Markdown("Twój inteligentny asystent do spraw dostępności cyfrowej.") with gr.Row(): # Left column: Chatbot with gr.Column(scale=1): gr.Markdown("### 💬 Chat") chatbot = gr.Chatbot(height=500, show_label=False) msg = gr.Textbox( placeholder="Zadaj pytanie o WCAG, ARIA, lub poproś o analizę kodu...", show_label=False, container=False, max_length=300 ) with gr.Row(): submit = gr.Button("Wyślij", variant="primary") clear = gr.Button("Wyczyść") # Example questions gr.Examples( examples=[ "Jakie są wymagania WCAG 2.2 dla etykiet formularzy?", "Wyjaśnij rolę 'alert' w ARIA i podaj przykład.", "Czy ten przycisk jest dostępny?