""" FocusFlow: AI Accountability Agent with Gradio 5 Interface. Configurable via environment variables for HuggingFace Spaces or local use. """ import gradio as gr import os from dotenv import load_dotenv from storage import TaskManager from monitor import FileMonitor from metrics import MetricsTracker from voice import voice_generator from linear_client import LinearClient from core.pomodoro import PomodoroTimer from core.focus_check import FocusMonitor from ui.handlers import UIHandlers from ui.layout import create_app # Load environment variables load_dotenv() # Debug: Print Configuration print("-" * 40) print("๐Ÿ”ง FOCUSFLOW CONFIGURATION") print(f" LAUNCH_MODE: {os.getenv('LAUNCH_MODE', 'demo')}") print(f" AI_PROVIDER: {os.getenv('AI_PROVIDER', 'openai')}") print(f" MONITOR_INTERVAL: {os.getenv('MONITOR_INTERVAL', '30')}") print(f" ENABLE_MCP: {os.getenv('ENABLE_MCP', 'true')}") print(f" HF_SPACE: {'Yes' if os.getenv('SPACE_ID') or os.getenv('huggingface_space_id') else 'No'}") # Masked Keys for safety for key in ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY", "LINEAR_API_KEY", "DEMO_OPENAI_API_KEY", "DEMO_ANTHROPIC_API_KEY", "DEMO_GEMINI_API_KEY"]: val = os.getenv(key) status = f"Set ({val[:4]}...)" if val else "Not Set" print(f" {key:<22}: {status}") print("-" * 40) # Import MCP tools to register them with Gradio try: import mcp_tools MCP_AVAILABLE = True except Exception as e: print(f"โš ๏ธ MCP tools not available: {e}") MCP_AVAILABLE = False # Configuration from environment LAUNCH_MODE = os.getenv("LAUNCH_MODE", "demo").lower() # 'demo' or 'local' AI_PROVIDER = os.getenv("AI_PROVIDER", "openai").lower() # 'openai', 'anthropic', or 'vllm' MONITOR_INTERVAL = int(os.getenv("MONITOR_INTERVAL", "30")) # seconds # Initialize Core Components task_manager = TaskManager() file_monitor = FileMonitor() metrics_tracker = MetricsTracker() linear_client = LinearClient() # Initialize Logic Modules focus_monitor = FocusMonitor(task_manager, file_monitor, metrics_tracker, voice_generator) focus_monitor.set_launch_mode(LAUNCH_MODE) pomodoro_timer = PomodoroTimer() # Initialize UI Handlers ui_handlers = UIHandlers(task_manager, file_monitor, metrics_tracker, focus_monitor, linear_client) # Register cleanup handler import atexit import signal import sys def cleanup(): """Cleanup resources on shutdown.""" print("๐Ÿงน Cleaning up resources...") if file_monitor.is_running(): print(" Stopping file monitor...") file_monitor.stop() try: print(" Closing Gradio app...") app.close() except Exception as e: print(f" Error closing app: {e}") atexit.register(cleanup) def handle_sigterm(*args): """Handle SIGTERM signal from K8s/Docker.""" print("๐Ÿ›‘ Received SIGTERM, initiating shutdown...") sys.exit(0) signal.signal(signal.SIGTERM, handle_sigterm) # Create App app = create_app(ui_handlers, pomodoro_timer, LAUNCH_MODE, AI_PROVIDER, MONITOR_INTERVAL) if __name__ == "__main__": # Enable MCP server if available mcp_enabled = os.getenv("ENABLE_MCP", "true").lower() == "true" try: if MCP_AVAILABLE and mcp_enabled: print("๐Ÿ”— MCP Server enabled! Connect via Claude Desktop or other MCP clients.") app.launch(server_name="0.0.0.0", server_port=7860, share=False, mcp_server=True) else: print("๐Ÿ“ฑ Running without MCP integration") app.launch(server_name="0.0.0.0", server_port=7860, share=False) except KeyboardInterrupt: print("๐Ÿ‘‹ FocusFlow stopped by user") except Exception as e: print(f"โŒ Unexpected error: {e}")