Spaces:
				
			
			
	
			
			
		Runtime error
		
	
	
	
			
			
	
	
	
	
		
		
		Runtime error
		
	Update with enhanced AI Research Assistant - streaming output, 8192 tokens, improved UI
Browse files- README.md +110 -49
 - app.py +124 -34
 - modules/analyzer.py +21 -4
 - modules/citation.py +10 -10
 - modules/context_enhancer.py +15 -19
 - modules/retriever.py +17 -11
 - modules/server_monitor.py +167 -0
 - requirements.txt +1 -0
 - version.json +2 -2
 
    	
        README.md
    CHANGED
    
    | 
         @@ -1,10 +1,4 @@ 
     | 
|
| 1 | 
         
             
            ---
         
     | 
| 2 | 
         
            -
            license: apache-2.0
         
     | 
| 3 | 
         
            -
            title: AI Research Assistant
         
     | 
| 4 | 
         
            -
            sdk: gradio
         
     | 
| 5 | 
         
            -
            ---
         
     | 
| 6 | 
         
            -
            # README.md
         
     | 
| 7 | 
         
            -
            ---
         
     | 
| 8 | 
         
             
            title: AI Research Assistant
         
     | 
| 9 | 
         
             
            sdk: gradio
         
     | 
| 10 | 
         
             
            sdk_version: 4.38.1
         
     | 
| 
         @@ -12,61 +6,128 @@ app_file: app.py 
     | 
|
| 12 | 
         
             
            license: apache-2.0
         
     | 
| 13 | 
         
             
            ---
         
     | 
| 14 | 
         | 
| 15 | 
         
            -
            # AI Research Assistant
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 16 | 
         | 
| 17 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 18 | 
         | 
| 19 | 
         
            -
            ## Features
         
     | 
| 20 | 
         | 
| 21 | 
         
            -
            - Web search integration with Tavily API
         
     | 
| 22 | 
         
            -
            - Context enrichment with weather and space weather data
         
     | 
| 23 | 
         
            -
            - LLM analysis using Hugging Face Inference Endpoint
         
     | 
| 24 | 
         
            -
            - Redis caching for improved performance
         
     | 
| 25 | 
         
            -
            - Citation generation for sources
         
     | 
| 26 | 
         
            -
            - Responsive Gradio interface
         
     | 
| 27 | 
         | 
| 28 | 
         
            -
            ##  
     | 
| 29 | 
         | 
| 30 | 
         
            -
             
     | 
| 31 | 
         | 
| 32 | 
         
            -
            -  
     | 
| 33 | 
         
            -
            -  
     | 
| 34 | 
         
            -
            -  
     | 
| 35 | 
         
            -
            -  
     | 
| 36 | 
         
            -
            - `modules/formatter.py`: Structures and formats final output
         
     | 
| 37 | 
         
            -
            - `modules/input_handler.py`: Validates and prepares user input
         
     | 
| 38 | 
         
            -
            - `modules/retriever.py`: Uses Tavily API for web search
         
     | 
| 39 | 
         
            -
            - `modules/server_cache.py`: Uses Redis for caching frequent queries
         
     | 
| 40 | 
         
            -
            - `modules/status_logger.py`: Logs system status and performance
         
     | 
| 41 | 
         
            -
            - `modules/visualizer.py`: Renders output in a user-friendly format
         
     | 
| 42 | 
         
            -
            - `modules/visualize_uptime.py`: Monitors system uptime
         
     | 
| 43 | 
         | 
| 44 | 
         
            -
            ## API Integrations
         
     | 
| 45 | 
         | 
| 46 | 
         
            -
             
     | 
| 47 | 
         
            -
             
     | 
| 48 | 
         
            -
             
     | 
| 49 | 
         
            -
             
     | 
| 50 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 51 | 
         | 
| 52 | 
         
            -
            ##  
     | 
| 53 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 54 | 
         
             
            1. Clone the repository
         
     | 
| 55 | 
         
            -
            2. Set up  
     | 
| 56 | 
         
            -
                
     | 
| 57 | 
         
            -
                
     | 
| 58 | 
         
            -
                
     | 
| 59 | 
         
            -
                
     | 
| 60 | 
         
            -
                
     | 
| 61 | 
         
            -
             
     | 
| 62 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 63 | 
         | 
| 64 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 65 | 
         | 
| 66 | 
         
            -
             
     | 
| 67 | 
         
            -
             
     | 
| 68 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 69 | 
         | 
| 70 | 
         
            -
             
     | 
| 
         | 
|
| 71 | 
         | 
| 72 | 
         
            -
             
     | 
| 
         | 
| 
         | 
|
| 1 | 
         
             
            ---
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 2 | 
         
             
            title: AI Research Assistant
         
     | 
| 3 | 
         
             
            sdk: gradio
         
     | 
| 4 | 
         
             
            sdk_version: 4.38.1
         
     | 
| 
         | 
|
| 6 | 
         
             
            license: apache-2.0
         
     | 
| 7 | 
         
             
            ---
         
     | 
| 8 | 
         | 
| 9 | 
         
            +
            # 🧠 AI Research Assistant
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
            +
            An advanced AI-powered research assistant that combines web search capabilities with contextual awareness to provide comprehensive answers to complex questions.
         
     | 
| 12 | 
         
            +
             
     | 
| 13 | 
         
            +
            ## 🌟 Key Features
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
            - **Real-time Streaming Output**: See responses as they're generated for immediate feedback
         
     | 
| 16 | 
         
            +
            - **Contextual Awareness**: Incorporates current weather and space weather data
         
     | 
| 17 | 
         
            +
            - **Web Search Integration**: Powered by Tavily API for up-to-date information
         
     | 
| 18 | 
         
            +
            - **Smart Caching**: Redis-based caching for faster repeated queries
         
     | 
| 19 | 
         
            +
            - **Intelligent Server Monitoring**: Clear guidance during model warm-up periods
         
     | 
| 20 | 
         
            +
            - **Accurate Citations**: Real sources extracted from search results
         
     | 
| 21 | 
         
            +
            - **Asynchronous Processing**: Parallel execution for optimal performance
         
     | 
| 22 | 
         
            +
            - **Responsive Interface**: Modern Gradio UI with example queries
         
     | 
| 23 | 
         
            +
             
     | 
| 24 | 
         
            +
            ## 🏗️ Architecture
         
     | 
| 25 | 
         | 
| 26 | 
         
            +
            The application follows a modular architecture for maintainability and scalability:
         
     | 
| 27 | 
         
            +
            myspace134v/
         
     | 
| 28 | 
         
            +
            ├── app.py # Main Gradio interface
         
     | 
| 29 | 
         
            +
            ├── modules/
         
     | 
| 30 | 
         
            +
            │ ├── analyzer.py # LLM interaction with streaming
         
     | 
| 31 | 
         
            +
            │ ├── citation.py # Citation generation and formatting
         
     | 
| 32 | 
         
            +
            │ ├── context_enhancer.py # Weather and space context (async)
         
     | 
| 33 | 
         
            +
            │ ├── formatter.py # Response formatting
         
     | 
| 34 | 
         
            +
            │ ├── input_handler.py # Input validation
         
     | 
| 35 | 
         
            +
            │ ├── retriever.py # Web search with Tavily
         
     | 
| 36 | 
         
            +
            │ ├── server_cache.py # Redis caching
         
     | 
| 37 | 
         
            +
            │ ├── server_monitor.py # Server health monitoring
         
     | 
| 38 | 
         
            +
            │ ├── status_logger.py # Event logging
         
     | 
| 39 | 
         
            +
            │ ├── visualizer.py # Output rendering
         
     | 
| 40 | 
         
            +
            │ └── visualize_uptime.py # System uptime monitoring
         
     | 
| 41 | 
         
            +
            ├── tests/ # Unit tests
         
     | 
| 42 | 
         
            +
            ├── requirements.txt # Dependencies
         
     | 
| 43 | 
         
            +
            └── version.json # Version tracking
         
     | 
| 44 | 
         | 
| 
         | 
|
| 45 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 46 | 
         | 
| 47 | 
         
            +
            ## 🤖 AI Model Information
         
     | 
| 48 | 
         | 
| 49 | 
         
            +
            This assistant uses the **DavidAU/OpenAi-GPT-oss-20b-abliterated-uncensored-NEO-Imatrix-gguf** model hosted on Hugging Face Endpoints. This is a powerful open-source language model with:
         
     | 
| 50 | 
         | 
| 51 | 
         
            +
            - **20 Billion Parameters**: Capable of handling complex reasoning tasks
         
     | 
| 52 | 
         
            +
            - **Extended Context Window**: Supports up to 8192 tokens per response
         
     | 
| 53 | 
         
            +
            - **Uncensored Capabilities**: Provides comprehensive answers without artificial limitations
         
     | 
| 54 | 
         
            +
            - **Specialized Training**: Optimized for research and analytical tasks
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 55 | 
         | 
| 56 | 
         
            +
            ## 🔧 API Integrations
         
     | 
| 57 | 
         | 
| 58 | 
         
            +
            | Service | Purpose | Usage |
         
     | 
| 59 | 
         
            +
            |---------|---------|-------|
         
     | 
| 60 | 
         
            +
            | **Tavily** | Web Search | Real-time information retrieval |
         
     | 
| 61 | 
         
            +
            | **Hugging Face Inference** | LLM Processing | Natural language understanding |
         
     | 
| 62 | 
         
            +
            | **Redis** | Caching | Performance optimization |
         
     | 
| 63 | 
         
            +
            | **NASA** | Space Data | Astronomical context |
         
     | 
| 64 | 
         
            +
            | **OpenWeatherMap** | Weather Data | Environmental context |
         
     | 
| 65 | 
         | 
| 66 | 
         
            +
            ## ⚡ Enhanced Features
         
     | 
| 67 | 
         | 
| 68 | 
         
            +
            ### 🔁 Streaming Output
         
     | 
| 69 | 
         
            +
            Responses stream in real-time, allowing users to start reading before the complete answer is generated. This creates a more natural conversational experience.
         
     | 
| 70 | 
         
            +
             
     | 
| 71 | 
         
            +
            ### 📚 Dynamic Citations
         
     | 
| 72 | 
         
            +
            All information is properly sourced with clickable links to original content, ensuring transparency and enabling further exploration.
         
     | 
| 73 | 
         
            +
             
     | 
| 74 | 
         
            +
            ### ⚡ Asynchronous Operations
         
     | 
| 75 | 
         
            +
            Weather data, space weather, and web searches run in parallel, significantly reducing response times.
         
     | 
| 76 | 
         
            +
             
     | 
| 77 | 
         
            +
            ### 🧠 Contextual Intelligence
         
     | 
| 78 | 
         
            +
            Each query is enhanced with:
         
     | 
| 79 | 
         
            +
            - Current weather conditions
         
     | 
| 80 | 
         
            +
            - Recent space events
         
     | 
| 81 | 
         
            +
            - Accurate timestamps
         
     | 
| 82 | 
         
            +
             
     | 
| 83 | 
         
            +
            ### 🛡️ Server State Management
         
     | 
| 84 | 
         
            +
            Intelligent monitoring detects when the model server is initializing and provides clear user guidance with estimated wait times.
         
     | 
| 85 | 
         
            +
             
     | 
| 86 | 
         
            +
            ## 🚀 Getting Started
         
     | 
| 87 | 
         
            +
             
     | 
| 88 | 
         
            +
            ### Prerequisites
         
     | 
| 89 | 
         
            +
            - Python 3.8+
         
     | 
| 90 | 
         
            +
            - Hugging Face account and token
         
     | 
| 91 | 
         
            +
            - API keys for Tavily, NASA, and OpenWeatherMap
         
     | 
| 92 | 
         
            +
            - Redis instance for caching
         
     | 
| 93 | 
         
            +
             
     | 
| 94 | 
         
            +
            ### Setup Instructions
         
     | 
| 95 | 
         
             
            1. Clone the repository
         
     | 
| 96 | 
         
            +
            2. Set up required environment variables:
         
     | 
| 97 | 
         
            +
               ```bash
         
     | 
| 98 | 
         
            +
               export HF_TOKEN="your_hugging_face_token"
         
     | 
| 99 | 
         
            +
               export TAVILY_API_KEY="your_tavily_api_key"
         
     | 
| 100 | 
         
            +
               export REDIS_HOST="your_redis_host"
         
     | 
| 101 | 
         
            +
               export REDIS_PORT="your_redis_port"
         
     | 
| 102 | 
         
            +
               export REDIS_USERNAME="your_redis_username"
         
     | 
| 103 | 
         
            +
               export REDIS_PASSWORD="your_redis_password"
         
     | 
| 104 | 
         
            +
               export NASA_API_KEY="your_nasa_api_key"
         
     | 
| 105 | 
         
            +
               export OPENWEATHER_API_KEY="your_openweather_api_key"
         
     | 
| 106 | 
         
            +
            Install dependencies:
         
     | 
| 107 | 
         
            +
             
     | 
| 108 | 
         
            +
            pip install -r requirements.txt
         
     | 
| 109 | 
         
            +
            Run the application:
         
     | 
| 110 | 
         
            +
             
     | 
| 111 | 
         
            +
            python app.py
         
     | 
| 112 | 
         
            +
            📊 System Monitoring
         
     | 
| 113 | 
         
            +
            The assistant includes built-in monitoring capabilities:
         
     | 
| 114 | 
         | 
| 115 | 
         
            +
            Server Health Tracking: Detects and reports server state changes
         
     | 
| 116 | 
         
            +
            Performance Metrics: Logs request processing times
         
     | 
| 117 | 
         
            +
            Uptime Monitoring: Tracks system availability
         
     | 
| 118 | 
         
            +
            Failure Recovery: Automatic handling of transient errors
         
     | 
| 119 | 
         
            +
            📋 Example Queries
         
     | 
| 120 | 
         
            +
            Try these sample questions to see the assistant in action:
         
     | 
| 121 | 
         | 
| 122 | 
         
            +
            "What are the latest developments in fusion energy research?"
         
     | 
| 123 | 
         
            +
            "How does climate change impact global food security?"
         
     | 
| 124 | 
         
            +
            "Explain the significance of recent Mars rover discoveries"
         
     | 
| 125 | 
         
            +
            "What are the economic implications of AI advancement?"
         
     | 
| 126 | 
         
            +
            📄 License
         
     | 
| 127 | 
         
            +
            This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
         
     | 
| 128 | 
         | 
| 129 | 
         
            +
            🤝 Contributing
         
     | 
| 130 | 
         
            +
            Contributions are welcome! Please feel free to submit a Pull Request.
         
     | 
| 131 | 
         | 
| 132 | 
         
            +
            📞 Support
         
     | 
| 133 | 
         
            +
            For issues, questions, or feedback, please open an issue on the repository.
         
     | 
    	
        app.py
    CHANGED
    
    | 
         @@ -1,61 +1,151 @@ 
     | 
|
| 1 | 
         
             
            import gradio as gr
         
     | 
| 
         | 
|
| 2 | 
         
             
            from modules.input_handler import validate_input
         
     | 
| 3 | 
         
             
            from modules.retriever import perform_search
         
     | 
| 4 | 
         
             
            from modules.context_enhancer import add_weather_context, add_space_weather_context
         
     | 
| 5 | 
         
             
            from modules.analyzer import analyze_with_model
         
     | 
| 6 | 
         
             
            from modules.formatter import format_output
         
     | 
| 7 | 
         
            -
            from modules.citation import generate_citations
         
     | 
| 8 | 
         
            -
            from modules.visualizer import render_output
         
     | 
| 9 | 
         
             
            from modules.server_cache import get_cached_result, cache_result
         
     | 
| 10 | 
         
             
            from modules.status_logger import log_request
         
     | 
| 
         | 
|
| 11 | 
         | 
| 12 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 13 | 
         
             
                log_request("Research started", query=query)
         
     | 
| 14 | 
         | 
| 15 | 
         
            -
                # Check cache first
         
     | 
| 16 | 
         
             
                cached = get_cached_result(query)
         
     | 
| 17 | 
         
             
                if cached:
         
     | 
| 18 | 
         
             
                    log_request("Cache hit", query=query)
         
     | 
| 19 | 
         
            -
                     
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 20 | 
         | 
| 21 | 
         
            -
                #  
     | 
| 22 | 
         
            -
                 
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 23 | 
         | 
| 24 | 
         
            -
                 
     | 
| 25 | 
         
            -
                weather_data = add_weather_context()
         
     | 
| 26 | 
         
            -
                space_weather_data = add_space_weather_context()
         
     | 
| 27 | 
         | 
| 28 | 
         
            -
                 
     | 
| 29 | 
         
            -
                 
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 30 | 
         | 
| 31 | 
         
            -
                 
     | 
| 32 | 
         
            -
             
     | 
| 
         | 
|
| 33 | 
         | 
| 34 | 
         
            -
             
     | 
| 35 | 
         
            -
             
     | 
| 
         | 
|
| 36 | 
         | 
| 37 | 
         
            -
             
     | 
| 38 | 
         
            -
             
     | 
| 39 | 
         
            -
             
     | 
| 40 | 
         | 
| 41 | 
         
            -
             
     | 
| 42 | 
         
            -
             
     | 
| 
         | 
|
| 43 | 
         | 
| 44 | 
         
            -
                 
     | 
| 45 | 
         
            -
             
     | 
| 
         | 
|
| 46 | 
         | 
| 47 | 
         
            -
             
     | 
| 48 | 
         
            -
             
     | 
| 
         | 
|
| 49 | 
         | 
| 50 | 
         
            -
            # Gradio Interface
         
     | 
| 51 | 
         
            -
             
     | 
| 52 | 
         
            -
                 
     | 
| 53 | 
         
            -
                 
     | 
| 54 | 
         
            -
             
     | 
| 55 | 
         
            -
                 
     | 
| 56 | 
         
            -
                 
     | 
| 57 | 
         
            -
             
     | 
| 58 | 
         
            -
            )
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 59 | 
         | 
| 60 | 
         
             
            if __name__ == "__main__":
         
     | 
| 61 | 
         
             
                demo.launch()
         
     | 
| 
         | 
|
| 1 | 
         
             
            import gradio as gr
         
     | 
| 2 | 
         
            +
            import asyncio
         
     | 
| 3 | 
         
             
            from modules.input_handler import validate_input
         
     | 
| 4 | 
         
             
            from modules.retriever import perform_search
         
     | 
| 5 | 
         
             
            from modules.context_enhancer import add_weather_context, add_space_weather_context
         
     | 
| 6 | 
         
             
            from modules.analyzer import analyze_with_model
         
     | 
| 7 | 
         
             
            from modules.formatter import format_output
         
     | 
| 8 | 
         
            +
            from modules.citation import generate_citations, format_citations
         
     | 
| 
         | 
|
| 9 | 
         
             
            from modules.server_cache import get_cached_result, cache_result
         
     | 
| 10 | 
         
             
            from modules.status_logger import log_request
         
     | 
| 11 | 
         
            +
            from modules.server_monitor import ServerMonitor
         
     | 
| 12 | 
         | 
| 13 | 
         
            +
            server_monitor = ServerMonitor()
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
            async def research_assistant(query):
         
     | 
| 16 | 
         
             
                log_request("Research started", query=query)
         
     | 
| 17 | 
         | 
| 
         | 
|
| 18 | 
         
             
                cached = get_cached_result(query)
         
     | 
| 19 | 
         
             
                if cached:
         
     | 
| 20 | 
         
             
                    log_request("Cache hit", query=query)
         
     | 
| 21 | 
         
            +
                    yield cached
         
     | 
| 22 | 
         
            +
                    return
         
     | 
| 23 | 
         
            +
             
     | 
| 24 | 
         
            +
                try:
         
     | 
| 25 | 
         
            +
                    validated_query = validate_input(query)
         
     | 
| 26 | 
         
            +
                except ValueError as e:
         
     | 
| 27 | 
         
            +
                    yield f"⚠️ Input Error: {str(e)}"
         
     | 
| 28 | 
         
            +
                    return
         
     | 
| 29 | 
         
            +
             
     | 
| 30 | 
         
            +
                # Run context enhancement and search in parallel
         
     | 
| 31 | 
         
            +
                weather_task = asyncio.create_task(add_weather_context())
         
     | 
| 32 | 
         
            +
                space_weather_task = asyncio.create_task(add_space_weather_context())
         
     | 
| 33 | 
         
            +
                search_task = asyncio.create_task(asyncio.to_thread(perform_search, validated_query))
         
     | 
| 34 | 
         
            +
             
     | 
| 35 | 
         
            +
                weather_data = await weather_task
         
     | 
| 36 | 
         
            +
                space_weather_data = await space_weather_task
         
     | 
| 37 | 
         
            +
                search_results = await search_task
         
     | 
| 38 | 
         
            +
             
     | 
| 39 | 
         
            +
                # Handle search errors
         
     | 
| 40 | 
         
            +
                if isinstance(search_results, list) and len(search_results) > 0 and "error" in search_results[0]:
         
     | 
| 41 | 
         
            +
                    yield f"🔍 Search Error: {search_results[0]['error']}"
         
     | 
| 42 | 
         
            +
                    return
         
     | 
| 43 | 
         | 
| 44 | 
         
            +
                # Format search content for LLM
         
     | 
| 45 | 
         
            +
                search_content = ""
         
     | 
| 46 | 
         
            +
                answer_content = ""
         
     | 
| 47 | 
         
            +
                for result in search_results:
         
     | 
| 48 | 
         
            +
                    if result.get("type") == "answer":
         
     | 
| 49 | 
         
            +
                        answer_content = f"Direct Answer: {result['content']}\n\n"
         
     | 
| 50 | 
         
            +
                    elif result.get("type") == "source":
         
     | 
| 51 | 
         
            +
                        search_content += f"Source: {result['content']}\n\n"
         
     | 
| 52 | 
         | 
| 53 | 
         
            +
                enriched_input = f"{validated_query}\n\n{answer_content}Weather: {weather_data}\nSpace Weather: {space_weather_data}\n\nSearch Results:\n{search_content}"
         
     | 
| 
         | 
|
| 
         | 
|
| 54 | 
         | 
| 55 | 
         
            +
                server_status = server_monitor.check_server_status()
         
     | 
| 56 | 
         
            +
                if not server_status["available"]:
         
     | 
| 57 | 
         
            +
                    wait_time = server_status["estimated_wait"]
         
     | 
| 58 | 
         
            +
                    yield (
         
     | 
| 59 | 
         
            +
                        f"⏳ **Server Initializing** ⏳\n\n"
         
     | 
| 60 | 
         
            +
                        f"The AI model server is currently starting up. This happens automatically after periods of inactivity.\n\n"
         
     | 
| 61 | 
         
            +
                        f"**Estimated wait time: {wait_time} minutes**\n\n"
         
     | 
| 62 | 
         
            +
                        f"**What you can do:**\n"
         
     | 
| 63 | 
         
            +
                        f"- Wait for {wait_time} minutes and try again\n"
         
     | 
| 64 | 
         
            +
                        f"- Try a simpler query which might process faster\n"
         
     | 
| 65 | 
         
            +
                        f"- Check back shortly - the server will be ready soon!\n\n"
         
     | 
| 66 | 
         
            +
                        f"*Technical Details: {server_status['message']}*"
         
     | 
| 67 | 
         
            +
                    )
         
     | 
| 68 | 
         
            +
                    return
         
     | 
| 69 | 
         | 
| 70 | 
         
            +
                try:
         
     | 
| 71 | 
         
            +
                    stream = analyze_with_model(enriched_input)
         
     | 
| 72 | 
         
            +
                    full_response = ""
         
     | 
| 73 | 
         | 
| 74 | 
         
            +
                    for chunk in stream:
         
     | 
| 75 | 
         
            +
                        full_response += chunk
         
     | 
| 76 | 
         
            +
                        yield format_output(full_response)
         
     | 
| 77 | 
         | 
| 78 | 
         
            +
                    citations = generate_citations(search_results)
         
     | 
| 79 | 
         
            +
                    citation_text = format_citations(citations)
         
     | 
| 80 | 
         
            +
                    full_output = format_output(full_response) + citation_text
         
     | 
| 81 | 
         | 
| 82 | 
         
            +
                    cache_result(query, full_output)
         
     | 
| 83 | 
         
            +
                    server_monitor.report_success()
         
     | 
| 84 | 
         
            +
                    log_request("Research completed", result_length=len(full_output))
         
     | 
| 85 | 
         | 
| 86 | 
         
            +
                except Exception as e:
         
     | 
| 87 | 
         
            +
                    server_monitor.report_failure()
         
     | 
| 88 | 
         
            +
                    yield f"🤖 **Unexpected Error** 🤖\n\nAn unexpected error occurred:\n\n{str(e)}"
         
     | 
| 89 | 
         | 
| 90 | 
         
            +
            # Wrapper for Gradio
         
     | 
| 91 | 
         
            +
            def research_assistant_wrapper(query):
         
     | 
| 92 | 
         
            +
                return asyncio.run(research_assistant(query))
         
     | 
| 93 | 
         | 
| 94 | 
         
            +
            # Gradio Interface for Streaming
         
     | 
| 95 | 
         
            +
            with gr.Blocks(theme=gr.themes.Soft(), title="AI Research Assistant") as demo:
         
     | 
| 96 | 
         
            +
                gr.Markdown("# 🧠 AI Research Assistant")
         
     | 
| 97 | 
         
            +
                gr.Markdown("This advanced AI assistant combines web search with contextual awareness to answer complex questions. "
         
     | 
| 98 | 
         
            +
                            "It incorporates current weather and space weather data for richer context.")
         
     | 
| 99 | 
         
            +
                
         
     | 
| 100 | 
         
            +
                with gr.Row():
         
     | 
| 101 | 
         
            +
                    with gr.Column(scale=1):
         
     | 
| 102 | 
         
            +
                        gr.Markdown("## How to Use")
         
     | 
| 103 | 
         
            +
                        gr.Markdown("""
         
     | 
| 104 | 
         
            +
                        1. Enter a research question in the input box
         
     | 
| 105 | 
         
            +
                        2. Click Submit or press Enter
         
     | 
| 106 | 
         
            +
                        3. Watch as the response streams in real-time
         
     | 
| 107 | 
         
            +
                        4. Review sources at the end of each response
         
     | 
| 108 | 
         
            +
                        
         
     | 
| 109 | 
         
            +
                        ## Features
         
     | 
| 110 | 
         
            +
                        - 🔍 Web search integration
         
     | 
| 111 | 
         
            +
                        - 🌤️ Weather context
         
     | 
| 112 | 
         
            +
                        - 🌌 Space weather context
         
     | 
| 113 | 
         
            +
                        - 📚 Real-time citations
         
     | 
| 114 | 
         
            +
                        - ⚡ Streaming output
         
     | 
| 115 | 
         
            +
                        """)
         
     | 
| 116 | 
         
            +
                        
         
     | 
| 117 | 
         
            +
                    with gr.Column(scale=2):
         
     | 
| 118 | 
         
            +
                        chatbot = gr.Chatbot(height=500, label="Research Conversation")
         
     | 
| 119 | 
         
            +
                        msg = gr.Textbox(
         
     | 
| 120 | 
         
            +
                            label="Research Question",
         
     | 
| 121 | 
         
            +
                            placeholder="Ask a complex research question...",
         
     | 
| 122 | 
         
            +
                            lines=3
         
     | 
| 123 | 
         
            +
                        )
         
     | 
| 124 | 
         
            +
                        submit_btn = gr.Button("Submit Research Query")
         
     | 
| 125 | 
         
            +
                        clear_btn = gr.Button("Clear Conversation")
         
     | 
| 126 | 
         
            +
                        
         
     | 
| 127 | 
         
            +
                        examples = gr.Examples(
         
     | 
| 128 | 
         
            +
                            examples=[
         
     | 
| 129 | 
         
            +
                                "What are the latest developments in quantum computing?",
         
     | 
| 130 | 
         
            +
                                "How does climate change affect ocean currents?",
         
     | 
| 131 | 
         
            +
                                "Explain the significance of the James Webb Space Telescope findings",
         
     | 
| 132 | 
         
            +
                                "What are the economic implications of renewable energy adoption?",
         
     | 
| 133 | 
         
            +
                                "How do solar flares affect satellite communications?"
         
     | 
| 134 | 
         
            +
                            ],
         
     | 
| 135 | 
         
            +
                            inputs=msg,
         
     | 
| 136 | 
         
            +
                            label="Example Questions"
         
     | 
| 137 | 
         
            +
                        )
         
     | 
| 138 | 
         
            +
                
         
     | 
| 139 | 
         
            +
                def respond(message, chat_history):
         
     | 
| 140 | 
         
            +
                    bot_response = ""
         
     | 
| 141 | 
         
            +
                    for partial_response in research_assistant_wrapper(message):
         
     | 
| 142 | 
         
            +
                        bot_response = partial_response
         
     | 
| 143 | 
         
            +
                        yield bot_response
         
     | 
| 144 | 
         
            +
                
         
     | 
| 145 | 
         
            +
                submit_btn.click(respond, [msg, chatbot], chatbot)
         
     | 
| 146 | 
         
            +
                msg.submit(respond, [msg, chatbot], chatbot)
         
     | 
| 147 | 
         
            +
                
         
     | 
| 148 | 
         
            +
                clear_btn.click(lambda: None, None, chatbot, queue=False)
         
     | 
| 149 | 
         | 
| 150 | 
         
             
            if __name__ == "__main__":
         
     | 
| 151 | 
         
             
                demo.launch()
         
     | 
    	
        modules/analyzer.py
    CHANGED
    
    | 
         @@ -1,5 +1,6 @@ 
     | 
|
| 1 | 
         
             
            from openai import OpenAI
         
     | 
| 2 | 
         
             
            import os
         
     | 
| 
         | 
|
| 3 | 
         | 
| 4 | 
         
             
            client = OpenAI(
         
     | 
| 5 | 
         
             
                base_url="https://zxzbfrlg3ssrk7d9.us-east-1.aws.endpoints.huggingface.cloud/v1/",
         
     | 
| 
         @@ -7,14 +8,30 @@ client = OpenAI( 
     | 
|
| 7 | 
         
             
            )
         
     | 
| 8 | 
         | 
| 9 | 
         
             
            def analyze_with_model(prompt):
         
     | 
| 
         | 
|
| 10 | 
         
             
                try:
         
     | 
| 11 | 
         
             
                    response = client.chat.completions.create(
         
     | 
| 12 | 
         
             
                        model="DavidAU/OpenAi-GPT-oss-20b-abliterated-uncensored-NEO-Imatrix-gguf",
         
     | 
| 13 | 
         
             
                        messages=[{"role": "user", "content": prompt}],
         
     | 
| 14 | 
         
            -
                        stream= 
     | 
| 15 | 
         
             
                        temperature=0.7,
         
     | 
| 16 | 
         
            -
                        max_tokens= 
     | 
| 
         | 
|
| 17 | 
         
             
                    )
         
     | 
| 18 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 19 | 
         
             
                except Exception as e:
         
     | 
| 20 | 
         
            -
                     
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
             
            from openai import OpenAI
         
     | 
| 2 | 
         
             
            import os
         
     | 
| 3 | 
         
            +
            import time
         
     | 
| 4 | 
         | 
| 5 | 
         
             
            client = OpenAI(
         
     | 
| 6 | 
         
             
                base_url="https://zxzbfrlg3ssrk7d9.us-east-1.aws.endpoints.huggingface.cloud/v1/",
         
     | 
| 
         | 
|
| 8 | 
         
             
            )
         
     | 
| 9 | 
         | 
| 10 | 
         
             
            def analyze_with_model(prompt):
         
     | 
| 11 | 
         
            +
                """Analyze prompt with LLM, returning a generator for streaming"""
         
     | 
| 12 | 
         
             
                try:
         
     | 
| 13 | 
         
             
                    response = client.chat.completions.create(
         
     | 
| 14 | 
         
             
                        model="DavidAU/OpenAi-GPT-oss-20b-abliterated-uncensored-NEO-Imatrix-gguf",
         
     | 
| 15 | 
         
             
                        messages=[{"role": "user", "content": prompt}],
         
     | 
| 16 | 
         
            +
                        stream=True,  # Enable streaming
         
     | 
| 17 | 
         
             
                        temperature=0.7,
         
     | 
| 18 | 
         
            +
                        max_tokens=8192,  # Increased token limit
         
     | 
| 19 | 
         
            +
                        timeout=120  # Increased timeout for longer responses
         
     | 
| 20 | 
         
             
                    )
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
            +
                    for chunk in response:
         
     | 
| 23 | 
         
            +
                        content = chunk.choices[0].delta.content
         
     | 
| 24 | 
         
            +
                        if content:
         
     | 
| 25 | 
         
            +
                            yield content
         
     | 
| 26 | 
         
            +
                        time.sleep(0.01)  # Smooth out the stream
         
     | 
| 27 | 
         
            +
             
     | 
| 28 | 
         
             
                except Exception as e:
         
     | 
| 29 | 
         
            +
                    error_msg = str(e)
         
     | 
| 30 | 
         
            +
                    if "503" in error_msg:
         
     | 
| 31 | 
         
            +
                        yield f"Error during analysis: Service temporarily unavailable (503). The model server is likely initializing. Please wait 5 minutes and try again. Details: {error_msg}"
         
     | 
| 32 | 
         
            +
                    elif "timeout" in error_msg.lower():
         
     | 
| 33 | 
         
            +
                        yield f"Error during analysis: Request timed out. The model server may be initializing. Please wait 5 minutes and try again. Details: {error_msg}"
         
     | 
| 34 | 
         
            +
                    elif "connection" in error_msg.lower():
         
     | 
| 35 | 
         
            +
                        yield f"Error during analysis: Connection error. The model server may be initializing. Please wait 5 minutes and try again. Details: {error_msg}"
         
     | 
| 36 | 
         
            +
                    else:
         
     | 
| 37 | 
         
            +
                        yield f"Error during analysis: {error_msg}"
         
     | 
    	
        modules/citation.py
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 1 | 
         
            -
            import json
         
     | 
| 2 | 
         
            -
             
     | 
| 3 | 
         
             
            def generate_citations(search_results):
         
     | 
| 4 | 
         
            -
                """Generate citations from search results"""
         
     | 
| 5 | 
         
             
                try:
         
     | 
| 6 | 
         
            -
                     
     | 
| 7 | 
         
            -
                     
     | 
| 8 | 
         
            -
             
     | 
| 9 | 
         
            -
             
     | 
| 10 | 
         
            -
             
     | 
| 11 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 12 | 
         
             
                except Exception as e:
         
     | 
| 13 | 
         
             
                    return [{"error": f"Citation generation failed: {str(e)}"}]
         
     | 
| 14 | 
         | 
| 
         @@ -16,7 +16,7 @@ def format_citations(citations): 
     | 
|
| 16 | 
         
             
                """Format citations for display"""
         
     | 
| 17 | 
         
             
                if not citations:
         
     | 
| 18 | 
         
             
                    return ""
         
     | 
| 19 | 
         
            -
             
     | 
| 20 | 
         
             
                formatted = "\n\n**Sources:**\n"
         
     | 
| 21 | 
         
             
                for i, citation in enumerate(citations, 1):
         
     | 
| 22 | 
         
             
                    if "error" in citation:
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 1 | 
         
             
            def generate_citations(search_results):
         
     | 
| 2 | 
         
            +
                """Generate citations from structured search results"""
         
     | 
| 3 | 
         
             
                try:
         
     | 
| 4 | 
         
            +
                    citations = []
         
     | 
| 5 | 
         
            +
                    for result in search_results:
         
     | 
| 6 | 
         
            +
                        if result.get("type") == "source" and result.get("url"):
         
     | 
| 7 | 
         
            +
                            citations.append({
         
     | 
| 8 | 
         
            +
                                "source": result.get("title", "Unknown Source"),
         
     | 
| 9 | 
         
            +
                                "url": result.get("url")
         
     | 
| 10 | 
         
            +
                            })
         
     | 
| 11 | 
         
            +
                    return citations
         
     | 
| 12 | 
         
             
                except Exception as e:
         
     | 
| 13 | 
         
             
                    return [{"error": f"Citation generation failed: {str(e)}"}]
         
     | 
| 14 | 
         | 
| 
         | 
|
| 16 | 
         
             
                """Format citations for display"""
         
     | 
| 17 | 
         
             
                if not citations:
         
     | 
| 18 | 
         
             
                    return ""
         
     | 
| 19 | 
         
            +
             
     | 
| 20 | 
         
             
                formatted = "\n\n**Sources:**\n"
         
     | 
| 21 | 
         
             
                for i, citation in enumerate(citations, 1):
         
     | 
| 22 | 
         
             
                    if "error" in citation:
         
     | 
    	
        modules/context_enhancer.py
    CHANGED
    
    | 
         @@ -1,41 +1,37 @@ 
     | 
|
| 1 | 
         
            -
            import  
     | 
| 2 | 
         
             
            import os
         
     | 
| 3 | 
         
             
            from datetime import datetime
         
     | 
| 4 | 
         | 
| 5 | 
         
            -
            def add_weather_context(location="London"):
         
     | 
| 6 | 
         
            -
                """Add current weather context to the query"""
         
     | 
| 7 | 
         
             
                try:
         
     | 
| 8 | 
         
             
                    api_key = os.getenv("OPENWEATHER_API_KEY")
         
     | 
| 9 | 
         
             
                    if not api_key:
         
     | 
| 10 | 
         
             
                        return "Weather data unavailable (API key not configured)"
         
     | 
| 11 | 
         
            -
             
     | 
| 12 | 
         
             
                    url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric"
         
     | 
| 13 | 
         
            -
                     
     | 
| 14 | 
         
            -
             
     | 
| 15 | 
         
            -
             
     | 
| 16 | 
         
            -
             
     | 
| 17 | 
         
            -
             
     | 
| 18 | 
         
             
                except Exception as e:
         
     | 
| 19 | 
         
             
                    return f"Weather data unavailable: {str(e)}"
         
     | 
| 20 | 
         | 
| 21 | 
         
            -
            def add_space_weather_context():
         
     | 
| 22 | 
         
            -
                """Add space weather context to the query"""
         
     | 
| 23 | 
         
             
                try:
         
     | 
| 24 | 
         
             
                    api_key = os.getenv("NASA_API_KEY")
         
     | 
| 25 | 
         
             
                    if not api_key:
         
     | 
| 26 | 
         
             
                        return "Space weather data unavailable (API key not configured)"
         
     | 
| 27 | 
         
            -
             
     | 
| 28 | 
         
            -
                    # Using a different NASA endpoint that doesn't require parameters
         
     | 
| 29 | 
         
             
                    url = f"https://api.nasa.gov/planetary/apod?api_key={api_key}"
         
     | 
| 30 | 
         
            -
                     
     | 
| 31 | 
         
            -
             
     | 
| 32 | 
         
            -
             
     | 
| 33 | 
         
            -
             
     | 
| 34 | 
         
            -
             
     | 
| 35 | 
         
             
                except Exception as e:
         
     | 
| 36 | 
         
             
                    return f"Space weather data unavailable: {str(e)}"
         
     | 
| 37 | 
         | 
| 38 | 
         
             
            def add_time_context():
         
     | 
| 39 | 
         
            -
                """Add current time context"""
         
     | 
| 40 | 
         
             
                now = datetime.now()
         
     | 
| 41 | 
         
             
                return f"Current date and time: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}"
         
     | 
| 
         | 
|
| 1 | 
         
            +
            import aiohttp
         
     | 
| 2 | 
         
             
            import os
         
     | 
| 3 | 
         
             
            from datetime import datetime
         
     | 
| 4 | 
         | 
| 5 | 
         
            +
            async def add_weather_context(location="London"):
         
     | 
| 
         | 
|
| 6 | 
         
             
                try:
         
     | 
| 7 | 
         
             
                    api_key = os.getenv("OPENWEATHER_API_KEY")
         
     | 
| 8 | 
         
             
                    if not api_key:
         
     | 
| 9 | 
         
             
                        return "Weather data unavailable (API key not configured)"
         
     | 
| 10 | 
         
            +
             
     | 
| 11 | 
         
             
                    url = f"http://api.openweathermap.org/data/2.5/weather?q={location}&appid={api_key}&units=metric"
         
     | 
| 12 | 
         
            +
                    async with aiohttp.ClientSession() as session:
         
     | 
| 13 | 
         
            +
                        async with session.get(url, timeout=5) as response:
         
     | 
| 14 | 
         
            +
                            response.raise_for_status()
         
     | 
| 15 | 
         
            +
                            data = await response.json()
         
     | 
| 16 | 
         
            +
                            return f"Current weather in {location}: {data['weather'][0]['description']}, {data['main']['temp']}°C"
         
     | 
| 17 | 
         
             
                except Exception as e:
         
     | 
| 18 | 
         
             
                    return f"Weather data unavailable: {str(e)}"
         
     | 
| 19 | 
         | 
| 20 | 
         
            +
            async def add_space_weather_context():
         
     | 
| 
         | 
|
| 21 | 
         
             
                try:
         
     | 
| 22 | 
         
             
                    api_key = os.getenv("NASA_API_KEY")
         
     | 
| 23 | 
         
             
                    if not api_key:
         
     | 
| 24 | 
         
             
                        return "Space weather data unavailable (API key not configured)"
         
     | 
| 25 | 
         
            +
             
     | 
| 
         | 
|
| 26 | 
         
             
                    url = f"https://api.nasa.gov/planetary/apod?api_key={api_key}"
         
     | 
| 27 | 
         
            +
                    async with aiohttp.ClientSession() as session:
         
     | 
| 28 | 
         
            +
                        async with session.get(url, timeout=5) as response:
         
     | 
| 29 | 
         
            +
                            response.raise_for_status()
         
     | 
| 30 | 
         
            +
                            data = await response.json()
         
     | 
| 31 | 
         
            +
                            return f"Space context: Astronomy Picture of the Day - {data.get('title', 'N/A')}"
         
     | 
| 32 | 
         
             
                except Exception as e:
         
     | 
| 33 | 
         
             
                    return f"Space weather data unavailable: {str(e)}"
         
     | 
| 34 | 
         | 
| 35 | 
         
             
            def add_time_context():
         
     | 
| 
         | 
|
| 36 | 
         
             
                now = datetime.now()
         
     | 
| 37 | 
         
             
                return f"Current date and time: {now.strftime('%Y-%m-%d %H:%M:%S %Z')}"
         
     | 
    	
        modules/retriever.py
    CHANGED
    
    | 
         @@ -4,25 +4,31 @@ import os 
     | 
|
| 4 | 
         
             
            tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
         
     | 
| 5 | 
         | 
| 6 | 
         
             
            def perform_search(query):
         
     | 
| 7 | 
         
            -
                """Perform web search using Tavily API"""
         
     | 
| 8 | 
         
             
                try:
         
     | 
| 9 | 
         
             
                    if not os.getenv("TAVILY_API_KEY"):
         
     | 
| 10 | 
         
            -
                        return " 
     | 
| 11 | 
         
            -
             
     | 
| 12 | 
         
             
                    response = tavily.search(
         
     | 
| 13 | 
         
            -
                        query=query, 
     | 
| 14 | 
         
             
                        max_results=5,
         
     | 
| 15 | 
         
             
                        include_answer=True,
         
     | 
| 16 | 
         
             
                        include_raw_content=False
         
     | 
| 17 | 
         
             
                    )
         
     | 
| 18 | 
         
            -
             
     | 
| 19 | 
         
             
                    results = []
         
     | 
| 20 | 
         
             
                    if response.get('answer'):
         
     | 
| 21 | 
         
            -
                        results.append( 
     | 
| 22 | 
         
            -
             
     | 
| 23 | 
         
             
                    for result in response.get('results', []):
         
     | 
| 24 | 
         
            -
                        results.append( 
     | 
| 25 | 
         
            -
             
     | 
| 26 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 27 | 
         
             
                except Exception as e:
         
     | 
| 28 | 
         
            -
                    return f"Search failed: {str(e)}"
         
     | 
| 
         | 
|
| 4 | 
         
             
            tavily = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
         
     | 
| 5 | 
         | 
| 6 | 
         
             
            def perform_search(query):
         
     | 
| 7 | 
         
            +
                """Perform web search using Tavily API and return structured results"""
         
     | 
| 8 | 
         
             
                try:
         
     | 
| 9 | 
         
             
                    if not os.getenv("TAVILY_API_KEY"):
         
     | 
| 10 | 
         
            +
                        return [{"error": "API key not configured"}]
         
     | 
| 11 | 
         
            +
             
     | 
| 12 | 
         
             
                    response = tavily.search(
         
     | 
| 13 | 
         
            +
                        query=query,
         
     | 
| 14 | 
         
             
                        max_results=5,
         
     | 
| 15 | 
         
             
                        include_answer=True,
         
     | 
| 16 | 
         
             
                        include_raw_content=False
         
     | 
| 17 | 
         
             
                    )
         
     | 
| 18 | 
         
            +
             
     | 
| 19 | 
         
             
                    results = []
         
     | 
| 20 | 
         
             
                    if response.get('answer'):
         
     | 
| 21 | 
         
            +
                        results.append({"type": "answer", "content": response['answer']})
         
     | 
| 22 | 
         
            +
             
     | 
| 23 | 
         
             
                    for result in response.get('results', []):
         
     | 
| 24 | 
         
            +
                        results.append({
         
     | 
| 25 | 
         
            +
                            "type": "source",
         
     | 
| 26 | 
         
            +
                            "title": result.get("title"),
         
     | 
| 27 | 
         
            +
                            "url": result.get("url"),
         
     | 
| 28 | 
         
            +
                            "content": result.get("content")
         
     | 
| 29 | 
         
            +
                        })
         
     | 
| 30 | 
         
            +
             
     | 
| 31 | 
         
            +
                    return results
         
     | 
| 32 | 
         
            +
             
     | 
| 33 | 
         
             
                except Exception as e:
         
     | 
| 34 | 
         
            +
                    return [{"error": f"Search failed: {str(e)}"}]
         
     | 
    	
        modules/server_monitor.py
    ADDED
    
    | 
         @@ -0,0 +1,167 @@ 
     | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
| 
         | 
|
| 1 | 
         
            +
            import redis
         
     | 
| 2 | 
         
            +
            import os
         
     | 
| 3 | 
         
            +
            import time
         
     | 
| 4 | 
         
            +
            from datetime import datetime, timedelta
         
     | 
| 5 | 
         
            +
             
     | 
| 6 | 
         
            +
            class ServerMonitor:
         
     | 
| 7 | 
         
            +
                def __init__(self):
         
     | 
| 8 | 
         
            +
                    try:
         
     | 
| 9 | 
         
            +
                        self.redis_client = redis.Redis(
         
     | 
| 10 | 
         
            +
                            host=os.getenv("REDIS_HOST", "localhost"),
         
     | 
| 11 | 
         
            +
                            port=int(os.getenv("REDIS_PORT", 6379)),
         
     | 
| 12 | 
         
            +
                            username=os.getenv("REDIS_USERNAME"),
         
     | 
| 13 | 
         
            +
                            password=os.getenv("REDIS_PASSWORD"),
         
     | 
| 14 | 
         
            +
                            decode_responses=True
         
     | 
| 15 | 
         
            +
                        )
         
     | 
| 16 | 
         
            +
                        # Test connection
         
     | 
| 17 | 
         
            +
                        self.redis_client.ping()
         
     | 
| 18 | 
         
            +
                        self.connected = True
         
     | 
| 19 | 
         
            +
                    except Exception:
         
     | 
| 20 | 
         
            +
                        self.redis_client = None
         
     | 
| 21 | 
         
            +
                        self.connected = False
         
     | 
| 22 | 
         
            +
                
         
     | 
| 23 | 
         
            +
                def report_failure(self):
         
     | 
| 24 | 
         
            +
                    """Report a server failure (e.g., 503 error)"""
         
     | 
| 25 | 
         
            +
                    if not self.connected:
         
     | 
| 26 | 
         
            +
                        return
         
     | 
| 27 | 
         
            +
                        
         
     | 
| 28 | 
         
            +
                    try:
         
     | 
| 29 | 
         
            +
                        # Increment failure counter
         
     | 
| 30 | 
         
            +
                        key = f"server_failures:{datetime.now().strftime('%Y-%m-%d:%H')}"
         
     | 
| 31 | 
         
            +
                        self.redis_client.incr(key)
         
     | 
| 32 | 
         
            +
                        self.redis_client.expire(key, 3600)  # Expire in 1 hour
         
     | 
| 33 | 
         
            +
                        
         
     | 
| 34 | 
         
            +
                        # Record last failure time
         
     | 
| 35 | 
         
            +
                        self.redis_client.set("last_failure", datetime.now().isoformat())
         
     | 
| 36 | 
         
            +
                        self.redis_client.expire("last_failure", 86400)  # Expire in 24 hours
         
     | 
| 37 | 
         
            +
                    except Exception:
         
     | 
| 38 | 
         
            +
                        pass  # Silently fail to avoid breaking the main app
         
     | 
| 39 | 
         
            +
                
         
     | 
| 40 | 
         
            +
                def report_success(self):
         
     | 
| 41 | 
         
            +
                    """Report a successful request"""
         
     | 
| 42 | 
         
            +
                    if not self.connected:
         
     | 
| 43 | 
         
            +
                        return
         
     | 
| 44 | 
         
            +
                        
         
     | 
| 45 | 
         
            +
                    try:
         
     | 
| 46 | 
         
            +
                        # Reset failure counter for current hour
         
     | 
| 47 | 
         
            +
                        key = f"server_failures:{datetime.now().strftime('%Y-%m-%d:%H')}"
         
     | 
| 48 | 
         
            +
                        self.redis_client.delete(key)
         
     | 
| 49 | 
         
            +
                        
         
     | 
| 50 | 
         
            +
                        # Record last success time
         
     | 
| 51 | 
         
            +
                        self.redis_client.set("last_success", datetime.now().isoformat())
         
     | 
| 52 | 
         
            +
                        self.redis_client.expire("last_success", 86400)  # Expire in 24 hours
         
     | 
| 53 | 
         
            +
                    except Exception:
         
     | 
| 54 | 
         
            +
                        pass  # Silently fail to avoid breaking the main app
         
     | 
| 55 | 
         
            +
                
         
     | 
| 56 | 
         
            +
                def check_server_status(self):
         
     | 
| 57 | 
         
            +
                    """Check if server is likely available based on recent activity"""
         
     | 
| 58 | 
         
            +
                    if not self.connected:
         
     | 
| 59 | 
         
            +
                        return {"available": True, "message": "Redis not configured, assuming server available"}
         
     | 
| 60 | 
         
            +
                    
         
     | 
| 61 | 
         
            +
                    try:
         
     | 
| 62 | 
         
            +
                        # Get recent failures
         
     | 
| 63 | 
         
            +
                        now = datetime.now()
         
     | 
| 64 | 
         
            +
                        failures_last_hour = 0
         
     | 
| 65 | 
         
            +
                        
         
     | 
| 66 | 
         
            +
                        # Check current and previous hour
         
     | 
| 67 | 
         
            +
                        for i in range(2):
         
     | 
| 68 | 
         
            +
                            check_time = now - timedelta(hours=i)
         
     | 
| 69 | 
         
            +
                            key = f"server_failures:{check_time.strftime('%Y-%m-%d:%H')}"
         
     | 
| 70 | 
         
            +
                            failures = self.redis_client.get(key)
         
     | 
| 71 | 
         
            +
                            if failures:
         
     | 
| 72 | 
         
            +
                                failures_last_hour += int(failures)
         
     | 
| 73 | 
         
            +
                        
         
     | 
| 74 | 
         
            +
                        # Get last failure time
         
     | 
| 75 | 
         
            +
                        last_failure_str = self.redis_client.get("last_failure")
         
     | 
| 76 | 
         
            +
                        last_success_str = self.redis_client.get("last_success")
         
     | 
| 77 | 
         
            +
                        
         
     | 
| 78 | 
         
            +
                        # If we had recent failures but no recent success, server might be down
         
     | 
| 79 | 
         
            +
                        if failures_last_hour > 3:
         
     | 
| 80 | 
         
            +
                            if last_success_str:
         
     | 
| 81 | 
         
            +
                                last_success = datetime.fromisoformat(last_success_str)
         
     | 
| 82 | 
         
            +
                                minutes_since_success = (now - last_success).total_seconds() / 60
         
     | 
| 83 | 
         
            +
                                if minutes_since_success < 15:
         
     | 
| 84 | 
         
            +
                                    return {
         
     | 
| 85 | 
         
            +
                                        "available": True, 
         
     | 
| 86 | 
         
            +
                                        "message": "Recent success detected, server likely available",
         
     | 
| 87 | 
         
            +
                                        "estimated_wait": 0
         
     | 
| 88 | 
         
            +
                                    }
         
     | 
| 89 | 
         
            +
                            
         
     | 
| 90 | 
         
            +
                            # Estimate wait time based on typical warmup
         
     | 
| 91 | 
         
            +
                            return {
         
     | 
| 92 | 
         
            +
                                "available": False, 
         
     | 
| 93 | 
         
            +
                                "message": f"High failure rate detected ({failures_last_hour} failures recently)",
         
     | 
| 94 | 
         
            +
                                "estimated_wait": 5
         
     | 
| 95 | 
         
            +
                            }
         
     | 
| 96 | 
         
            +
                        
         
     | 
| 97 | 
         
            +
                        # If we had a very recent failure (< 5 mins), suggest waiting
         
     | 
| 98 | 
         
            +
                        if last_failure_str:
         
     | 
| 99 | 
         
            +
                            last_failure = datetime.fromisoformat(last_failure_str)
         
     | 
| 100 | 
         
            +
                            minutes_since_failure = (now - last_failure).total_seconds() / 60
         
     | 
| 101 | 
         
            +
                            if minutes_since_failure < 5:
         
     | 
| 102 | 
         
            +
                                return {
         
     | 
| 103 | 
         
            +
                                    "available": False, 
         
     | 
| 104 | 
         
            +
                                    "message": f"Recent failure {int(minutes_since_failure)} minutes ago",
         
     | 
| 105 | 
         
            +
                                    "estimated_wait": max(1, 5 - int(minutes_since_failure))
         
     | 
| 106 | 
         
            +
                                }
         
     | 
| 107 | 
         
            +
                        
         
     | 
| 108 | 
         
            +
                        return {
         
     | 
| 109 | 
         
            +
                            "available": True, 
         
     | 
| 110 | 
         
            +
                            "message": "Server appears to be available",
         
     | 
| 111 | 
         
            +
                            "estimated_wait": 0
         
     | 
| 112 | 
         
            +
                        }
         
     | 
| 113 | 
         
            +
                        
         
     | 
| 114 | 
         
            +
                    except Exception as e:
         
     | 
| 115 | 
         
            +
                        # On any Redis error, assume server is available
         
     | 
| 116 | 
         
            +
                        return {
         
     | 
| 117 | 
         
            +
                            "available": True, 
         
     | 
| 118 | 
         
            +
                            "message": f"Monitoring check failed: {str(e)}, assuming server available",
         
     | 
| 119 | 
         
            +
                            "estimated_wait": 0
         
     | 
| 120 | 
         
            +
                        }
         
     | 
| 121 | 
         
            +
                
         
     | 
| 122 | 
         
            +
                def get_system_stats(self):
         
     | 
| 123 | 
         
            +
                    """Get detailed system statistics"""
         
     | 
| 124 | 
         
            +
                    if not self.connected:
         
     | 
| 125 | 
         
            +
                        return {"error": "Redis not configured"}
         
     | 
| 126 | 
         
            +
                    
         
     | 
| 127 | 
         
            +
                    try:
         
     | 
| 128 | 
         
            +
                        stats = {}
         
     | 
| 129 | 
         
            +
                        
         
     | 
| 130 | 
         
            +
                        # Get recent failures
         
     | 
| 131 | 
         
            +
                        now = datetime.now()
         
     | 
| 132 | 
         
            +
                        total_failures = 0
         
     | 
| 133 | 
         
            +
                        for i in range(24):  # Last 24 hours
         
     | 
| 134 | 
         
            +
                            check_time = now - timedelta(hours=i)
         
     | 
| 135 | 
         
            +
                            key = f"server_failures:{check_time.strftime('%Y-%m-%d:%H')}"
         
     | 
| 136 | 
         
            +
                            failures = self.redis_client.get(key)
         
     | 
| 137 | 
         
            +
                            if failures:
         
     | 
| 138 | 
         
            +
                                total_failures += int(failures)
         
     | 
| 139 | 
         
            +
                        
         
     | 
| 140 | 
         
            +
                        stats["failures_last_24h"] = total_failures
         
     | 
| 141 | 
         
            +
                        
         
     | 
| 142 | 
         
            +
                        # Get last events
         
     | 
| 143 | 
         
            +
                        last_failure = self.redis_client.get("last_failure")
         
     | 
| 144 | 
         
            +
                        last_success = self.redis_client.get("last_success")
         
     | 
| 145 | 
         
            +
                        
         
     | 
| 146 | 
         
            +
                        stats["last_failure"] = last_failure if last_failure else "None recorded"
         
     | 
| 147 | 
         
            +
                        stats["last_success"] = last_success if last_success else "None recorded"
         
     | 
| 148 | 
         
            +
                        
         
     | 
| 149 | 
         
            +
                        # Calculate uptime percentage (approximate)
         
     | 
| 150 | 
         
            +
                        if last_failure and last_success:
         
     | 
| 151 | 
         
            +
                            failure_time = datetime.fromisoformat(last_failure)
         
     | 
| 152 | 
         
            +
                            success_time = datetime.fromisoformat(last_success)
         
     | 
| 153 | 
         
            +
                            if success_time > failure_time:
         
     | 
| 154 | 
         
            +
                                stats["status"] = "Operational"
         
     | 
| 155 | 
         
            +
                            else:
         
     | 
| 156 | 
         
            +
                                stats["status"] = "Degraded"
         
     | 
| 157 | 
         
            +
                        elif last_success:
         
     | 
| 158 | 
         
            +
                            stats["status"] = "Operational"
         
     | 
| 159 | 
         
            +
                        elif last_failure:
         
     | 
| 160 | 
         
            +
                            stats["status"] = "Issues Detected"
         
     | 
| 161 | 
         
            +
                        else:
         
     | 
| 162 | 
         
            +
                            stats["status"] = "Unknown"
         
     | 
| 163 | 
         
            +
                            
         
     | 
| 164 | 
         
            +
                        return stats
         
     | 
| 165 | 
         
            +
                        
         
     | 
| 166 | 
         
            +
                    except Exception as e:
         
     | 
| 167 | 
         
            +
                        return {"error": str(e)}
         
     | 
    	
        requirements.txt
    CHANGED
    
    | 
         @@ -2,5 +2,6 @@ gradio==4.38.1 
     | 
|
| 2 | 
         
             
            openai
         
     | 
| 3 | 
         
             
            tavily-python
         
     | 
| 4 | 
         
             
            redis
         
     | 
| 
         | 
|
| 5 | 
         
             
            requests
         
     | 
| 6 | 
         
             
            python-dotenv
         
     | 
| 
         | 
|
| 2 | 
         
             
            openai
         
     | 
| 3 | 
         
             
            tavily-python
         
     | 
| 4 | 
         
             
            redis
         
     | 
| 5 | 
         
            +
            aiohttp
         
     | 
| 6 | 
         
             
            requests
         
     | 
| 7 | 
         
             
            python-dotenv
         
     | 
    	
        version.json
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 1 | 
         
             
            {
         
     | 
| 2 | 
         
            -
             
     | 
| 3 | 
         
            -
             
     | 
| 4 | 
         
             
            }
         
     | 
| 
         | 
|
| 1 | 
         
             
            {
         
     | 
| 2 | 
         
            +
            "version": "1.0.0",
         
     | 
| 3 | 
         
            +
            "description": "Initial modular architecture with Redis, weather, and space weather integration"
         
     | 
| 4 | 
         
             
            }
         
     |