import os import sys import re import numpy as np from dotenv import load_dotenv import gradio as gr from audio_utils import process_input_and_generate_speech, GeminiHandler, generate_tts_response import google.genai as genai from fastrtc import WebRTC, get_cloudflare_turn_credentials_async from gradio.utils import get_space from utils import process_query from audio_utils import get_transcription_or_text from logger.custom_logger import CustomLoggerTracker # Import enhanced functions from specific_utils from gradio_utils import ( get_all_document_choices, enhanced_document_upload_handler, enhanced_audio_transcription, process_text_with_audio_support, process_audio_only_response, process_both_text_and_audio_response, create_document_info_panel, get_enhanced_css ) # Setup sys.path.append(os.path.dirname(os.path.abspath(__file__))) load_dotenv() # Initialize Gemini client properly gemini_client = genai.Client(api_key=os.getenv("GEMINI_API_KEY")) custom_log = CustomLoggerTracker() logger = custom_log.get_logger("main") logger.info("Logger initialized for main module") def parse_score_safely(score_str): """Safely parse hallucination score, handling extra characters.""" if not score_str: return 0 # Extract just the number from strings like "4**" or "Score: 4" import re numbers = re.findall(r'\d+', str(score_str)) if numbers: return int(numbers[0]) return 0 def process_text_only(user_input, audio_input, chat_history): """Process input and generate only text response using enhanced functions.""" try: # Use enhanced text processing with audio support new_history, cleared_input, status_msg, stats_display = process_text_with_audio_support( user_input, audio_input, chat_history ) return new_history, status_msg, cleared_input, None except Exception as e: logger.error(f"Error in enhanced text processing: {e}") return chat_history, f"Status: Error - {str(e)}", "", None def process_audio_only(user_input, audio_input, voice_dropdown, chat_history): """Process input and generate only audio response using enhanced functions.""" try: # Use enhanced audio processing audio_response, cleared_input, status_msg, stats_display = process_audio_only_response( user_input, audio_input, voice_dropdown, chat_history ) return audio_response, status_msg, cleared_input, None except Exception as e: logger.error(f"Error in enhanced audio processing: {e}") return None, f"Status: Error - {str(e)}", "", None def process_both_text_and_audio(text_input, audio_input, voice_dropdown, chat_history): """Process input and generate both text and audio responses using enhanced functions.""" try: # Use enhanced combined processing new_history, audio_response, cleared_input, status_msg, stats_display = process_both_text_and_audio_response( text_input, audio_input, voice_dropdown, chat_history ) return new_history, audio_response, status_msg, cleared_input, None except Exception as e: logger.error(f"Error in enhanced combined processing: {e}") return chat_history, None, f"Status: Error - {str(e)}", "", None def toggle_user_doc_visibility(selected_type): """Toggle visibility of user document options.""" return gr.update(visible=(selected_type == "User-Specific Document")) def validate_environment() -> bool: """Validate required environment variables are present.""" required_env_vars = [ "GEMINI_API_KEY", "SILICONFLOW_API_KEY", "SILICONFLOW_CHAT_URL" ] missing_vars = [] for var in required_env_vars: if not os.getenv(var): missing_vars.append(var) if missing_vars: logger.warning(f"Missing environment variables: {', '.join(missing_vars)}") return False logger.info("All required environment variables are present") return True def clear_chat(): """Clear chat history.""" return [], None, "Status: Chat cleared." # Gradio Interface Configuration image_path = "assets/Compumacy-Logo-Trans2.png" # Dark Mode Professional Theme with Custom CSS dark_theme_css = """ /* Dark Mode Theme */ .gradio-container { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f1419 100%) !important; color: #e0e6ed !important; border-radius: 20px !important; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important; max-width: 1200px !important; margin: auto; } /* Header styling */ .gradio-container h1 { color: #ffffff !important; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3) !important; } /* Logo container */ .logo-container { background: rgba(255, 255, 255, 0.1) !important; border-radius: 15px !important; padding: 10px !important; backdrop-filter: blur(10px) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; } /* Chatbot styling */ .chatbot { background: rgba(255, 255, 255, 0.05) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; border-radius: 15px !important; } .chatbot .message { background: rgba(255, 255, 255, 0.08) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; border-radius: 10px !important; color: #e0e6ed !important; } .chatbot .message.user { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; } .chatbot .message.bot { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important; color: white !important; } /* Input fields */ .gr-textbox, .gr-dropdown, .gr-file { background: rgba(255, 255, 255, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 10px !important; color: #e0e6ed !important; backdrop-filter: blur(5px) !important; } .gr-textbox::placeholder { color: rgba(224, 230, 237, 0.6) !important; } /* Buttons */ .gr-button { border-radius: 10px !important; border: none !important; font-weight: 600 !important; text-transform: uppercase !important; letter-spacing: 0.5px !important; transition: all 0.3s ease !important; backdrop-filter: blur(10px) !important; } .text-button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important; } .text-button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important; } .audio-button { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%) !important; color: white !important; box-shadow: 0 4px 15px rgba(240, 147, 251, 0.3) !important; } .audio-button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(240, 147, 251, 0.4) !important; } .both-button { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%) !important; color: white !important; box-shadow: 0 4px 15px rgba(79, 172, 254, 0.3) !important; } .both-button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(79, 172, 254, 0.4) !important; } /* Clear button */ .clear-button { background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%) !important; color: white !important; box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3) !important; } .clear-button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(255, 107, 107, 0.4) !important; } /* Labels and text */ label, .gr-form label { color: #e0e6ed !important; font-weight: 500 !important; } /* Audio component */ .gr-audio { background: rgba(255, 255, 255, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 15px !important; backdrop-filter: blur(10px) !important; } /* Accordion */ .gr-accordion { background: rgba(255, 255, 255, 0.05) !important; border: 1px solid rgba(255, 255, 255, 0.1) !important; border-radius: 15px !important; } /* Info boxes */ .info-box { background: rgba(79, 172, 254, 0.1) !important; border-left: 4px solid #4facfe !important; border-radius: 10px !important; padding: 15px !important; margin: 10px 0 !important; backdrop-filter: blur(5px) !important; } /* Status output */ .status-output { background: rgba(255, 255, 255, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.2) !important; border-radius: 10px !important; color: #e0e6ed !important; } /* Hide default footer */ footer { display: none !important; } /* Scrollbar styling */ ::-webkit-scrollbar { width: 8px; } ::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.1); border-radius: 4px; } ::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } /* Responsive design */ @media (max-width: 768px) { .gradio-container { margin: 10px !important; border-radius: 15px !important; } } """ with gr.Blocks( title="Wisal Chatbot - Autism AI Assistant", theme=gr.themes.Base( primary_hue="blue", secondary_hue="purple", neutral_hue="slate", font=gr.themes.GoogleFont("Inter") ), css=dark_theme_css ) as demo: gr.HTML("""
Your personalized AI assistant designed specifically for individuals with autism
Please use the text and audio inputs above instead.
Perfect for quick questions when you want to read the response and save it in chat history.
Great for hands-free interaction when you want to listen to responses without cluttering the chat.
Best of both worlds - see and hear responses, perfect for learning and accessibility.