Spaces:
Running
Running
| """ | |
| 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}") | |