project-chimera / app.py
mgbam's picture
Update app.py
2bd0651 verified
# 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