Dhruv Pawar
		
	commited on
		
		
					Commit 
							
							Β·
						
						92a25bc
	
1
								Parent(s):
							
							2287eaf
								
Enhanced UI: Added collapsible sidebar toggle, increased chat area size, fixed streaming bugs
Browse files
    	
        config.py
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ import logging 
     | 
|
| 2 | 
         
             
            from pathlib import Path
         
     | 
| 3 | 
         
             
            from enum import Enum
         
     | 
| 4 | 
         
             
            from logging.handlers import RotatingFileHandler
         
     | 
| 
         | 
|
| 5 | 
         | 
| 6 | 
         
             
            def setup_logging():
         
     | 
| 7 | 
         
             
                """Setup advanced logging with rotation"""
         
     | 
| 
         @@ -40,29 +41,29 @@ logger = setup_logging() 
     | 
|
| 40 | 
         | 
| 41 | 
         
             
            class AppConfig:
         
     | 
| 42 | 
         
             
                """Centralized application configuration"""
         
     | 
| 43 | 
         
            -
                MAX_HISTORY_LENGTH: int = 10
         
     | 
| 44 | 
         
            -
                MAX_CONVERSATION_STORAGE: int = 1000
         
     | 
| 45 | 
         
            -
                DEFAULT_TEMPERATURE: float = 0.7
         
     | 
| 46 | 
         
            -
                MIN_TEMPERATURE: float = 0.0
         
     | 
| 47 | 
         
            -
                MAX_TEMPERATURE: float = 2.0
         
     | 
| 48 | 
         
            -
                DEFAULT_MAX_TOKENS: int = 4000
         
     | 
| 49 | 
         
            -
                MIN_TOKENS: int = 100
         
     | 
| 50 | 
         
            -
                MAX_TOKENS: int = 32000
         
     | 
| 51 | 
         
            -
                REQUEST_TIMEOUT: int = 60
         
     | 
| 52 | 
         
            -
                MAX_RETRIES: int = 3
         
     | 
| 53 | 
         
            -
                RETRY_DELAY: float = 1.0
         
     | 
| 54 | 
         
            -
                CACHE_SIZE: int = 100
         
     | 
| 55 | 
         
            -
                CACHE_TTL: int = 3600
         
     | 
| 56 | 
         
            -
                RATE_LIMIT_REQUESTS: int = 50
         
     | 
| 57 | 
         
            -
                RATE_LIMIT_WINDOW: int = 60
         
     | 
| 58 | 
         
            -
                EXPORT_DIR: Path = Path("exports")
         
     | 
| 59 | 
         
            -
                BACKUP_DIR: Path = Path("backups")
         
     | 
| 60 | 
         
            -
                MAX_EXPORT_SIZE_MB: int = 50
         
     | 
| 61 | 
         
            -
                THEME_PRIMARY: str = "purple"
         
     | 
| 62 | 
         
            -
                THEME_SECONDARY: str = "blue"
         
     | 
| 63 | 
         
            -
                AUTO_SAVE_INTERVAL: int = 300
         
     | 
| 64 | 
         
            -
                ENABLE_ANALYTICS: bool = True
         
     | 
| 65 | 
         
            -
                ANALYTICS_BATCH_SIZE: int = 10
         
     | 
| 66 | 
         | 
| 67 | 
         
             
                @classmethod
         
     | 
| 68 | 
         
             
                def validate(cls) -> bool:
         
     | 
| 
         @@ -70,6 +71,7 @@ class AppConfig: 
     | 
|
| 70 | 
         
             
                        assert cls.MIN_TEMPERATURE <= cls.DEFAULT_TEMPERATURE <= cls.MAX_TEMPERATURE
         
     | 
| 71 | 
         
             
                        assert cls.MIN_TOKENS <= cls.DEFAULT_MAX_TOKENS <= cls.MAX_TOKENS
         
     | 
| 72 | 
         
             
                        assert cls.MAX_HISTORY_LENGTH > 0
         
     | 
| 
         | 
|
| 73 | 
         
             
                        return True
         
     | 
| 74 | 
         
             
                    except AssertionError as e:
         
     | 
| 75 | 
         
             
                        logger.error(f"Configuration validation failed: {e}")
         
     | 
| 
         @@ -77,9 +79,12 @@ class AppConfig: 
     | 
|
| 77 | 
         | 
| 78 | 
         
             
                @classmethod
         
     | 
| 79 | 
         
             
                def create_directories(cls) -> None:
         
     | 
| 80 | 
         
            -
                     
     | 
| 81 | 
         
            -
             
     | 
| 82 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 83 | 
         | 
| 84 | 
         
             
            AppConfig.create_directories()
         
     | 
| 85 | 
         
             
            AppConfig.validate()
         
     | 
| 
         @@ -185,10 +190,13 @@ CUSTOM_CSS = """ 
     | 
|
| 185 | 
         
             
                margin: 1rem 0;
         
     | 
| 186 | 
         
             
                font-family: 'JetBrains Mono', monospace;
         
     | 
| 187 | 
         
             
                transition: var(--transition);
         
     | 
| 188 | 
         
            -
                color: #2c3e50 !important;
         
     | 
| 189 | 
         
             
                box-shadow: 0 2px 8px rgba(0,0,0,0.08);
         
     | 
| 190 | 
         
             
            }
         
     | 
| 191 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 192 | 
         
             
            .metrics-card strong {
         
     | 
| 193 | 
         
             
                color: #1a1a1a !important;
         
     | 
| 194 | 
         
             
                font-weight: 600;
         
     | 
| 
         @@ -208,19 +216,20 @@ CUSTOM_CSS = """ 
     | 
|
| 208 | 
         
             
                box-shadow: var(--shadow-lg);
         
     | 
| 209 | 
         
             
            }
         
     | 
| 210 | 
         | 
| 211 | 
         
            -
            .analytics-panel  
     | 
| 212 | 
         
             
                color: white !important;
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 213 | 
         
             
                margin-bottom: 1rem;
         
     | 
| 214 | 
         
             
                font-size: 1.5rem;
         
     | 
| 215 | 
         
             
            }
         
     | 
| 216 | 
         | 
| 217 | 
         
             
            .analytics-panel p {
         
     | 
| 218 | 
         
            -
                color: rgba(255,255,255,0.95) !important;
         
     | 
| 219 | 
         
             
                line-height: 1.6;
         
     | 
| 220 | 
         
             
            }
         
     | 
| 221 | 
         | 
| 222 | 
         
             
            .analytics-panel strong {
         
     | 
| 223 | 
         
            -
                color: white !important;
         
     | 
| 224 | 
         
             
                font-weight: 600;
         
     | 
| 225 | 
         
             
            }
         
     | 
| 226 | 
         | 
| 
         @@ -258,14 +267,6 @@ CUSTOM_CSS = """ 
     | 
|
| 258 | 
         
             
            .gr-button:hover { 
         
     | 
| 259 | 
         
             
                transform: translateY(-2px) !important; 
         
     | 
| 260 | 
         
             
            }
         
     | 
| 261 | 
         
            -
             
     | 
| 262 | 
         
            -
            .gr-markdown {
         
     | 
| 263 | 
         
            -
                color: #2c3e50 !important;
         
     | 
| 264 | 
         
            -
            }
         
     | 
| 265 | 
         
            -
             
     | 
| 266 | 
         
            -
            .gr-markdown strong {
         
     | 
| 267 | 
         
            -
                color: #1a1a1a !important;
         
     | 
| 268 | 
         
            -
            }
         
     | 
| 269 | 
         
             
            """
         
     | 
| 270 | 
         | 
| 271 | 
         
            -
            logger.info("Enhanced configuration initialized")
         
     | 
| 
         | 
|
| 2 | 
         
             
            from pathlib import Path
         
     | 
| 3 | 
         
             
            from enum import Enum
         
     | 
| 4 | 
         
             
            from logging.handlers import RotatingFileHandler
         
     | 
| 5 | 
         
            +
            from typing import ClassVar
         
     | 
| 6 | 
         | 
| 7 | 
         
             
            def setup_logging():
         
     | 
| 8 | 
         
             
                """Setup advanced logging with rotation"""
         
     | 
| 
         | 
|
| 41 | 
         | 
| 42 | 
         
             
            class AppConfig:
         
     | 
| 43 | 
         
             
                """Centralized application configuration"""
         
     | 
| 44 | 
         
            +
                MAX_HISTORY_LENGTH: ClassVar[int] = 10
         
     | 
| 45 | 
         
            +
                MAX_CONVERSATION_STORAGE: ClassVar[int] = 1000
         
     | 
| 46 | 
         
            +
                DEFAULT_TEMPERATURE: ClassVar[float] = 0.7
         
     | 
| 47 | 
         
            +
                MIN_TEMPERATURE: ClassVar[float] = 0.0
         
     | 
| 48 | 
         
            +
                MAX_TEMPERATURE: ClassVar[float] = 2.0
         
     | 
| 49 | 
         
            +
                DEFAULT_MAX_TOKENS: ClassVar[int] = 4000
         
     | 
| 50 | 
         
            +
                MIN_TOKENS: ClassVar[int] = 100
         
     | 
| 51 | 
         
            +
                MAX_TOKENS: ClassVar[int] = 32000
         
     | 
| 52 | 
         
            +
                REQUEST_TIMEOUT: ClassVar[int] = 60
         
     | 
| 53 | 
         
            +
                MAX_RETRIES: ClassVar[int] = 3
         
     | 
| 54 | 
         
            +
                RETRY_DELAY: ClassVar[float] = 1.0
         
     | 
| 55 | 
         
            +
                CACHE_SIZE: ClassVar[int] = 100
         
     | 
| 56 | 
         
            +
                CACHE_TTL: ClassVar[int] = 3600
         
     | 
| 57 | 
         
            +
                RATE_LIMIT_REQUESTS: ClassVar[int] = 50
         
     | 
| 58 | 
         
            +
                RATE_LIMIT_WINDOW: ClassVar[int] = 60
         
     | 
| 59 | 
         
            +
                EXPORT_DIR: ClassVar[Path] = Path("exports")
         
     | 
| 60 | 
         
            +
                BACKUP_DIR: ClassVar[Path] = Path("backups")
         
     | 
| 61 | 
         
            +
                MAX_EXPORT_SIZE_MB: ClassVar[int] = 50
         
     | 
| 62 | 
         
            +
                THEME_PRIMARY: ClassVar[str] = "purple"
         
     | 
| 63 | 
         
            +
                THEME_SECONDARY: ClassVar[str] = "blue"
         
     | 
| 64 | 
         
            +
                AUTO_SAVE_INTERVAL: ClassVar[int] = 300
         
     | 
| 65 | 
         
            +
                ENABLE_ANALYTICS: ClassVar[bool] = True
         
     | 
| 66 | 
         
            +
                ANALYTICS_BATCH_SIZE: ClassVar[int] = 10
         
     | 
| 67 | 
         | 
| 68 | 
         
             
                @classmethod
         
     | 
| 69 | 
         
             
                def validate(cls) -> bool:
         
     | 
| 
         | 
|
| 71 | 
         
             
                        assert cls.MIN_TEMPERATURE <= cls.DEFAULT_TEMPERATURE <= cls.MAX_TEMPERATURE
         
     | 
| 72 | 
         
             
                        assert cls.MIN_TOKENS <= cls.DEFAULT_MAX_TOKENS <= cls.MAX_TOKENS
         
     | 
| 73 | 
         
             
                        assert cls.MAX_HISTORY_LENGTH > 0
         
     | 
| 74 | 
         
            +
                        logger.info("Configuration validation passed")
         
     | 
| 75 | 
         
             
                        return True
         
     | 
| 76 | 
         
             
                    except AssertionError as e:
         
     | 
| 77 | 
         
             
                        logger.error(f"Configuration validation failed: {e}")
         
     | 
| 
         | 
|
| 79 | 
         | 
| 80 | 
         
             
                @classmethod
         
     | 
| 81 | 
         
             
                def create_directories(cls) -> None:
         
     | 
| 82 | 
         
            +
                    try:
         
     | 
| 83 | 
         
            +
                        cls.EXPORT_DIR.mkdir(exist_ok=True, parents=True)
         
     | 
| 84 | 
         
            +
                        cls.BACKUP_DIR.mkdir(exist_ok=True, parents=True)
         
     | 
| 85 | 
         
            +
                        logger.info("Application directories initialized")
         
     | 
| 86 | 
         
            +
                    except Exception as e:
         
     | 
| 87 | 
         
            +
                        logger.error(f"Failed to create directories: {e}")
         
     | 
| 88 | 
         | 
| 89 | 
         
             
            AppConfig.create_directories()
         
     | 
| 90 | 
         
             
            AppConfig.validate()
         
     | 
| 
         | 
|
| 190 | 
         
             
                margin: 1rem 0;
         
     | 
| 191 | 
         
             
                font-family: 'JetBrains Mono', monospace;
         
     | 
| 192 | 
         
             
                transition: var(--transition);
         
     | 
| 
         | 
|
| 193 | 
         
             
                box-shadow: 0 2px 8px rgba(0,0,0,0.08);
         
     | 
| 194 | 
         
             
            }
         
     | 
| 195 | 
         | 
| 196 | 
         
            +
            .metrics-card * {
         
     | 
| 197 | 
         
            +
                color: #2c3e50 !important;
         
     | 
| 198 | 
         
            +
            }
         
     | 
| 199 | 
         
            +
             
     | 
| 200 | 
         
             
            .metrics-card strong {
         
     | 
| 201 | 
         
             
                color: #1a1a1a !important;
         
     | 
| 202 | 
         
             
                font-weight: 600;
         
     | 
| 
         | 
|
| 216 | 
         
             
                box-shadow: var(--shadow-lg);
         
     | 
| 217 | 
         
             
            }
         
     | 
| 218 | 
         | 
| 219 | 
         
            +
            .analytics-panel * {
         
     | 
| 220 | 
         
             
                color: white !important;
         
     | 
| 221 | 
         
            +
            }
         
     | 
| 222 | 
         
            +
             
     | 
| 223 | 
         
            +
            .analytics-panel h3 {
         
     | 
| 224 | 
         
             
                margin-bottom: 1rem;
         
     | 
| 225 | 
         
             
                font-size: 1.5rem;
         
     | 
| 226 | 
         
             
            }
         
     | 
| 227 | 
         | 
| 228 | 
         
             
            .analytics-panel p {
         
     | 
| 
         | 
|
| 229 | 
         
             
                line-height: 1.6;
         
     | 
| 230 | 
         
             
            }
         
     | 
| 231 | 
         | 
| 232 | 
         
             
            .analytics-panel strong {
         
     | 
| 
         | 
|
| 233 | 
         
             
                font-weight: 600;
         
     | 
| 234 | 
         
             
            }
         
     | 
| 235 | 
         | 
| 
         | 
|
| 267 | 
         
             
            .gr-button:hover { 
         
     | 
| 268 | 
         
             
                transform: translateY(-2px) !important; 
         
     | 
| 269 | 
         
             
            }
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 270 | 
         
             
            """
         
     | 
| 271 | 
         | 
| 272 | 
         
            +
            logger.info("Enhanced configuration initialized")
         
     | 
    	
        core.py
    CHANGED
    
    | 
         @@ -13,6 +13,7 @@ from concurrent.futures import ThreadPoolExecutor 
     | 
|
| 13 | 
         | 
| 14 | 
         
             
            from dotenv import load_dotenv
         
     | 
| 15 | 
         
             
            from groq import Groq
         
     | 
| 
         | 
|
| 16 | 
         | 
| 17 | 
         
             
            from config import logger, AppConfig, ReasoningMode, ModelConfig
         
     | 
| 18 | 
         | 
| 
         @@ -53,6 +54,8 @@ class ResponseCache: 
     | 
|
| 53 | 
         
             
                    """Clear cache"""
         
     | 
| 54 | 
         
             
                    with self.lock:
         
     | 
| 55 | 
         
             
                        self.cache.clear()
         
     | 
| 
         | 
|
| 
         | 
|
| 56 | 
         
             
                        logger.info("Cache cleared")
         
     | 
| 57 | 
         | 
| 58 | 
         
             
                def get_stats(self) -> Dict[str, int]:
         
     | 
| 
         @@ -96,7 +99,7 @@ class RateLimiter: 
     | 
|
| 96 | 
         | 
| 97 | 
         
             
            @dataclass
         
     | 
| 98 | 
         
             
            class ConversationMetrics:
         
     | 
| 99 | 
         
            -
                """Enhanced metrics with  
     | 
| 100 | 
         
             
                reasoning_depth: int = 0
         
     | 
| 101 | 
         
             
                self_corrections: int = 0
         
     | 
| 102 | 
         
             
                confidence_score: float = 0.0
         
     | 
| 
         @@ -116,27 +119,45 @@ class ConversationMetrics: 
     | 
|
| 116 | 
         
             
                mode_switches: int = 0
         
     | 
| 117 | 
         
             
                peak_tokens: int = 0
         
     | 
| 118 | 
         
             
                total_latency: float = 0.0
         
     | 
| 
         | 
|
| 119 | 
         | 
| 120 | 
         
             
                def update_confidence(self) -> None:
         
     | 
| 121 | 
         
             
                    """Calculate confidence based on multiple factors"""
         
     | 
| 122 | 
         
            -
                     
     | 
| 123 | 
         
            -
             
     | 
| 124 | 
         
            -
             
     | 
| 125 | 
         
            -
             
     | 
| 126 | 
         
            -
             
     | 
| 
         | 
|
| 127 | 
         | 
| 128 | 
         
             
                def update_tokens_per_second(self, tokens: int, time_taken: float) -> None:
         
     | 
| 129 | 
         
             
                    """Calculate tokens per second"""
         
     | 
| 130 | 
         
            -
                     
     | 
| 131 | 
         
            -
                         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 132 | 
         | 
| 133 | 
         
             
                def reset(self) -> None:
         
     | 
| 134 | 
         
             
                    """Reset metrics for new session"""
         
     | 
| 135 | 
         
            -
                    self. 
     | 
| 
         | 
|
| 136 | 
         | 
| 137 | 
         
             
                def to_dict(self) -> Dict[str, Any]:
         
     | 
| 138 | 
         
             
                    """Convert to dictionary"""
         
     | 
| 139 | 
         
            -
                     
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 140 | 
         | 
| 141 | 
         
             
            @dataclass
         
     | 
| 142 | 
         
             
            class ConversationEntry:
         
     | 
| 
         @@ -168,7 +189,7 @@ class ConversationEntry: 
     | 
|
| 168 | 
         | 
| 169 | 
         
             
                def _generate_id(self) -> str:
         
     | 
| 170 | 
         
             
                    """Generate unique conversation ID"""
         
     | 
| 171 | 
         
            -
                    content = f"{self.timestamp}{self.user_message}"
         
     | 
| 172 | 
         
             
                    return hashlib.md5(content.encode()).hexdigest()[:12]
         
     | 
| 173 | 
         | 
| 174 | 
         
             
                def to_dict(self) -> Dict[str, Any]:
         
     | 
| 
         @@ -191,7 +212,7 @@ class ConversationEntry: 
     | 
|
| 191 | 
         
             
                        self.rating = rating
         
     | 
| 192 | 
         | 
| 193 | 
         
             
            def error_handler(func):
         
     | 
| 194 | 
         
            -
                """Enhanced error handling decorator  
     | 
| 195 | 
         
             
                @wraps(func)
         
     | 
| 196 | 
         
             
                def wrapper(*args, **kwargs):
         
     | 
| 197 | 
         
             
                    max_retries = AppConfig.MAX_RETRIES
         
     | 
| 
         @@ -199,27 +220,51 @@ def error_handler(func): 
     | 
|
| 199 | 
         | 
| 200 | 
         
             
                    for attempt in range(max_retries):
         
     | 
| 201 | 
         
             
                        try:
         
     | 
| 202 | 
         
            -
                             
     | 
| 203 | 
         
            -
             
     | 
| 204 | 
         
            -
                             
     | 
| 205 | 
         
            -
             
     | 
| 206 | 
         
            -
             
     | 
| 207 | 
         
            -
                                logger.info(f"Retrying in {retry_delay}s...")
         
     | 
| 208 | 
         
            -
                                time.sleep(retry_delay)
         
     | 
| 209 | 
         
            -
                                retry_delay *= 2
         
     | 
| 210 | 
         
             
                            else:
         
     | 
| 211 | 
         
            -
                                 
     | 
| 212 | 
         
            -
                                
         
     | 
| 213 | 
         
            -
             
     | 
| 214 | 
         
            -
             
     | 
| 215 | 
         
            -
             
     | 
| 216 | 
         
            -
             
     | 
| 217 | 
         
            -
             
     | 
| 218 | 
         
            -
             
     | 
| 219 | 
         
            -
             
     | 
| 220 | 
         
            -
             
     | 
| 221 | 
         
            -
             
     | 
| 222 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 223 | 
         
             
                return wrapper
         
     | 
| 224 | 
         | 
| 225 | 
         
             
            @contextmanager
         
     | 
| 
         @@ -514,7 +559,7 @@ class ConversationExporter: 
     | 
|
| 514 | 
         | 
| 515 | 
         
             
                @staticmethod
         
     | 
| 516 | 
         
             
                def to_pdf(entries: List[ConversationEntry], filename: str) -> str:
         
     | 
| 517 | 
         
            -
                    """Export to PDF format"""
         
     | 
| 518 | 
         
             
                    try:
         
     | 
| 519 | 
         
             
                        from reportlab.lib.pagesizes import letter
         
     | 
| 520 | 
         
             
                        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
         
     | 
| 
         @@ -589,23 +634,25 @@ class ConversationExporter: 
     | 
|
| 589 | 
         
             
                            story.append(Spacer(1, 0.1*inch))
         
     | 
| 590 | 
         | 
| 591 | 
         
             
                            story.append(Paragraph("<b>User:</b>", user_style))
         
     | 
| 592 | 
         
            -
                             
     | 
| 593 | 
         
            -
                             
     | 
| 594 | 
         
            -
             
     | 
| 
         | 
|
| 595 | 
         
             
                            story.append(Paragraph(user_msg, user_style))
         
     | 
| 596 | 
         
             
                            story.append(Spacer(1, 0.15*inch))
         
     | 
| 597 | 
         | 
| 598 | 
         
             
                            story.append(Paragraph("<b>Assistant:</b>", ai_style))
         
     | 
| 599 | 
         
            -
                             
     | 
| 600 | 
         
            -
                             
     | 
| 601 | 
         
            -
             
     | 
| 
         | 
|
| 602 | 
         
             
                            story.append(Paragraph(ai_resp, ai_style))
         
     | 
| 603 | 
         | 
| 604 | 
         
             
                            if i < len(entries):
         
     | 
| 605 | 
         
             
                                story.append(PageBreak())
         
     | 
| 606 | 
         | 
| 607 | 
         
             
                        doc.build(story)
         
     | 
| 608 | 
         
            -
                        logger.info(f"PDF exported to {filename}")
         
     | 
| 609 | 
         
             
                        return filename
         
     | 
| 610 | 
         | 
| 611 | 
         
             
                    except ImportError:
         
     | 
| 
         @@ -613,7 +660,7 @@ class ConversationExporter: 
     | 
|
| 613 | 
         
             
                        logger.error(error_msg)
         
     | 
| 614 | 
         
             
                        return ""
         
     | 
| 615 | 
         
             
                    except Exception as e:
         
     | 
| 616 | 
         
            -
                        logger.error(f"PDF export failed: {e}")
         
     | 
| 617 | 
         
             
                        return ""
         
     | 
| 618 | 
         | 
| 619 | 
         
             
                @classmethod
         
     | 
| 
         @@ -628,9 +675,9 @@ class ConversationExporter: 
     | 
|
| 628 | 
         
             
                        filename = AppConfig.EXPORT_DIR / f"conversation_{timestamp}.{ext}"
         
     | 
| 629 | 
         
             
                        result = cls.to_pdf(entries, str(filename))
         
     | 
| 630 | 
         
             
                        if result:
         
     | 
| 631 | 
         
            -
                            return "PDF exported successfully!  
     | 
| 632 | 
         
             
                        else:
         
     | 
| 633 | 
         
            -
                            return "PDF export failed. Install reportlab: pip install reportlab", ""
         
     | 
| 634 | 
         | 
| 635 | 
         
             
                    exporters = {
         
     | 
| 636 | 
         
             
                        "json": lambda: cls.to_json(entries),
         
     | 
| 
         @@ -701,9 +748,11 @@ class AdvancedReasoner: 
     | 
|
| 701 | 
         | 
| 702 | 
         
             
                def _generate_cache_key(self, query: str, model: str, mode: str, 
         
     | 
| 703 | 
         
             
                                       temp: float, template: str) -> str:
         
     | 
| 704 | 
         
            -
                    """Generate cache key for request"""
         
     | 
| 705 | 
         
            -
                     
     | 
| 706 | 
         
            -
                     
     | 
| 
         | 
|
| 
         | 
|
| 707 | 
         | 
| 708 | 
         
             
                def _calculate_reasoning_depth(self, response: str) -> int:
         
     | 
| 709 | 
         
             
                    """Calculate reasoning depth from response"""
         
     | 
| 
         @@ -726,18 +775,21 @@ class AdvancedReasoner: 
     | 
|
| 726 | 
         
             
                    mode: ReasoningMode,
         
     | 
| 727 | 
         
             
                    template: str
         
     | 
| 728 | 
         
             
                ) -> List[Dict[str, str]]:
         
     | 
| 729 | 
         
            -
                    """Build message list for API call"""
         
     | 
| 730 | 
         
             
                    messages = [
         
     | 
| 731 | 
         
             
                        {"role": "system", "content": self.prompt_engine.SYSTEM_PROMPTS[mode]}
         
     | 
| 732 | 
         
             
                    ]
         
     | 
| 733 | 
         | 
| 734 | 
         
             
                    recent_history = history[-AppConfig.MAX_HISTORY_LENGTH:] if history else []
         
     | 
| 735 | 
         
             
                    for msg in recent_history:
         
     | 
| 736 | 
         
            -
                         
     | 
| 737 | 
         
            -
             
     | 
| 738 | 
         
            -
                             
     | 
| 739 | 
         
            -
             
     | 
| 740 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 741 | 
         | 
| 742 | 
         
             
                    enhanced_query = self.prompt_engine.build_prompt(query, mode, template)
         
     | 
| 743 | 
         
             
                    messages.append({"role": "user", "content": enhanced_query})
         
     | 
| 
         @@ -753,7 +805,7 @@ class AdvancedReasoner: 
     | 
|
| 753 | 
         
             
                        "context": context
         
     | 
| 754 | 
         
             
                    }
         
     | 
| 755 | 
         
             
                    self.error_log.append(error_entry)
         
     | 
| 756 | 
         
            -
                    self.metrics.error_count 
     | 
| 757 | 
         
             
                    logger.error(f"Error logged: {error_entry}")
         
     | 
| 758 | 
         | 
| 759 | 
         
             
                @error_handler
         
     | 
| 
         @@ -769,28 +821,28 @@ class AdvancedReasoner: 
     | 
|
| 769 | 
         
             
                    prompt_template: str = "Custom",
         
     | 
| 770 | 
         
             
                    use_cache: bool = True
         
     | 
| 771 | 
         
             
                ) -> Generator[str, None, None]:
         
     | 
| 772 | 
         
            -
                    """Generate response with advanced features"""
         
     | 
| 773 | 
         | 
| 774 | 
         
             
                    is_valid, error_msg = validate_input(query)
         
     | 
| 775 | 
         
             
                    if not is_valid:
         
     | 
| 776 | 
         
            -
                        yield f"Validation Error: {error_msg}"
         
     | 
| 777 | 
         
             
                        return
         
     | 
| 778 | 
         | 
| 779 | 
         
             
                    allowed, wait_time = self.rate_limiter.is_allowed()
         
     | 
| 780 | 
         
             
                    if not allowed:
         
     | 
| 781 | 
         
            -
                        yield f"Rate Limit: Please wait {wait_time:.1f} seconds."
         
     | 
| 782 | 
         
             
                        return
         
     | 
| 783 | 
         | 
| 784 | 
         
             
                    cache_key = self._generate_cache_key(query, model, reasoning_mode.value, temperature, prompt_template)
         
     | 
| 785 | 
         
             
                    if use_cache:
         
     | 
| 786 | 
         
             
                        cached_response = self.cache.get(cache_key)
         
     | 
| 787 | 
         
             
                        if cached_response:
         
     | 
| 788 | 
         
            -
                            self.metrics.cache_hits 
     | 
| 789 | 
         
             
                            logger.info("Returning cached response")
         
     | 
| 790 | 
         
             
                            yield cached_response
         
     | 
| 791 | 
         
             
                            return
         
     | 
| 792 | 
         | 
| 793 | 
         
            -
                    self.metrics.cache_misses 
     | 
| 794 | 
         | 
| 795 | 
         
             
                    with timer(f"Response generation for {model}"):
         
     | 
| 796 | 
         
             
                        start_time = time.time()
         
     | 
| 
         @@ -808,12 +860,14 @@ class AdvancedReasoner: 
     | 
|
| 808 | 
         
             
                                stream=True,
         
     | 
| 809 | 
         
             
                            )
         
     | 
| 810 | 
         | 
| 
         | 
|
| 811 | 
         
             
                            for chunk in stream:
         
     | 
| 812 | 
         
             
                                if chunk.choices[0].delta.content:
         
     | 
| 813 | 
         
             
                                    content = chunk.choices[0].delta.content
         
     | 
| 814 | 
         
             
                                    full_response += content
         
     | 
| 815 | 
         
             
                                    token_count += 1
         
     | 
| 816 | 
         
            -
                                    self.metrics.tokens_used 
     | 
| 
         | 
|
| 817 | 
         
             
                                    yield full_response
         
     | 
| 818 | 
         | 
| 819 | 
         
             
                        except Exception as e:
         
     | 
| 
         @@ -825,9 +879,9 @@ class AdvancedReasoner: 
     | 
|
| 825 | 
         
             
                            raise
         
     | 
| 826 | 
         | 
| 827 | 
         
             
                        inference_time = time.time() - start_time
         
     | 
| 828 | 
         
            -
                        self.metrics.reasoning_depth  
     | 
| 829 | 
         
             
                        self.metrics.update_tokens_per_second(token_count, inference_time)
         
     | 
| 830 | 
         
            -
                        self.metrics.peak_tokens  
     | 
| 831 | 
         | 
| 832 | 
         
             
                        if enable_critique and len(full_response) > 150:
         
     | 
| 833 | 
         
             
                            messages.append({"role": "assistant", "content": full_response})
         
     | 
| 
         @@ -837,6 +891,7 @@ class AdvancedReasoner: 
     | 
|
| 837 | 
         
             
                            })
         
     | 
| 838 | 
         | 
| 839 | 
         
             
                            full_response += "\n\n---\n### Validation & Self-Critique\n"
         
     | 
| 
         | 
|
| 840 | 
         | 
| 841 | 
         
             
                            try:
         
     | 
| 842 | 
         
             
                                critique_stream = self.client.chat.completions.create(
         
     | 
| 
         @@ -854,19 +909,19 @@ class AdvancedReasoner: 
     | 
|
| 854 | 
         
             
                                        token_count += 1
         
     | 
| 855 | 
         
             
                                        yield full_response
         
     | 
| 856 | 
         | 
| 857 | 
         
            -
                                self.metrics.self_corrections 
     | 
| 858 | 
         | 
| 859 | 
         
             
                            except Exception as e:
         
     | 
| 860 | 
         
             
                                logger.warning(f"Critique phase failed: {e}")
         
     | 
| 861 | 
         | 
| 862 | 
         
             
                        final_inference_time = time.time() - start_time
         
     | 
| 863 | 
         
            -
                        self.metrics.inference_time  
     | 
| 864 | 
         
            -
                        self.metrics.total_latency  
     | 
| 865 | 
         
             
                        self.response_times.append(final_inference_time)
         
     | 
| 866 | 
         
            -
                        self.metrics.avg_response_time  
     | 
| 867 | 
         
            -
                        self.metrics.last_updated  
     | 
| 868 | 
         
             
                        self.metrics.update_confidence()
         
     | 
| 869 | 
         
            -
                        self.metrics.total_conversations 
     | 
| 870 | 
         | 
| 871 | 
         
             
                        self.model_usage[model] += 1
         
     | 
| 872 | 
         
             
                        self.mode_usage[reasoning_mode.value] += 1
         
     | 
| 
         @@ -894,15 +949,13 @@ class AdvancedReasoner: 
     | 
|
| 894 | 
         | 
| 895 | 
         
             
                        if len(self.conversation_history) % 10 == 0:
         
     | 
| 896 | 
         
             
                            try:
         
     | 
| 897 | 
         
            -
                                self.exporter.create_backup 
     | 
| 898 | 
         
             
                            except Exception as e:
         
     | 
| 899 | 
         
             
                                logger.warning(f"Auto-backup failed: {e}")
         
     | 
| 900 | 
         | 
| 901 | 
         
             
                        if len(self.conversation_history) > AppConfig.MAX_CONVERSATION_STORAGE:
         
     | 
| 902 | 
         
             
                            self.conversation_history = self.conversation_history[-AppConfig.MAX_CONVERSATION_STORAGE:]
         
     | 
| 903 | 
         
             
                            logger.info(f"Trimmed history to {AppConfig.MAX_CONVERSATION_STORAGE} entries")
         
     | 
| 904 | 
         
            -
                        
         
     | 
| 905 | 
         
            -
                        yield full_response
         
     | 
| 906 | 
         | 
| 907 | 
         
             
                def export_conversation(self, format_type: str, include_metadata: bool = True) -> Tuple[str, str]:
         
     | 
| 908 | 
         
             
                    """Export conversation history"""
         
     | 
| 
         @@ -952,8 +1005,8 @@ class AdvancedReasoner: 
     | 
|
| 952 | 
         
             
                        "total_time": total_time,
         
     | 
| 953 | 
         
             
                        "avg_inference_time": self.metrics.avg_response_time,
         
     | 
| 954 | 
         
             
                        "peak_tokens": self.metrics.peak_tokens,
         
     | 
| 955 | 
         
            -
                        "most_used_model": max(set(models), key=models.count),
         
     | 
| 956 | 
         
            -
                        "most_used_mode": max(set(modes), key=modes.count),
         
     | 
| 957 | 
         
             
                        "cache_hits": self.metrics.cache_hits,
         
     | 
| 958 | 
         
             
                        "cache_misses": self.metrics.cache_misses,
         
     | 
| 959 | 
         
             
                        "error_count": self.metrics.error_count
         
     | 
| 
         @@ -963,7 +1016,7 @@ class AdvancedReasoner: 
     | 
|
| 963 | 
         
             
                    """Clear conversation history and reset metrics"""
         
     | 
| 964 | 
         
             
                    if self.conversation_history:
         
     | 
| 965 | 
         
             
                        try:
         
     | 
| 966 | 
         
            -
                            self.exporter.create_backup 
     | 
| 967 | 
         
             
                        except Exception as e:
         
     | 
| 968 | 
         
             
                            logger.warning(f"Failed to backup before clearing: {e}")
         
     | 
| 969 | 
         | 
| 
         @@ -983,4 +1036,4 @@ class AdvancedReasoner: 
     | 
|
| 983 | 
         
             
                        self.executor.shutdown(wait=False)
         
     | 
| 984 | 
         
             
                        logger.info("AdvancedReasoner cleanup completed")
         
     | 
| 985 | 
         
             
                    except:
         
     | 
| 986 | 
         
            -
                        pass
         
     | 
| 
         | 
|
| 13 | 
         | 
| 14 | 
         
             
            from dotenv import load_dotenv
         
     | 
| 15 | 
         
             
            from groq import Groq
         
     | 
| 16 | 
         
            +
            import groq
         
     | 
| 17 | 
         | 
| 18 | 
         
             
            from config import logger, AppConfig, ReasoningMode, ModelConfig
         
     | 
| 19 | 
         | 
| 
         | 
|
| 54 | 
         
             
                    """Clear cache"""
         
     | 
| 55 | 
         
             
                    with self.lock:
         
     | 
| 56 | 
         
             
                        self.cache.clear()
         
     | 
| 57 | 
         
            +
                        self.hits = 0
         
     | 
| 58 | 
         
            +
                        self.misses = 0
         
     | 
| 59 | 
         
             
                        logger.info("Cache cleared")
         
     | 
| 60 | 
         | 
| 61 | 
         
             
                def get_stats(self) -> Dict[str, int]:
         
     | 
| 
         | 
|
| 99 | 
         | 
| 100 | 
         
             
            @dataclass
         
     | 
| 101 | 
         
             
            class ConversationMetrics:
         
     | 
| 102 | 
         
            +
                """Enhanced metrics with thread-safe operations"""
         
     | 
| 103 | 
         
             
                reasoning_depth: int = 0
         
     | 
| 104 | 
         
             
                self_corrections: int = 0
         
     | 
| 105 | 
         
             
                confidence_score: float = 0.0
         
     | 
| 
         | 
|
| 119 | 
         
             
                mode_switches: int = 0
         
     | 
| 120 | 
         
             
                peak_tokens: int = 0
         
     | 
| 121 | 
         
             
                total_latency: float = 0.0
         
     | 
| 122 | 
         
            +
                _lock: threading.Lock = field(default_factory=threading.Lock, init=False, repr=False)
         
     | 
| 123 | 
         | 
| 124 | 
         
             
                def update_confidence(self) -> None:
         
     | 
| 125 | 
         
             
                    """Calculate confidence based on multiple factors"""
         
     | 
| 126 | 
         
            +
                    with self._lock:
         
     | 
| 127 | 
         
            +
                        depth_score = min(30, self.reasoning_depth * 5)
         
     | 
| 128 | 
         
            +
                        correction_score = min(20, self.self_corrections * 10)
         
     | 
| 129 | 
         
            +
                        speed_score = min(25, 25 / max(1, self.avg_response_time))
         
     | 
| 130 | 
         
            +
                        consistency_score = 25
         
     | 
| 131 | 
         
            +
                        self.confidence_score = min(95.0, depth_score + correction_score + speed_score + consistency_score)
         
     | 
| 132 | 
         | 
| 133 | 
         
             
                def update_tokens_per_second(self, tokens: int, time_taken: float) -> None:
         
     | 
| 134 | 
         
             
                    """Calculate tokens per second"""
         
     | 
| 135 | 
         
            +
                    with self._lock:
         
     | 
| 136 | 
         
            +
                        if time_taken > 0:
         
     | 
| 137 | 
         
            +
                            self.tokens_per_second = tokens / time_taken
         
     | 
| 138 | 
         
            +
                
         
     | 
| 139 | 
         
            +
                def increment_field(self, field_name: str, value: Any = 1) -> None:
         
     | 
| 140 | 
         
            +
                    """Thread-safe field increment"""
         
     | 
| 141 | 
         
            +
                    with self._lock:
         
     | 
| 142 | 
         
            +
                        current = getattr(self, field_name)
         
     | 
| 143 | 
         
            +
                        setattr(self, field_name, current + value)
         
     | 
| 144 | 
         
            +
                
         
     | 
| 145 | 
         
            +
                def set_field(self, field_name: str, value: Any) -> None:
         
     | 
| 146 | 
         
            +
                    """Thread-safe field setter"""
         
     | 
| 147 | 
         
            +
                    with self._lock:
         
     | 
| 148 | 
         
            +
                        setattr(self, field_name, value)
         
     | 
| 149 | 
         | 
| 150 | 
         
             
                def reset(self) -> None:
         
     | 
| 151 | 
         
             
                    """Reset metrics for new session"""
         
     | 
| 152 | 
         
            +
                    with self._lock:
         
     | 
| 153 | 
         
            +
                        self.__init__()
         
     | 
| 154 | 
         | 
| 155 | 
         
             
                def to_dict(self) -> Dict[str, Any]:
         
     | 
| 156 | 
         
             
                    """Convert to dictionary"""
         
     | 
| 157 | 
         
            +
                    with self._lock:
         
     | 
| 158 | 
         
            +
                        data = asdict(self)
         
     | 
| 159 | 
         
            +
                        data.pop('_lock', None)
         
     | 
| 160 | 
         
            +
                        return data
         
     | 
| 161 | 
         | 
| 162 | 
         
             
            @dataclass
         
     | 
| 163 | 
         
             
            class ConversationEntry:
         
     | 
| 
         | 
|
| 189 | 
         | 
| 190 | 
         
             
                def _generate_id(self) -> str:
         
     | 
| 191 | 
         
             
                    """Generate unique conversation ID"""
         
     | 
| 192 | 
         
            +
                    content = f"{self.timestamp}{self.user_message[:100]}"
         
     | 
| 193 | 
         
             
                    return hashlib.md5(content.encode()).hexdigest()[:12]
         
     | 
| 194 | 
         | 
| 195 | 
         
             
                def to_dict(self) -> Dict[str, Any]:
         
     | 
| 
         | 
|
| 212 | 
         
             
                        self.rating = rating
         
     | 
| 213 | 
         | 
| 214 | 
         
             
            def error_handler(func):
         
     | 
| 215 | 
         
            +
                """Enhanced error handling decorator for generator functions"""
         
     | 
| 216 | 
         
             
                @wraps(func)
         
     | 
| 217 | 
         
             
                def wrapper(*args, **kwargs):
         
     | 
| 218 | 
         
             
                    max_retries = AppConfig.MAX_RETRIES
         
     | 
| 
         | 
|
| 220 | 
         | 
| 221 | 
         
             
                    for attempt in range(max_retries):
         
     | 
| 222 | 
         
             
                        try:
         
     | 
| 223 | 
         
            +
                            # Check if function is a generator
         
     | 
| 224 | 
         
            +
                            result = func(*args, **kwargs)
         
     | 
| 225 | 
         
            +
                            if hasattr(result, '__iter__') and hasattr(result, '__next__'):
         
     | 
| 226 | 
         
            +
                                # It's a generator, yield from it
         
     | 
| 227 | 
         
            +
                                yield from result
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 228 | 
         
             
                            else:
         
     | 
| 229 | 
         
            +
                                # Regular function, return result
         
     | 
| 230 | 
         
            +
                                return result
         
     | 
| 231 | 
         
            +
                            return  # Exit after successful completion
         
     | 
| 232 | 
         
            +
                            
         
     | 
| 233 | 
         
            +
                        except groq.APIConnectionError as e:
         
     | 
| 234 | 
         
            +
                            error_msg = f"π **Connection Error**: Cannot reach Groq API.\n\n"
         
     | 
| 235 | 
         
            +
                            error_msg += "Please check your internet connection and try again."
         
     | 
| 236 | 
         
            +
                            logger.error(f"API Connection Error in {func.__name__}: {str(e)}")
         
     | 
| 237 | 
         
            +
                            
         
     | 
| 238 | 
         
            +
                        except groq.RateLimitError as e:
         
     | 
| 239 | 
         
            +
                            error_msg = f"β±οΈ **Rate Limit Exceeded**: Too many requests.\n\n"
         
     | 
| 240 | 
         
            +
                            error_msg += "Please wait a moment and try again."
         
     | 
| 241 | 
         
            +
                            logger.error(f"Rate Limit Error in {func.__name__}: {str(e)}")
         
     | 
| 242 | 
         
            +
                            
         
     | 
| 243 | 
         
            +
                        except groq.AuthenticationError as e:
         
     | 
| 244 | 
         
            +
                            error_msg = f"π **Authentication Error**: Invalid API key.\n\n"
         
     | 
| 245 | 
         
            +
                            error_msg += "Please verify your GROQ_API_KEY in the .env file."
         
     | 
| 246 | 
         
            +
                            logger.error(f"Authentication Error in {func.__name__}: {str(e)}")
         
     | 
| 247 | 
         
            +
                            yield error_msg
         
     | 
| 248 | 
         
            +
                            return  # Don't retry authentication errors
         
     | 
| 249 | 
         
            +
                            
         
     | 
| 250 | 
         
            +
                        except groq.APIStatusError as e:
         
     | 
| 251 | 
         
            +
                            error_msg = f"β οΈ **API Error** (Status {e.status_code}):\n\n"
         
     | 
| 252 | 
         
            +
                            error_msg += f"{str(e)}\n\nPlease try again or select a different model."
         
     | 
| 253 | 
         
            +
                            logger.error(f"API Status Error in {func.__name__}: {str(e)}")
         
     | 
| 254 | 
         
            +
                            
         
     | 
| 255 | 
         
            +
                        except Exception as e:
         
     | 
| 256 | 
         
            +
                            error_msg = f"β **System Error**: {str(e)}\n\n"
         
     | 
| 257 | 
         
            +
                            error_msg += "Please try again or contact support if the issue persists."
         
     | 
| 258 | 
         
            +
                            logger.error(f"Unexpected error in {func.__name__}: {str(e)}", exc_info=True)
         
     | 
| 259 | 
         
            +
                        
         
     | 
| 260 | 
         
            +
                        if attempt < max_retries - 1:
         
     | 
| 261 | 
         
            +
                            logger.info(f"Retrying in {retry_delay}s... (attempt {attempt+1}/{max_retries})")
         
     | 
| 262 | 
         
            +
                            time.sleep(retry_delay)
         
     | 
| 263 | 
         
            +
                            retry_delay *= 2
         
     | 
| 264 | 
         
            +
                        else:
         
     | 
| 265 | 
         
            +
                            yield error_msg
         
     | 
| 266 | 
         
            +
                            return
         
     | 
| 267 | 
         
            +
                
         
     | 
| 268 | 
         
             
                return wrapper
         
     | 
| 269 | 
         | 
| 270 | 
         
             
            @contextmanager
         
     | 
| 
         | 
|
| 559 | 
         | 
| 560 | 
         
             
                @staticmethod
         
     | 
| 561 | 
         
             
                def to_pdf(entries: List[ConversationEntry], filename: str) -> str:
         
     | 
| 562 | 
         
            +
                    """Export to PDF format with memory optimization"""
         
     | 
| 563 | 
         
             
                    try:
         
     | 
| 564 | 
         
             
                        from reportlab.lib.pagesizes import letter
         
     | 
| 565 | 
         
             
                        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
         
     | 
| 
         | 
|
| 634 | 
         
             
                            story.append(Spacer(1, 0.1*inch))
         
     | 
| 635 | 
         | 
| 636 | 
         
             
                            story.append(Paragraph("<b>User:</b>", user_style))
         
     | 
| 637 | 
         
            +
                            # Escape and truncate for memory efficiency
         
     | 
| 638 | 
         
            +
                            user_msg = entry.user_message.replace('<', '<').replace('>', '>').replace('\n', '<br/>')[:3000]
         
     | 
| 639 | 
         
            +
                            if len(entry.user_message) > 3000:
         
     | 
| 640 | 
         
            +
                                user_msg += "... (truncated)"
         
     | 
| 641 | 
         
             
                            story.append(Paragraph(user_msg, user_style))
         
     | 
| 642 | 
         
             
                            story.append(Spacer(1, 0.15*inch))
         
     | 
| 643 | 
         | 
| 644 | 
         
             
                            story.append(Paragraph("<b>Assistant:</b>", ai_style))
         
     | 
| 645 | 
         
            +
                            # Escape and truncate for memory efficiency
         
     | 
| 646 | 
         
            +
                            ai_resp = entry.ai_response.replace('<', '<').replace('>', '>').replace('\n', '<br/>')[:5000]
         
     | 
| 647 | 
         
            +
                            if len(entry.ai_response) > 5000:
         
     | 
| 648 | 
         
            +
                                ai_resp += "... (truncated)"
         
     | 
| 649 | 
         
             
                            story.append(Paragraph(ai_resp, ai_style))
         
     | 
| 650 | 
         | 
| 651 | 
         
             
                            if i < len(entries):
         
     | 
| 652 | 
         
             
                                story.append(PageBreak())
         
     | 
| 653 | 
         | 
| 654 | 
         
             
                        doc.build(story)
         
     | 
| 655 | 
         
            +
                        logger.info(f"PDF exported successfully to {filename}")
         
     | 
| 656 | 
         
             
                        return filename
         
     | 
| 657 | 
         | 
| 658 | 
         
             
                    except ImportError:
         
     | 
| 
         | 
|
| 660 | 
         
             
                        logger.error(error_msg)
         
     | 
| 661 | 
         
             
                        return ""
         
     | 
| 662 | 
         
             
                    except Exception as e:
         
     | 
| 663 | 
         
            +
                        logger.error(f"PDF export failed: {e}", exc_info=True)
         
     | 
| 664 | 
         
             
                        return ""
         
     | 
| 665 | 
         | 
| 666 | 
         
             
                @classmethod
         
     | 
| 
         | 
|
| 675 | 
         
             
                        filename = AppConfig.EXPORT_DIR / f"conversation_{timestamp}.{ext}"
         
     | 
| 676 | 
         
             
                        result = cls.to_pdf(entries, str(filename))
         
     | 
| 677 | 
         
             
                        if result:
         
     | 
| 678 | 
         
            +
                            return f"β
 PDF exported successfully! File: conversation_{timestamp}.pdf", str(filename)
         
     | 
| 679 | 
         
             
                        else:
         
     | 
| 680 | 
         
            +
                            return "β PDF export failed. Install reportlab: pip install reportlab", ""
         
     | 
| 681 | 
         | 
| 682 | 
         
             
                    exporters = {
         
     | 
| 683 | 
         
             
                        "json": lambda: cls.to_json(entries),
         
     | 
| 
         | 
|
| 748 | 
         | 
| 749 | 
         
             
                def _generate_cache_key(self, query: str, model: str, mode: str, 
         
     | 
| 750 | 
         
             
                                       temp: float, template: str) -> str:
         
     | 
| 751 | 
         
            +
                    """Generate stable cache key for request"""
         
     | 
| 752 | 
         
            +
                    # Normalize inputs for consistent key generation
         
     | 
| 753 | 
         
            +
                    normalized_query = query.strip().lower()[:500]  # Limit length
         
     | 
| 754 | 
         
            +
                    content = f"{normalized_query}|{model}|{mode}|{temp:.2f}|{template}"
         
     | 
| 755 | 
         
            +
                    return hashlib.sha256(content.encode('utf-8')).hexdigest()
         
     | 
| 756 | 
         | 
| 757 | 
         
             
                def _calculate_reasoning_depth(self, response: str) -> int:
         
     | 
| 758 | 
         
             
                    """Calculate reasoning depth from response"""
         
     | 
| 
         | 
|
| 775 | 
         
             
                    mode: ReasoningMode,
         
     | 
| 776 | 
         
             
                    template: str
         
     | 
| 777 | 
         
             
                ) -> List[Dict[str, str]]:
         
     | 
| 778 | 
         
            +
                    """Build message list for API call with validation"""
         
     | 
| 779 | 
         
             
                    messages = [
         
     | 
| 780 | 
         
             
                        {"role": "system", "content": self.prompt_engine.SYSTEM_PROMPTS[mode]}
         
     | 
| 781 | 
         
             
                    ]
         
     | 
| 782 | 
         | 
| 783 | 
         
             
                    recent_history = history[-AppConfig.MAX_HISTORY_LENGTH:] if history else []
         
     | 
| 784 | 
         
             
                    for msg in recent_history:
         
     | 
| 785 | 
         
            +
                        # Validate message structure
         
     | 
| 786 | 
         
            +
                        if isinstance(msg, dict) and "role" in msg and "content" in msg:
         
     | 
| 787 | 
         
            +
                            role = msg.get("role")
         
     | 
| 788 | 
         
            +
                            content = msg.get("content", "")
         
     | 
| 789 | 
         
            +
                            
         
     | 
| 790 | 
         
            +
                            # Only add valid user/assistant messages
         
     | 
| 791 | 
         
            +
                            if role in ["user", "assistant"] and content:
         
     | 
| 792 | 
         
            +
                                messages.append({"role": role, "content": str(content)})
         
     | 
| 793 | 
         | 
| 794 | 
         
             
                    enhanced_query = self.prompt_engine.build_prompt(query, mode, template)
         
     | 
| 795 | 
         
             
                    messages.append({"role": "user", "content": enhanced_query})
         
     | 
| 
         | 
|
| 805 | 
         
             
                        "context": context
         
     | 
| 806 | 
         
             
                    }
         
     | 
| 807 | 
         
             
                    self.error_log.append(error_entry)
         
     | 
| 808 | 
         
            +
                    self.metrics.increment_field("error_count")
         
     | 
| 809 | 
         
             
                    logger.error(f"Error logged: {error_entry}")
         
     | 
| 810 | 
         | 
| 811 | 
         
             
                @error_handler
         
     | 
| 
         | 
|
| 821 | 
         
             
                    prompt_template: str = "Custom",
         
     | 
| 822 | 
         
             
                    use_cache: bool = True
         
     | 
| 823 | 
         
             
                ) -> Generator[str, None, None]:
         
     | 
| 824 | 
         
            +
                    """Generate response with advanced features - FIXED for streaming"""
         
     | 
| 825 | 
         | 
| 826 | 
         
             
                    is_valid, error_msg = validate_input(query)
         
     | 
| 827 | 
         
             
                    if not is_valid:
         
     | 
| 828 | 
         
            +
                        yield f"β οΈ Validation Error: {error_msg}"
         
     | 
| 829 | 
         
             
                        return
         
     | 
| 830 | 
         | 
| 831 | 
         
             
                    allowed, wait_time = self.rate_limiter.is_allowed()
         
     | 
| 832 | 
         
             
                    if not allowed:
         
     | 
| 833 | 
         
            +
                        yield f"β±οΈ Rate Limit: Please wait {wait_time:.1f} seconds."
         
     | 
| 834 | 
         
             
                        return
         
     | 
| 835 | 
         | 
| 836 | 
         
             
                    cache_key = self._generate_cache_key(query, model, reasoning_mode.value, temperature, prompt_template)
         
     | 
| 837 | 
         
             
                    if use_cache:
         
     | 
| 838 | 
         
             
                        cached_response = self.cache.get(cache_key)
         
     | 
| 839 | 
         
             
                        if cached_response:
         
     | 
| 840 | 
         
            +
                            self.metrics.increment_field("cache_hits")
         
     | 
| 841 | 
         
             
                            logger.info("Returning cached response")
         
     | 
| 842 | 
         
             
                            yield cached_response
         
     | 
| 843 | 
         
             
                            return
         
     | 
| 844 | 
         | 
| 845 | 
         
            +
                    self.metrics.increment_field("cache_misses")
         
     | 
| 846 | 
         | 
| 847 | 
         
             
                    with timer(f"Response generation for {model}"):
         
     | 
| 848 | 
         
             
                        start_time = time.time()
         
     | 
| 
         | 
|
| 860 | 
         
             
                                stream=True,
         
     | 
| 861 | 
         
             
                            )
         
     | 
| 862 | 
         | 
| 863 | 
         
            +
                            # FIXED: Only yield new content, not full_response repeatedly
         
     | 
| 864 | 
         
             
                            for chunk in stream:
         
     | 
| 865 | 
         
             
                                if chunk.choices[0].delta.content:
         
     | 
| 866 | 
         
             
                                    content = chunk.choices[0].delta.content
         
     | 
| 867 | 
         
             
                                    full_response += content
         
     | 
| 868 | 
         
             
                                    token_count += 1
         
     | 
| 869 | 
         
            +
                                    self.metrics.increment_field("tokens_used")
         
     | 
| 870 | 
         
            +
                                    # Yield only the accumulated response so far
         
     | 
| 871 | 
         
             
                                    yield full_response
         
     | 
| 872 | 
         | 
| 873 | 
         
             
                        except Exception as e:
         
     | 
| 
         | 
|
| 879 | 
         
             
                            raise
         
     | 
| 880 | 
         | 
| 881 | 
         
             
                        inference_time = time.time() - start_time
         
     | 
| 882 | 
         
            +
                        self.metrics.set_field("reasoning_depth", self._calculate_reasoning_depth(full_response))
         
     | 
| 883 | 
         
             
                        self.metrics.update_tokens_per_second(token_count, inference_time)
         
     | 
| 884 | 
         
            +
                        self.metrics.set_field("peak_tokens", max(self.metrics.peak_tokens, token_count))
         
     | 
| 885 | 
         | 
| 886 | 
         
             
                        if enable_critique and len(full_response) > 150:
         
     | 
| 887 | 
         
             
                            messages.append({"role": "assistant", "content": full_response})
         
     | 
| 
         | 
|
| 891 | 
         
             
                            })
         
     | 
| 892 | 
         | 
| 893 | 
         
             
                            full_response += "\n\n---\n### Validation & Self-Critique\n"
         
     | 
| 894 | 
         
            +
                            yield full_response
         
     | 
| 895 | 
         | 
| 896 | 
         
             
                            try:
         
     | 
| 897 | 
         
             
                                critique_stream = self.client.chat.completions.create(
         
     | 
| 
         | 
|
| 909 | 
         
             
                                        token_count += 1
         
     | 
| 910 | 
         
             
                                        yield full_response
         
     | 
| 911 | 
         | 
| 912 | 
         
            +
                                self.metrics.increment_field("self_corrections")
         
     | 
| 913 | 
         | 
| 914 | 
         
             
                            except Exception as e:
         
     | 
| 915 | 
         
             
                                logger.warning(f"Critique phase failed: {e}")
         
     | 
| 916 | 
         | 
| 917 | 
         
             
                        final_inference_time = time.time() - start_time
         
     | 
| 918 | 
         
            +
                        self.metrics.set_field("inference_time", final_inference_time)
         
     | 
| 919 | 
         
            +
                        self.metrics.increment_field("total_latency", final_inference_time)
         
     | 
| 920 | 
         
             
                        self.response_times.append(final_inference_time)
         
     | 
| 921 | 
         
            +
                        self.metrics.set_field("avg_response_time", sum(self.response_times) / len(self.response_times))
         
     | 
| 922 | 
         
            +
                        self.metrics.set_field("last_updated", datetime.now().strftime("%H:%M:%S"))
         
     | 
| 923 | 
         
             
                        self.metrics.update_confidence()
         
     | 
| 924 | 
         
            +
                        self.metrics.increment_field("total_conversations")
         
     | 
| 925 | 
         | 
| 926 | 
         
             
                        self.model_usage[model] += 1
         
     | 
| 927 | 
         
             
                        self.mode_usage[reasoning_mode.value] += 1
         
     | 
| 
         | 
|
| 949 | 
         | 
| 950 | 
         
             
                        if len(self.conversation_history) % 10 == 0:
         
     | 
| 951 | 
         
             
                            try:
         
     | 
| 952 | 
         
            +
                                self.executor.submit(self.exporter.create_backup, self.conversation_history.copy())
         
     | 
| 953 | 
         
             
                            except Exception as e:
         
     | 
| 954 | 
         
             
                                logger.warning(f"Auto-backup failed: {e}")
         
     | 
| 955 | 
         | 
| 956 | 
         
             
                        if len(self.conversation_history) > AppConfig.MAX_CONVERSATION_STORAGE:
         
     | 
| 957 | 
         
             
                            self.conversation_history = self.conversation_history[-AppConfig.MAX_CONVERSATION_STORAGE:]
         
     | 
| 958 | 
         
             
                            logger.info(f"Trimmed history to {AppConfig.MAX_CONVERSATION_STORAGE} entries")
         
     | 
| 
         | 
|
| 
         | 
|
| 959 | 
         | 
| 960 | 
         
             
                def export_conversation(self, format_type: str, include_metadata: bool = True) -> Tuple[str, str]:
         
     | 
| 961 | 
         
             
                    """Export conversation history"""
         
     | 
| 
         | 
|
| 1005 | 
         
             
                        "total_time": total_time,
         
     | 
| 1006 | 
         
             
                        "avg_inference_time": self.metrics.avg_response_time,
         
     | 
| 1007 | 
         
             
                        "peak_tokens": self.metrics.peak_tokens,
         
     | 
| 1008 | 
         
            +
                        "most_used_model": max(set(models), key=models.count) if models else "N/A",
         
     | 
| 1009 | 
         
            +
                        "most_used_mode": max(set(modes), key=modes.count) if modes else "N/A",
         
     | 
| 1010 | 
         
             
                        "cache_hits": self.metrics.cache_hits,
         
     | 
| 1011 | 
         
             
                        "cache_misses": self.metrics.cache_misses,
         
     | 
| 1012 | 
         
             
                        "error_count": self.metrics.error_count
         
     | 
| 
         | 
|
| 1016 | 
         
             
                    """Clear conversation history and reset metrics"""
         
     | 
| 1017 | 
         
             
                    if self.conversation_history:
         
     | 
| 1018 | 
         
             
                        try:
         
     | 
| 1019 | 
         
            +
                            self.executor.submit(self.exporter.create_backup, self.conversation_history.copy())
         
     | 
| 1020 | 
         
             
                        except Exception as e:
         
     | 
| 1021 | 
         
             
                            logger.warning(f"Failed to backup before clearing: {e}")
         
     | 
| 1022 | 
         | 
| 
         | 
|
| 1036 | 
         
             
                        self.executor.shutdown(wait=False)
         
     | 
| 1037 | 
         
             
                        logger.info("AdvancedReasoner cleanup completed")
         
     | 
| 1038 | 
         
             
                    except:
         
     | 
| 1039 | 
         
            +
                        pass
         
     | 
    	
        main.py
    CHANGED
    
    | 
         @@ -5,6 +5,60 @@ from core import AdvancedReasoner, PromptEngine 
     | 
|
| 5 | 
         
             
            # Initialize system
         
     | 
| 6 | 
         
             
            reasoner = AdvancedReasoner()
         
     | 
| 7 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 8 | 
         
             
            def get_metrics_html() -> str:
         
     | 
| 9 | 
         
             
                """Generate enhanced metrics HTML"""
         
     | 
| 10 | 
         
             
                m = reasoner.metrics
         
     | 
| 
         @@ -34,7 +88,7 @@ def get_empty_analytics_html() -> str: 
     | 
|
| 34 | 
         
             
                </div>"""
         
     | 
| 35 | 
         | 
| 36 | 
         
             
            def create_ui() -> gr.Blocks:
         
     | 
| 37 | 
         
            -
                """Create enhanced Gradio interface"""
         
     | 
| 38 | 
         | 
| 39 | 
         
             
                with gr.Blocks(
         
     | 
| 40 | 
         
             
                    theme=gr.themes.Soft(
         
     | 
| 
         @@ -42,7 +96,7 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 42 | 
         
             
                        secondary_hue=AppConfig.THEME_SECONDARY,
         
     | 
| 43 | 
         
             
                        font=gr.themes.GoogleFont("Inter")
         
     | 
| 44 | 
         
             
                    ),
         
     | 
| 45 | 
         
            -
                    css= 
     | 
| 46 | 
         
             
                    title="Advanced AI Reasoning System Pro"
         
     | 
| 47 | 
         
             
                ) as demo:
         
     | 
| 48 | 
         | 
| 
         @@ -55,18 +109,20 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 55 | 
         
             
                            <span class="badge">Bai et al. 2022 - Constitutional AI</span>
         
     | 
| 56 | 
         
             
                            <span class="badge">Enhanced with 6 Reasoning Modes</span>
         
     | 
| 57 | 
         
             
                            <span class="badge">Performance Optimized</span>
         
     | 
| 
         | 
|
| 58 | 
         
             
                        </div>
         
     | 
| 59 | 
         
             
                    </div>
         
     | 
| 60 | 
         
             
                    """)
         
     | 
| 61 | 
         | 
| 62 | 
         
             
                    with gr.Tabs():
         
     | 
| 63 | 
         
            -
                        # Main Chat Tab
         
     | 
| 64 | 
         
             
                        with gr.Tab("Reasoning Workspace"):
         
     | 
| 65 | 
         
             
                            with gr.Row():
         
     | 
| 66 | 
         
            -
                                with  
     | 
| 
         | 
|
| 67 | 
         
             
                                    chatbot = gr.Chatbot(
         
     | 
| 68 | 
         
             
                                        label="Reasoning Workspace",
         
     | 
| 69 | 
         
            -
                                        height= 
     | 
| 70 | 
         
             
                                        show_copy_button=True,
         
     | 
| 71 | 
         
             
                                        type="messages",
         
     | 
| 72 | 
         
             
                                        avatar_images=(
         
     | 
| 
         @@ -86,8 +142,11 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 86 | 
         
             
                                        submit_btn = gr.Button("Process", variant="primary", scale=2)
         
     | 
| 87 | 
         
             
                                        clear_btn = gr.Button("Clear", scale=1)
         
     | 
| 88 | 
         
             
                                        pdf_btn = gr.Button("Download PDF", scale=1)
         
     | 
| 
         | 
|
| 
         | 
|
| 89 | 
         | 
| 90 | 
         
            -
                                 
     | 
| 
         | 
|
| 91 | 
         
             
                                    gr.Markdown("### Configuration")
         
     | 
| 92 | 
         | 
| 93 | 
         
             
                                    reasoning_mode = gr.Radio(
         
     | 
| 
         @@ -151,6 +210,9 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 151 | 
         
             
                                        **Rate Limit:** {AppConfig.RATE_LIMIT_REQUESTS} req/{AppConfig.RATE_LIMIT_WINDOW}s  
         
     | 
| 152 | 
         
             
                                        **Max History:** {AppConfig.MAX_HISTORY_LENGTH} messages
         
     | 
| 153 | 
         
             
                                        """)
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 154 | 
         | 
| 155 | 
         
             
                        # Export Tab
         
     | 
| 156 | 
         
             
                        with gr.Tab("Export & History"):
         
     | 
| 
         @@ -231,12 +293,11 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 231 | 
         
             
                            clear_cache_btn = gr.Button("Clear Cache", variant="stop")
         
     | 
| 232 | 
         
             
                            cache_status = gr.Markdown("")
         
     | 
| 233 | 
         | 
| 234 | 
         
            -
                    #  
     | 
| 235 | 
         
            -
                    pdf_file_output = gr.File(visible=False)
         
     | 
| 236 | 
         | 
| 237 | 
         
            -
                    # Event handlers
         
     | 
| 238 | 
         
             
                    def process_message(message, history, mode, critique, model_name, temp, tokens, template, cache):
         
     | 
| 239 | 
         
            -
                         
     | 
| 
         | 
|
| 240 | 
         
             
                            return history, get_metrics_html()
         
     | 
| 241 | 
         | 
| 242 | 
         
             
                        history = history or []
         
     | 
| 
         @@ -247,30 +308,42 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 247 | 
         | 
| 248 | 
         
             
                        history.append({"role": "assistant", "content": ""})
         
     | 
| 249 | 
         | 
| 250 | 
         
            -
                         
     | 
| 251 | 
         
            -
                             
     | 
| 252 | 
         
            -
             
     | 
| 253 | 
         
            -
             
     | 
| 254 | 
         
            -
                             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 255 | 
         
             
                            yield history, get_metrics_html()
         
     | 
| 256 | 
         | 
| 257 | 
         
             
                    def reset_chat():
         
     | 
| 
         | 
|
| 258 | 
         
             
                        reasoner.clear_history()
         
     | 
| 259 | 
         
             
                        return [], get_metrics_html()
         
     | 
| 260 | 
         | 
| 261 | 
         
             
                    def export_conv(format_type, include_metadata):
         
     | 
| 
         | 
|
| 262 | 
         
             
                        content, filename = reasoner.export_conversation(format_type, include_metadata)
         
     | 
| 263 | 
         
            -
                         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 264 | 
         | 
| 265 | 
         
             
                    def download_chat_pdf():
         
     | 
| 266 | 
         
            -
                        """Download  
     | 
| 267 | 
         
             
                        pdf_file = reasoner.export_current_chat_pdf()
         
     | 
| 268 | 
         
             
                        if pdf_file:
         
     | 
| 
         | 
|
| 269 | 
         
             
                            return pdf_file
         
     | 
| 270 | 
         
             
                        return None
         
     | 
| 271 | 
         | 
| 272 | 
         
             
                    def search_conv(keyword):
         
     | 
| 273 | 
         
            -
                         
     | 
| 
         | 
|
| 274 | 
         
             
                            return "Please enter a search keyword."
         
     | 
| 275 | 
         | 
| 276 | 
         
             
                        results = reasoner.search_conversations(keyword)
         
     | 
| 
         @@ -280,7 +353,8 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 280 | 
         
             
                        output = f"### Found {len(results)} result(s) for '{keyword}'\n\n"
         
     | 
| 281 | 
         
             
                        for idx, entry in results[:10]:
         
     | 
| 282 | 
         
             
                            output += f"**{idx + 1}.** {entry.timestamp} | {entry.model}\n"
         
     | 
| 283 | 
         
            -
                             
     | 
| 
         | 
|
| 284 | 
         | 
| 285 | 
         
             
                        if len(results) > 10:
         
     | 
| 286 | 
         
             
                            output += f"\n*Showing first 10 of {len(results)} results*"
         
     | 
| 
         @@ -288,6 +362,7 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 288 | 
         
             
                        return output
         
     | 
| 289 | 
         | 
| 290 | 
         
             
                    def refresh_analytics():
         
     | 
| 
         | 
|
| 291 | 
         
             
                        analytics = reasoner.get_analytics()
         
     | 
| 292 | 
         
             
                        if not analytics:
         
     | 
| 293 | 
         
             
                            return get_empty_analytics_html(), "No cache data.", "No data", "No data"
         
     | 
| 
         @@ -309,6 +384,7 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 309 | 
         
             
                        - Hits: {analytics['cache_hits']}
         
     | 
| 310 | 
         
             
                        - Misses: {analytics['cache_misses']}
         
     | 
| 311 | 
         
             
                        - Total: {analytics['cache_hits'] + analytics['cache_misses']}
         
     | 
| 
         | 
|
| 312 | 
         
             
                        """
         
     | 
| 313 | 
         | 
| 314 | 
         
             
                        model_dist_html = f"**Model Usage:** {analytics['most_used_model']}"
         
     | 
| 
         @@ -317,18 +393,33 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 317 | 
         
             
                        return analytics_html, cache_html, model_dist_html, mode_dist_html
         
     | 
| 318 | 
         | 
| 319 | 
         
             
                    def update_history_stats():
         
     | 
| 
         | 
|
| 320 | 
         
             
                        count = len(reasoner.conversation_history)
         
     | 
| 321 | 
         
             
                        if count == 0:
         
     | 
| 322 | 
         
             
                            return "No conversations yet."
         
     | 
| 323 | 
         | 
| 324 | 
         
             
                        return f"""**Total Conversations:** {count}  
         
     | 
| 325 | 
         
            -
                        **Session:** {reasoner.session_id[:8]}... 
     | 
| 
         | 
|
| 326 | 
         | 
| 327 | 
         
             
                    def clear_cache_action():
         
     | 
| 
         | 
|
| 328 | 
         
             
                        reasoner.cache.clear()
         
     | 
| 329 | 
         
            -
                        return "Cache cleared successfully!"
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 330 | 
         | 
| 331 | 
         
            -
                    # Connect events
         
     | 
| 332 | 
         
             
                    submit_btn.click(
         
     | 
| 333 | 
         
             
                        process_message,
         
     | 
| 334 | 
         
             
                        [msg, chatbot, reasoning_mode, enable_critique, model, temperature, max_tokens, prompt_template, use_cache],
         
     | 
| 
         @@ -342,10 +433,15 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 342 | 
         
             
                    ).then(lambda: "", None, msg)
         
     | 
| 343 | 
         | 
| 344 | 
         
             
                    clear_btn.click(reset_chat, None, [chatbot, metrics_display])
         
     | 
| 345 | 
         
            -
                    
         
     | 
| 346 | 
         
            -
                    # PDF Download button
         
     | 
| 347 | 
         
             
                    pdf_btn.click(download_chat_pdf, None, pdf_file_output)
         
     | 
| 348 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 349 | 
         
             
                    export_btn.click(export_conv, [export_format, include_meta], [export_output, download_file])
         
     | 
| 350 | 
         
             
                    search_btn.click(search_conv, search_input, search_results)
         
     | 
| 351 | 
         
             
                    refresh_btn.click(
         
     | 
| 
         @@ -355,7 +451,6 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 355 | 
         
             
                    )
         
     | 
| 356 | 
         
             
                    clear_cache_btn.click(clear_cache_action, None, cache_status)
         
     | 
| 357 | 
         | 
| 358 | 
         
            -
                    # Update history stats on load
         
     | 
| 359 | 
         
             
                    demo.load(update_history_stats, None, history_stats)
         
     | 
| 360 | 
         | 
| 361 | 
         
             
                return demo
         
     | 
| 
         @@ -363,8 +458,9 @@ def create_ui() -> gr.Blocks: 
     | 
|
| 363 | 
         
             
            if __name__ == "__main__":
         
     | 
| 364 | 
         
             
                try:
         
     | 
| 365 | 
         
             
                    logger.info("="*60)
         
     | 
| 366 | 
         
            -
                    logger.info("Starting Advanced AI Reasoning System Pro...")
         
     | 
| 367 | 
         
            -
                    logger.info(f"Session ID: {reasoner.session_id}")
         
     | 
| 
         | 
|
| 368 | 
         
             
                    logger.info("="*60)
         
     | 
| 369 | 
         | 
| 370 | 
         
             
                    demo = create_ui()
         
     | 
| 
         @@ -376,6 +472,10 @@ if __name__ == "__main__": 
     | 
|
| 376 | 
         
             
                        show_api=False,
         
     | 
| 377 | 
         
             
                        favicon_path=None
         
     | 
| 378 | 
         
             
                    )
         
     | 
| 
         | 
|
| 
         | 
|
| 379 | 
         
             
                except Exception as e:
         
     | 
| 380 | 
         
            -
                    logger.critical(f"Failed to start application: {e}", exc_info=True)
         
     | 
| 381 | 
         
            -
                    raise
         
     | 
| 
         | 
|
| 
         | 
| 
         | 
|
| 5 | 
         
             
            # Initialize system
         
     | 
| 6 | 
         
             
            reasoner = AdvancedReasoner()
         
     | 
| 7 | 
         | 
| 8 | 
         
            +
            # Enhanced CSS with sidebar toggle animation
         
     | 
| 9 | 
         
            +
            SIDEBAR_CSS = CUSTOM_CSS + """
         
     | 
| 10 | 
         
            +
            /* Sidebar toggle animations */
         
     | 
| 11 | 
         
            +
            .sidebar-hidden {
         
     | 
| 12 | 
         
            +
                display: none !important;
         
     | 
| 13 | 
         
            +
            }
         
     | 
| 14 | 
         
            +
             
     | 
| 15 | 
         
            +
            .toggle-btn {
         
     | 
| 16 | 
         
            +
                position: fixed;
         
     | 
| 17 | 
         
            +
                right: 20px;
         
     | 
| 18 | 
         
            +
                top: 50%;
         
     | 
| 19 | 
         
            +
                transform: translateY(-50%);
         
     | 
| 20 | 
         
            +
                z-index: 1000;
         
     | 
| 21 | 
         
            +
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
         
     | 
| 22 | 
         
            +
                color: white;
         
     | 
| 23 | 
         
            +
                border: none;
         
     | 
| 24 | 
         
            +
                border-radius: 50%;
         
     | 
| 25 | 
         
            +
                width: 50px;
         
     | 
| 26 | 
         
            +
                height: 50px;
         
     | 
| 27 | 
         
            +
                cursor: pointer;
         
     | 
| 28 | 
         
            +
                box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
         
     | 
| 29 | 
         
            +
                transition: all 0.3s ease;
         
     | 
| 30 | 
         
            +
                font-size: 20px;
         
     | 
| 31 | 
         
            +
                display: flex;
         
     | 
| 32 | 
         
            +
                align-items: center;
         
     | 
| 33 | 
         
            +
                justify-content: center;
         
     | 
| 34 | 
         
            +
            }
         
     | 
| 35 | 
         
            +
             
     | 
| 36 | 
         
            +
            .toggle-btn:hover {
         
     | 
| 37 | 
         
            +
                transform: translateY(-50%) scale(1.1);
         
     | 
| 38 | 
         
            +
                box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
         
     | 
| 39 | 
         
            +
            }
         
     | 
| 40 | 
         
            +
             
     | 
| 41 | 
         
            +
            /* Fullscreen chat mode */
         
     | 
| 42 | 
         
            +
            .fullscreen-chat .gradio-container {
         
     | 
| 43 | 
         
            +
                max-width: 98% !important;
         
     | 
| 44 | 
         
            +
            }
         
     | 
| 45 | 
         
            +
             
     | 
| 46 | 
         
            +
            /* Smooth transitions */
         
     | 
| 47 | 
         
            +
            .settings-column {
         
     | 
| 48 | 
         
            +
                transition: all 0.3s ease-in-out;
         
     | 
| 49 | 
         
            +
            }
         
     | 
| 50 | 
         
            +
             
     | 
| 51 | 
         
            +
            /* Mobile responsive toggle */
         
     | 
| 52 | 
         
            +
            @media (max-width: 768px) {
         
     | 
| 53 | 
         
            +
                .toggle-btn {
         
     | 
| 54 | 
         
            +
                    width: 40px;
         
     | 
| 55 | 
         
            +
                    height: 40px;
         
     | 
| 56 | 
         
            +
                    font-size: 16px;
         
     | 
| 57 | 
         
            +
                    right: 10px;
         
     | 
| 58 | 
         
            +
                }
         
     | 
| 59 | 
         
            +
            }
         
     | 
| 60 | 
         
            +
            """
         
     | 
| 61 | 
         
            +
             
     | 
| 62 | 
         
             
            def get_metrics_html() -> str:
         
     | 
| 63 | 
         
             
                """Generate enhanced metrics HTML"""
         
     | 
| 64 | 
         
             
                m = reasoner.metrics
         
     | 
| 
         | 
|
| 88 | 
         
             
                </div>"""
         
     | 
| 89 | 
         | 
| 90 | 
         
             
            def create_ui() -> gr.Blocks:
         
     | 
| 91 | 
         
            +
                """Create enhanced Gradio interface with collapsible sidebar"""
         
     | 
| 92 | 
         | 
| 93 | 
         
             
                with gr.Blocks(
         
     | 
| 94 | 
         
             
                    theme=gr.themes.Soft(
         
     | 
| 
         | 
|
| 96 | 
         
             
                        secondary_hue=AppConfig.THEME_SECONDARY,
         
     | 
| 97 | 
         
             
                        font=gr.themes.GoogleFont("Inter")
         
     | 
| 98 | 
         
             
                    ),
         
     | 
| 99 | 
         
            +
                    css=SIDEBAR_CSS,
         
     | 
| 100 | 
         
             
                    title="Advanced AI Reasoning System Pro"
         
     | 
| 101 | 
         
             
                ) as demo:
         
     | 
| 102 | 
         | 
| 
         | 
|
| 109 | 
         
             
                            <span class="badge">Bai et al. 2022 - Constitutional AI</span>
         
     | 
| 110 | 
         
             
                            <span class="badge">Enhanced with 6 Reasoning Modes</span>
         
     | 
| 111 | 
         
             
                            <span class="badge">Performance Optimized</span>
         
     | 
| 112 | 
         
            +
                            <span class="badge">ποΈ Collapsible Sidebar</span>
         
     | 
| 113 | 
         
             
                        </div>
         
     | 
| 114 | 
         
             
                    </div>
         
     | 
| 115 | 
         
             
                    """)
         
     | 
| 116 | 
         | 
| 117 | 
         
             
                    with gr.Tabs():
         
     | 
| 118 | 
         
            +
                        # Main Chat Tab with Toggle
         
     | 
| 119 | 
         
             
                        with gr.Tab("Reasoning Workspace"):
         
     | 
| 120 | 
         
             
                            with gr.Row():
         
     | 
| 121 | 
         
            +
                                # CHAT AREA - Larger with toggle support
         
     | 
| 122 | 
         
            +
                                with gr.Column(scale=4):
         
     | 
| 123 | 
         
             
                                    chatbot = gr.Chatbot(
         
     | 
| 124 | 
         
             
                                        label="Reasoning Workspace",
         
     | 
| 125 | 
         
            +
                                        height=750,
         
     | 
| 126 | 
         
             
                                        show_copy_button=True,
         
     | 
| 127 | 
         
             
                                        type="messages",
         
     | 
| 128 | 
         
             
                                        avatar_images=(
         
     | 
| 
         | 
|
| 142 | 
         
             
                                        submit_btn = gr.Button("Process", variant="primary", scale=2)
         
     | 
| 143 | 
         
             
                                        clear_btn = gr.Button("Clear", scale=1)
         
     | 
| 144 | 
         
             
                                        pdf_btn = gr.Button("Download PDF", scale=1)
         
     | 
| 145 | 
         
            +
                                        # β NEW: Toggle Sidebar Button
         
     | 
| 146 | 
         
            +
                                        toggle_sidebar_btn = gr.Button("βοΈ Toggle Settings", scale=1, variant="secondary")
         
     | 
| 147 | 
         | 
| 148 | 
         
            +
                                # COLLAPSIBLE SIDEBAR
         
     | 
| 149 | 
         
            +
                                with gr.Column(scale=1, visible=True, elem_classes="settings-column") as sidebar:
         
     | 
| 150 | 
         
             
                                    gr.Markdown("### Configuration")
         
     | 
| 151 | 
         | 
| 152 | 
         
             
                                    reasoning_mode = gr.Radio(
         
     | 
| 
         | 
|
| 210 | 
         
             
                                        **Rate Limit:** {AppConfig.RATE_LIMIT_REQUESTS} req/{AppConfig.RATE_LIMIT_WINDOW}s  
         
     | 
| 211 | 
         
             
                                        **Max History:** {AppConfig.MAX_HISTORY_LENGTH} messages
         
     | 
| 212 | 
         
             
                                        """)
         
     | 
| 213 | 
         
            +
                            
         
     | 
| 214 | 
         
            +
                            # PDF download output
         
     | 
| 215 | 
         
            +
                            pdf_file_output = gr.File(label="Download Your PDF", visible=True)
         
     | 
| 216 | 
         | 
| 217 | 
         
             
                        # Export Tab
         
     | 
| 218 | 
         
             
                        with gr.Tab("Export & History"):
         
     | 
| 
         | 
|
| 293 | 
         
             
                            clear_cache_btn = gr.Button("Clear Cache", variant="stop")
         
     | 
| 294 | 
         
             
                            cache_status = gr.Markdown("")
         
     | 
| 295 | 
         | 
| 296 | 
         
            +
                    # ==================== EVENT HANDLERS ====================
         
     | 
| 
         | 
|
| 297 | 
         | 
| 
         | 
|
| 298 | 
         
             
                    def process_message(message, history, mode, critique, model_name, temp, tokens, template, cache):
         
     | 
| 299 | 
         
            +
                        """Process message with streaming"""
         
     | 
| 300 | 
         
            +
                        if not message or not message.strip():
         
     | 
| 301 | 
         
             
                            return history, get_metrics_html()
         
     | 
| 302 | 
         | 
| 303 | 
         
             
                        history = history or []
         
     | 
| 
         | 
|
| 308 | 
         | 
| 309 | 
         
             
                        history.append({"role": "assistant", "content": ""})
         
     | 
| 310 | 
         | 
| 311 | 
         
            +
                        try:
         
     | 
| 312 | 
         
            +
                            for response in reasoner.generate_response(
         
     | 
| 313 | 
         
            +
                                message, history[:-1], model_name, mode_enum, 
         
     | 
| 314 | 
         
            +
                                critique, temp, tokens, template, cache
         
     | 
| 315 | 
         
            +
                            ):
         
     | 
| 316 | 
         
            +
                                history[-1]["content"] = response
         
     | 
| 317 | 
         
            +
                                yield history, get_metrics_html()
         
     | 
| 318 | 
         
            +
                        except Exception as e:
         
     | 
| 319 | 
         
            +
                            history[-1]["content"] = f"β **Error:** {str(e)}\n\nPlease try again."
         
     | 
| 320 | 
         
            +
                            logger.error(f"Error in process_message: {e}", exc_info=True)
         
     | 
| 321 | 
         
             
                            yield history, get_metrics_html()
         
     | 
| 322 | 
         | 
| 323 | 
         
             
                    def reset_chat():
         
     | 
| 324 | 
         
            +
                        """Reset chat"""
         
     | 
| 325 | 
         
             
                        reasoner.clear_history()
         
     | 
| 326 | 
         
             
                        return [], get_metrics_html()
         
     | 
| 327 | 
         | 
| 328 | 
         
             
                    def export_conv(format_type, include_metadata):
         
     | 
| 329 | 
         
            +
                        """Export conversation"""
         
     | 
| 330 | 
         
             
                        content, filename = reasoner.export_conversation(format_type, include_metadata)
         
     | 
| 331 | 
         
            +
                        if filename:
         
     | 
| 332 | 
         
            +
                            return content, filename
         
     | 
| 333 | 
         
            +
                        else:
         
     | 
| 334 | 
         
            +
                            return content, None
         
     | 
| 335 | 
         | 
| 336 | 
         
             
                    def download_chat_pdf():
         
     | 
| 337 | 
         
            +
                        """Download PDF"""
         
     | 
| 338 | 
         
             
                        pdf_file = reasoner.export_current_chat_pdf()
         
     | 
| 339 | 
         
             
                        if pdf_file:
         
     | 
| 340 | 
         
            +
                            logger.info(f"PDF ready: {pdf_file}")
         
     | 
| 341 | 
         
             
                            return pdf_file
         
     | 
| 342 | 
         
             
                        return None
         
     | 
| 343 | 
         | 
| 344 | 
         
             
                    def search_conv(keyword):
         
     | 
| 345 | 
         
            +
                        """Search conversations"""
         
     | 
| 346 | 
         
            +
                        if not keyword or not keyword.strip():
         
     | 
| 347 | 
         
             
                            return "Please enter a search keyword."
         
     | 
| 348 | 
         | 
| 349 | 
         
             
                        results = reasoner.search_conversations(keyword)
         
     | 
| 
         | 
|
| 353 | 
         
             
                        output = f"### Found {len(results)} result(s) for '{keyword}'\n\n"
         
     | 
| 354 | 
         
             
                        for idx, entry in results[:10]:
         
     | 
| 355 | 
         
             
                            output += f"**{idx + 1}.** {entry.timestamp} | {entry.model}\n"
         
     | 
| 356 | 
         
            +
                            preview = entry.user_message[:100].replace('\n', ' ')
         
     | 
| 357 | 
         
            +
                            output += f"**User:** {preview}...\n\n"
         
     | 
| 358 | 
         | 
| 359 | 
         
             
                        if len(results) > 10:
         
     | 
| 360 | 
         
             
                            output += f"\n*Showing first 10 of {len(results)} results*"
         
     | 
| 
         | 
|
| 362 | 
         
             
                        return output
         
     | 
| 363 | 
         | 
| 364 | 
         
             
                    def refresh_analytics():
         
     | 
| 365 | 
         
            +
                        """Refresh analytics"""
         
     | 
| 366 | 
         
             
                        analytics = reasoner.get_analytics()
         
     | 
| 367 | 
         
             
                        if not analytics:
         
     | 
| 368 | 
         
             
                            return get_empty_analytics_html(), "No cache data.", "No data", "No data"
         
     | 
| 
         | 
|
| 384 | 
         
             
                        - Hits: {analytics['cache_hits']}
         
     | 
| 385 | 
         
             
                        - Misses: {analytics['cache_misses']}
         
     | 
| 386 | 
         
             
                        - Total: {analytics['cache_hits'] + analytics['cache_misses']}
         
     | 
| 387 | 
         
            +
                        - Hit Rate: {reasoner.cache.get_stats()['hit_rate']}%
         
     | 
| 388 | 
         
             
                        """
         
     | 
| 389 | 
         | 
| 390 | 
         
             
                        model_dist_html = f"**Model Usage:** {analytics['most_used_model']}"
         
     | 
| 
         | 
|
| 393 | 
         
             
                        return analytics_html, cache_html, model_dist_html, mode_dist_html
         
     | 
| 394 | 
         | 
| 395 | 
         
             
                    def update_history_stats():
         
     | 
| 396 | 
         
            +
                        """Update history"""
         
     | 
| 397 | 
         
             
                        count = len(reasoner.conversation_history)
         
     | 
| 398 | 
         
             
                        if count == 0:
         
     | 
| 399 | 
         
             
                            return "No conversations yet."
         
     | 
| 400 | 
         | 
| 401 | 
         
             
                        return f"""**Total Conversations:** {count}  
         
     | 
| 402 | 
         
            +
                        **Session:** {reasoner.session_id[:8]}...  
         
     | 
| 403 | 
         
            +
                        **Started:** {reasoner.metrics.session_start}"""
         
     | 
| 404 | 
         | 
| 405 | 
         
             
                    def clear_cache_action():
         
     | 
| 406 | 
         
            +
                        """Clear cache"""
         
     | 
| 407 | 
         
             
                        reasoner.cache.clear()
         
     | 
| 408 | 
         
            +
                        return "β
 Cache cleared successfully!"
         
     | 
| 409 | 
         
            +
                    
         
     | 
| 410 | 
         
            +
                    def toggle_sidebar(sidebar_state):
         
     | 
| 411 | 
         
            +
                        """β NEW: Toggle sidebar visibility"""
         
     | 
| 412 | 
         
            +
                        new_state = not sidebar_state
         
     | 
| 413 | 
         
            +
                        logger.info(f"Sidebar toggled: {'Hidden' if not new_state else 'Visible'}")
         
     | 
| 414 | 
         
            +
                        return gr.update(visible=new_state), new_state
         
     | 
| 415 | 
         
            +
                    
         
     | 
| 416 | 
         
            +
                    # ==================== STATE MANAGEMENT ====================
         
     | 
| 417 | 
         
            +
                    
         
     | 
| 418 | 
         
            +
                    # Track sidebar visibility state
         
     | 
| 419 | 
         
            +
                    sidebar_visible_state = gr.State(value=True)
         
     | 
| 420 | 
         
            +
                    
         
     | 
| 421 | 
         
            +
                    # ==================== CONNECT EVENTS ====================
         
     | 
| 422 | 
         | 
| 
         | 
|
| 423 | 
         
             
                    submit_btn.click(
         
     | 
| 424 | 
         
             
                        process_message,
         
     | 
| 425 | 
         
             
                        [msg, chatbot, reasoning_mode, enable_critique, model, temperature, max_tokens, prompt_template, use_cache],
         
     | 
| 
         | 
|
| 433 | 
         
             
                    ).then(lambda: "", None, msg)
         
     | 
| 434 | 
         | 
| 435 | 
         
             
                    clear_btn.click(reset_chat, None, [chatbot, metrics_display])
         
     | 
| 
         | 
|
| 
         | 
|
| 436 | 
         
             
                    pdf_btn.click(download_chat_pdf, None, pdf_file_output)
         
     | 
| 437 | 
         | 
| 438 | 
         
            +
                    # β NEW: Toggle Sidebar Event
         
     | 
| 439 | 
         
            +
                    toggle_sidebar_btn.click(
         
     | 
| 440 | 
         
            +
                        toggle_sidebar,
         
     | 
| 441 | 
         
            +
                        inputs=[sidebar_visible_state],
         
     | 
| 442 | 
         
            +
                        outputs=[sidebar, sidebar_visible_state]
         
     | 
| 443 | 
         
            +
                    )
         
     | 
| 444 | 
         
            +
                    
         
     | 
| 445 | 
         
             
                    export_btn.click(export_conv, [export_format, include_meta], [export_output, download_file])
         
     | 
| 446 | 
         
             
                    search_btn.click(search_conv, search_input, search_results)
         
     | 
| 447 | 
         
             
                    refresh_btn.click(
         
     | 
| 
         | 
|
| 451 | 
         
             
                    )
         
     | 
| 452 | 
         
             
                    clear_cache_btn.click(clear_cache_action, None, cache_status)
         
     | 
| 453 | 
         | 
| 
         | 
|
| 454 | 
         
             
                    demo.load(update_history_stats, None, history_stats)
         
     | 
| 455 | 
         | 
| 456 | 
         
             
                return demo
         
     | 
| 
         | 
|
| 458 | 
         
             
            if __name__ == "__main__":
         
     | 
| 459 | 
         
             
                try:
         
     | 
| 460 | 
         
             
                    logger.info("="*60)
         
     | 
| 461 | 
         
            +
                    logger.info("π Starting Advanced AI Reasoning System Pro...")
         
     | 
| 462 | 
         
            +
                    logger.info(f"π Session ID: {reasoner.session_id}")
         
     | 
| 463 | 
         
            +
                    logger.info("ποΈ Collapsible Sidebar: Enabled")
         
     | 
| 464 | 
         
             
                    logger.info("="*60)
         
     | 
| 465 | 
         | 
| 466 | 
         
             
                    demo = create_ui()
         
     | 
| 
         | 
|
| 472 | 
         
             
                        show_api=False,
         
     | 
| 473 | 
         
             
                        favicon_path=None
         
     | 
| 474 | 
         
             
                    )
         
     | 
| 475 | 
         
            +
                except KeyboardInterrupt:
         
     | 
| 476 | 
         
            +
                    logger.info("βΉοΈ Application stopped by user")
         
     | 
| 477 | 
         
             
                except Exception as e:
         
     | 
| 478 | 
         
            +
                    logger.critical(f"β Failed to start application: {e}", exc_info=True)
         
     | 
| 479 | 
         
            +
                    raise
         
     | 
| 480 | 
         
            +
                finally:
         
     | 
| 481 | 
         
            +
                    logger.info("π Shutting down gracefully...")
         
     |