File size: 8,423 Bytes
b172fa2
2bd0651
 
aaa20d9
 
2bd0651
66dc777
2bd0651
66dc777
2bd0651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66dc777
2bd0651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b172fa2
2bd0651
 
 
 
 
b172fa2
2bd0651
 
 
 
 
66dc777
2bd0651
 
 
 
66dc777
2bd0651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66dc777
 
2bd0651
 
 
66dc777
 
2bd0651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# 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