# app.py import gradio as gr import asyncio import sys import os import traceback # Import traceback for detailed error logging if needed # --- Attempt to import necessary components for the actual app --- try: from src.chimera.core.orchestrator import run_analysis from src.chimera.utils.logging_config import setup_logging, logger from src.chimera.config import GEMINI_API_KEY, SERPAPI_API_KEY # Import API keys to check configuration # --- Perform initial setup --- setup_logging() # Initialize logging system logger.info("Logging initialized.") # --- Configuration Checks --- CONFIG_ERRORS = [] if not GEMINI_API_KEY: CONFIG_ERRORS.append("GEMINI_API_KEY is not set.") logger.error("CRITICAL CONFIG ERROR: GEMINI_API_KEY is not set.") if not SERPAPI_API_KEY: # Assuming SERP is essential, make it critical too CONFIG_ERRORS.append("SERPAPI_API_KEY is not set.") logger.error("CRITICAL CONFIG ERROR: SERPAPI_API_KEY is not set.") # Add checks for other essential API keys here # --- Set flag based on configuration status --- IS_CONFIGURED_PROPERLY = not CONFIG_ERRORS except ImportError as e: # Handle case where core modules cannot be imported - app cannot function print(f"FATAL IMPORT ERROR: {e}") print("Could not import necessary Chimera components. Ensure structure is correct and __init__.py files exist.") traceback.print_exc() # Define dummy elements to allow the script to potentially reach the end # but indicate a non-functional state run_analysis = None setup_logging = lambda: print("!!! Logging setup skipped due to import error !!!") logger = type('DummyLogger', (object,), {'info': print, 'warning': print, 'error': print, 'exception': print})() logger.error("!!! Logging is not properly configured due to import errors !!!") IS_CONFIGURED_PROPERLY = False CONFIG_ERRORS = ["Core application modules failed to import."] print("!!! Application will not function correctly due to import errors !!!") # --- Define the core Gradio interface function --- async def chimera_interface(query: str): """ Wrapper function for Gradio to call the async orchestrator. Handles input validation, configuration checks, and calls the backend. """ # 1. Check if the application backend is fundamentally broken (import errors) if run_analysis is None: logger.error("Attempted analysis but run_analysis function failed to import during startup.") # Provide a clear error message in the UI return "## Error: Application Backend Failed\n\nThe core analysis function could not be loaded. Please check the application logs." # 2. Check if essential configuration (API Keys) is missing if not IS_CONFIGURED_PROPERLY: logger.error(f"Attempted analysis with missing configuration: {', '.join(CONFIG_ERRORS)}") error_list = "\n".join([f"- {err}" for err in CONFIG_ERRORS]) # Provide a clear error message in the UI return f"## Error: Application Not Configured\n\nThe following essential configurations are missing:\n{error_list}\nPlease check the application secrets/environment variables." # 3. Check for empty user input if not query or not query.strip(): logger.warning("Received empty query.") # Provide guidance in the UI return "Please enter a query in the text box above." # 4. If all checks pass, proceed with the analysis logger.info(f"Gradio interface received query: '{query[:100]}...'") # Log snippet try: # Gradio handles running the async function correctly result = await run_analysis(query) logger.info("Gradio interface processing complete.") # Return the result (expecting markdown formatted string from backend) return result except Exception as e: # Log the full error details for debugging logger.exception(f"Unexpected error during run_analysis for query: '{query[:100]}...'") # Provide a user-friendly error message in the UI # Avoid exposing raw exception details directly unless intended for debugging UI return f"## Error: Analysis Failed\n\nAn unexpected error occurred while processing your request.\nDetails have been logged by the application administrator.\n\n*(Error type: {type(e).__name__})*" # --- Gradio UI Definition --- # Use a try-except block to catch errors during UI construction demo = None # Initialize demo to None try: with gr.Blocks(theme=gr.themes.Soft(), title="Project Chimera") as demo: # Display configuration errors prominently if they exist if not IS_CONFIGURED_PROPERLY: config_error_msg = "\n".join([f"- {err}" for err in CONFIG_ERRORS]) gr.Markdown( f""" ## ⚠️ Application Configuration Error The application cannot function correctly because the following configurations are missing: {config_error_msg} Please contact the administrator or check the environment secrets. """, elem_id="config_error_banner" ) # Main UI components gr.Markdown( """ # Project Chimera : Real-Time Global Analysis Engine Enter your query to analyze real-time data from SERP and other sources using Gemini. *(Example: "Analyze recent news about renewable energy investments in the US")* """ ) with gr.Row(): query_input = gr.Textbox( label="Your Query:", placeholder="Type your complex question or analysis request here...", lines=3, elem_id="query_input_box" ) submit_button = gr.Button("Analyze", variant="primary", elem_id="submit_button") with gr.Row(): # Using Markdown for output allows richer formatting (like headers from error messages) output_display = gr.Markdown(label="Chimera Analysis:", elem_id="output_display_markdown") # Link the button click to the interface function submit_button.click( fn=chimera_interface, inputs=[query_input], outputs=[output_display], # Add API name for potential tracking/analytics if needed by Gradio # api_name="chimera_analysis" ) # Example usage display gr.Examples( examples=[ "Search recent news about AI impact on healthcare.", "What are the latest developments in fusion energy according to recent searches?", "Summarize the latest discussion around supply chain disruptions.", "What are recent news headlines mentioning 'geopolitical tensions'?" ], inputs=[query_input], outputs=[output_display], fn=chimera_interface, # Make examples clickable cache_examples=False, # Avoid caching as results depend on real-time data elem_id="examples_section" ) except Exception as e: logger.exception("FATAL: Failed to build Gradio interface.") # demo remains None if gr.Blocks fails print(f"Error creating Gradio Blocks: {e}") traceback.print_exc() # --- Launching the App --- if __name__ == "__main__": if demo and IS_CONFIGURED_PROPERLY and run_analysis is not None: # Only launch if Gradio Blocks were created AND config is okay AND core function loaded logger.info("Starting Gradio application...") # Standard launch for Hugging Face Spaces # queue() can be added for better handling of concurrent users if needed: demo.queue().launch() demo.launch() elif not demo: logger.error("Gradio application blocks failed to initialize. Cannot launch.") print("Application launch aborted due to UI build errors.") else: # Handles cases where config failed or run_analysis didn't import logger.error("Application launch aborted due to configuration errors or failed imports.") print("Application launch aborted. Please check configuration and import logs.") # Keep the container running but without the app interface (or exit) # Depending on HF Spaces behavior, it might restart or just show logs