Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| PomoCat - Your focus buddy with a paw-sitive vibe | |
| A Gradio-powered MCP server for managing pomodoro sessions with AI encouragement | |
| """ | |
| import gradio as gr | |
| import os | |
| import argparse | |
| from datetime import datetime, timedelta | |
| from typing import Dict, Any, List, Optional | |
| import json | |
| from dataclasses import dataclass | |
| # LLM API imports | |
| try: | |
| from anthropic import Anthropic | |
| ANTHROPIC_AVAILABLE = True | |
| except ImportError: | |
| ANTHROPIC_AVAILABLE = False | |
| print("β οΈ Anthropic library not available. AI encouragement will use fallback responses.") | |
| class PomoCatState: | |
| """State management for PomoCat sessions""" | |
| is_working: bool = False | |
| is_on_break: bool = False | |
| current_session_start: Optional[datetime] = None | |
| total_work_time: int = 0 # in minutes | |
| session_count: int = 0 | |
| last_break_time: Optional[datetime] = None | |
| work_start_time: Optional[datetime] = None | |
| def to_dict(self) -> Dict[str, Any]: | |
| """Convert state to dictionary for JSON serialization""" | |
| return { | |
| "is_working": self.is_working, | |
| "is_on_break": self.is_on_break, | |
| "current_session_start": self.current_session_start.isoformat() if self.current_session_start else None, | |
| "total_work_time": self.total_work_time, | |
| "session_count": self.session_count, | |
| "last_break_time": self.last_break_time.isoformat() if self.last_break_time else None, | |
| "work_start_time": self.work_start_time.isoformat() if self.work_start_time else None | |
| } | |
| # Global state instance | |
| pomocat_state = PomoCatState() | |
| def pomodoro_manager(user_query: str) -> str: | |
| """ | |
| Manage pomodoro work sessions and provide AI encouragement. | |
| This function handles all pomodoro-related commands including starting work sessions, | |
| taking breaks, checking status, and providing AI-powered motivational support. | |
| It enforces a minimum 20-minute work requirement before allowing breaks. | |
| Args: | |
| user_query (str): User command or message. Commands include: | |
| - "start working" / "start pomodoro" - Begin a 25-minute focus session | |
| - "take a break" / "rest" - Take a break (requires 20+ minutes of work) | |
| - "resume work" / "back to work" - End break and resume working | |
| - "stop" / "end session" - End current session completely | |
| - "status" / "check progress" - Get current session status | |
| - "help" / "commands" - Show available commands | |
| - Any other text - Get AI encouragement and motivation | |
| Returns: | |
| str: Response message with session status, encouragement, or command results | |
| """ | |
| global pomocat_state | |
| query = user_query.lower().strip() | |
| # Handle different commands | |
| if any(keyword in query for keyword in ["start work", "start pomodoro", "begin work", "start focus"]): | |
| return start_work_session() | |
| elif any(keyword in query for keyword in ["take a break", "start break", "break time", "rest"]): | |
| return start_break_mode() | |
| elif any(keyword in query for keyword in ["resume work", "back to work", "end break", "continue work"]): | |
| return resume_work() | |
| elif any(keyword in query for keyword in ["stop", "end session", "quit", "finish"]): | |
| return stop_session() | |
| elif any(keyword in query for keyword in ["status", "check progress", "how am i doing", "progress"]): | |
| return get_session_status() | |
| elif any(keyword in query for keyword in ["help", "commands", "what can you do"]): | |
| return get_help_text() | |
| else: | |
| # AI encouragement for any other input | |
| return provide_ai_encouragement(user_query) | |
| def get_session_status() -> str: | |
| """ | |
| Get current pomodoro session status and detailed progress statistics. | |
| This function provides comprehensive information about the current session including: | |
| work time elapsed, break status, total accumulated work time, and current activity. | |
| Returns: | |
| str: Detailed session status including work time, break duration, and progress info | |
| """ | |
| global pomocat_state | |
| if not pomocat_state.is_working and not pomocat_state.is_on_break: | |
| return "πΈ No active session. Ready to start working? Use 'start working' to begin! πΎ" | |
| current_work_time = calculate_work_time() if pomocat_state.is_working else 0 | |
| total_time = pomocat_state.total_work_time + current_work_time | |
| if pomocat_state.is_working: | |
| return f"πΊ Currently working! \nπΎ Current session: {current_work_time} minutes \nβ° Total work time: {total_time} minutes \nπͺ Keep up the great focus! You're doing fantastic! β¨" | |
| elif pomocat_state.is_on_break: | |
| break_duration = (datetime.now() - pomocat_state.last_break_time).seconds // 60 if pomocat_state.last_break_time else 0 | |
| return f"πΊπΊπΊ Currently on break! \nπΎ Break duration: {break_duration} minutes \nβ° Total work time: {total_time} minutes \nπΈ Enjoy your rest! Use 'resume work' when ready to continue! π" | |
| def reset_session() -> str: | |
| """ | |
| Reset the current pomodoro session completely and clear all progress. | |
| This function resets all session data including work time, break status, | |
| timers, and accumulated statistics. Use this to start completely fresh. | |
| Returns: | |
| str: Confirmation message that the session has been successfully reset | |
| """ | |
| global pomocat_state | |
| pomocat_state = PomoCatState() | |
| return "πΊ Session reset! All timers and progress cleared. Ready for a fresh start? πΎβ¨" | |
| def start_work_session() -> str: | |
| """Start a new work session""" | |
| global pomocat_state | |
| if pomocat_state.is_working: | |
| elapsed = calculate_work_time() | |
| return f"πΊ You're already working! Keep it up! Current session: {elapsed} minutes" | |
| pomocat_state.is_working = True | |
| pomocat_state.is_on_break = False | |
| pomocat_state.current_session_start = datetime.now() | |
| pomocat_state.work_start_time = datetime.now() | |
| return "πΊ Awesome! Pomodoro timer started! Focus time: 25 minutes... \nπΎ *purrs encouragingly* You've got this! Stay focused and let's make some progress together! πͺβ¨" | |
| def start_break_mode() -> str: | |
| """Start break mode with time validation""" | |
| global pomocat_state | |
| if not pomocat_state.is_working: | |
| return "πΈ You're not currently working! Use 'start working' to begin a pomodoro session first. πΎ" | |
| # Calculate elapsed work time | |
| elapsed_minutes = calculate_work_time() | |
| if elapsed_minutes < 20: | |
| remaining = 20 - elapsed_minutes | |
| return f"πΎ Meow! You need to work at least 20 minutes before taking a break! You've worked for {elapsed_minutes} minutes, keep going for {remaining} more minutes! πͺ \nπ± *encouraging purr* You're doing great - just a little bit more focus time! π" | |
| # Allow break | |
| pomocat_state.is_on_break = True | |
| pomocat_state.is_working = False | |
| pomocat_state.last_break_time = datetime.now() | |
| if elapsed_minutes >= 25: | |
| return f"π» Perfect! You've completed {elapsed_minutes} minutes of focused work! Time for a well-deserved break! π \nπΊπΊπΊ *stretches paws* Enjoy your break! You've earned it! πΎβ¨" | |
| else: | |
| return f"πΈ Good job! You've worked for {elapsed_minutes} minutes. While you haven't completed the full 25-minute session, you've earned a break! \nπΊπΊπΊ *gentle purr* Take a moment to rest, then we can tackle more work together! π" | |
| def resume_work() -> str: | |
| """Resume work session""" | |
| global pomocat_state | |
| if not pomocat_state.is_on_break: | |
| if pomocat_state.is_working: | |
| return "πΊ You're already working! Keep up the great focus! πΎ" | |
| else: | |
| return "πΈ You're not on a break. Use 'start working' to begin a new pomodoro session! πΎ" | |
| pomocat_state.is_working = True | |
| pomocat_state.is_on_break = False | |
| pomocat_state.work_start_time = datetime.now() # Reset work start time for new session | |
| return "π» Welcome back! Focus mode resumed. Let's tackle this together! π \nπΎ *determined purr* Time to get back into the flow! You're doing amazing! πͺβ¨" | |
| def stop_session() -> str: | |
| """Stop the current session""" | |
| global pomocat_state | |
| if not pomocat_state.is_working and not pomocat_state.is_on_break: | |
| return "πΈ No active session to stop. You're all set! πΎ" | |
| elapsed = calculate_work_time() if pomocat_state.is_working else 0 | |
| total_time = pomocat_state.total_work_time + elapsed | |
| # Reset state | |
| pomocat_state = PomoCatState() | |
| return f"πΊ Session ended! Great work today! \nπΎ Total focused time: {total_time} minutes. You should be proud of your effort! π \nπ» *content purr* Take care and see you next time! π" | |
| def provide_ai_encouragement(user_input: str) -> str: | |
| """ | |
| Provide AI-powered encouragement based on user input using Claude API. | |
| Analyzes the user's message and provides personalized, cat-themed | |
| motivational responses to help with focus and productivity. | |
| Args: | |
| user_input: The user's message or question about motivation, challenges, or feelings | |
| Returns: | |
| Encouraging cat-themed response tailored to the user's emotional state | |
| """ | |
| # Get current session context for more personalized responses | |
| global pomocat_state | |
| current_work_time = calculate_work_time() if pomocat_state.is_working else 0 | |
| total_time = pomocat_state.total_work_time + current_work_time | |
| session_context = "" | |
| if pomocat_state.is_working: | |
| session_context = f"The user is currently in a work session ({current_work_time} minutes so far, {total_time} total minutes today)." | |
| elif pomocat_state.is_on_break: | |
| session_context = f"The user is currently on a break (total work time today: {total_time} minutes)." | |
| else: | |
| session_context = f"The user has no active session (total work time today: {total_time} minutes)." | |
| # Try to use Claude API if available | |
| api_key = os.getenv('ANTHROPIC_API_KEY') | |
| if ANTHROPIC_AVAILABLE and api_key: | |
| try: | |
| client = Anthropic(api_key=api_key) | |
| prompt = f"""You are PomoCat, a friendly and encouraging productivity assistant with a cat theme. | |
| Your role is to provide supportive, motivational responses to help users with their work and focus. | |
| Context: {session_context} | |
| User message: "{user_input}" | |
| Please respond with: | |
| - Cat-themed language and emojis (πΊπΈπ»πΎetc.) | |
| - Warm, encouraging tone | |
| - Practical advice when appropriate | |
| - Keep responses conversational and supportive | |
| - 2-3 sentences maximum | |
| - Use cat behavior metaphors when helpful | |
| Respond as PomoCat would:""" | |
| response = client.messages.create( | |
| model="claude-sonnet-4-20250514", | |
| max_tokens=200, | |
| temperature=0.7, | |
| messages=[{"role": "user", "content": prompt}] | |
| ) | |
| return response.content[0].text.strip() | |
| except Exception as e: | |
| print(f"β οΈ Claude API error: {e}") | |
| print(f" API Key present: {'Yes' if api_key else 'No'}") | |
| print(f" Using fallback responses instead...") | |
| # Fall back to keyword-based responses | |
| else: | |
| if not ANTHROPIC_AVAILABLE: | |
| print("β οΈ Anthropic library not available") | |
| elif not api_key: | |
| print("β οΈ ANTHROPIC_API_KEY environment variable not set") | |
| # Fallback to keyword-based responses when API is not available | |
| return get_fallback_encouragement(user_input) | |
| def get_fallback_encouragement(user_input: str) -> str: | |
| """Fallback encouragement responses when AI API is not available""" | |
| input_lower = user_input.lower() | |
| if any(word in input_lower for word in ["stress", "overwhelm", "anxious", "worried", "pressure"]): | |
| return "πΈ *purrs softly* I can sense you're feeling a bit overwhelmed, but remember that even the biggest projects are just a series of small, focused steps! πΎ Take a deep breath with me - you're more capable than you know! πͺβ¨" | |
| elif any(word in input_lower for word in ["tired", "exhausted", "drained", "fatigue"]): | |
| return "π΄ *yawns sympathetically* It sounds like you need some rest! Remember, even cats need their naps to stay sharp! π± Maybe take a short break, stretch those muscles, and come back refreshed! π€β¨" | |
| elif any(word in input_lower for word in ["difficult", "hard", "challenging", "tough", "struggle"]): | |
| return "πΌ *sits up alertly* Challenges are just opportunities in disguise! Every difficult moment is making you stronger! πΎ Break it down into smaller pieces - even the mightiest cat catches mice one at a time! π¦πͺ" | |
| elif any(word in input_lower for word in ["motivation", "inspire", "encourage", "boost"]): | |
| return "π» *purrs enthusiastically* You're already taking the right steps by being here! Every small action is progress! π Remember, I believe in you completely - you have everything you need inside you already! πΎπ" | |
| elif any(word in input_lower for word in ["success", "complete", "finish", "achieve", "done", "accomplish"]): | |
| return "π» *happy purr* That's amazing! I'm so proud of you! π Celebrating your wins is just as important as the work itself! πΎ You're building incredible momentum - keep this energy going! β¨π" | |
| elif any(word in input_lower for word in ["procrastinate", "delay", "avoid", "postpone"]): | |
| return "πΊ *gentle head bump* We all have those moments! The secret is to start with just one tiny step - even 5 minutes of work is better than none! πΎ I'll be right here cheering you on! Sometimes the hardest part is just beginning! πͺβ¨" | |
| elif any(word in input_lower for word in ["focus", "concentrate", "attention", "distract"]): | |
| return "πΈ *alert ears* Focus is like a muscle - it gets stronger with practice! πΎ Try the pomodoro technique: 25 minutes of focused work, then a short break! Even cats know when to hunt and when to rest! π―β¨" | |
| else: | |
| # General encouraging response | |
| return "πΊ *purrs warmly* I'm here to support you through your work journey! πΎ Whatever you're working on, remember that progress is progress, no matter how small! πͺ You've got this! β¨" | |
| def calculate_work_time() -> int: | |
| """Calculate elapsed work time in minutes""" | |
| global pomocat_state | |
| if not pomocat_state.work_start_time: | |
| return 0 | |
| return int((datetime.now() - pomocat_state.work_start_time).total_seconds() / 60) | |
| def get_help_text() -> str: | |
| """Get help text with available commands""" | |
| return """πΊ PomoCat Commands Help πΎ | |
| Work Session Commands: | |
| β’ "start working" / "start pomodoro" - Begin a 25-minute focus session | |
| β’ "take a break" / "rest" - Take a break (minimum 20 minutes work required) | |
| β’ "resume work" / "back to work" - End break and resume working | |
| β’ "stop" / "end session" - End current session completely | |
| Status Commands: | |
| β’ "status" / "check progress" - See current session status | |
| β’ "help" / "commands" - Show this help message | |
| πΎ Special Features: | |
| β’ Must work at least 20 minutes before breaks are allowed | |
| β’ AI encouragement for any questions or concerns | |
| β’ Session tracking and progress monitoring | |
| π» Just chat with me about anything - I'm here to encourage and support you! π | |
| """ | |
| # Create Gradio interface with MCP support | |
| def create_gradio_interface(): | |
| """Create the main Gradio interface for PomoCat with MCP support""" | |
| with gr.Blocks( | |
| title="π± PomoCat - Your Focus Buddy" | |
| ) as demo: | |
| gr.Markdown(""" | |
| # π± PomoCat - Your Focus Buddy with a Paw-sitive Vibe | |
| Welcome to PomoCat! I'm here to help you stay focused with the Pomodoro technique. | |
| **π― Key Features:** | |
| - 25-minute focused work sessions | |
| - 20-minute minimum work time before breaks | |
| - AI-powered encouragement and support | |
| - Session tracking and progress monitoring | |
| **π§ Available APIs:** | |
| - `pomodoro_manager(user_query)` - Main interface: start/stop sessions, AI encouragement | |
| - `get_session_status()` - Get detailed session status and progress statistics | |
| - `reset_session()` - Reset current session and clear all progress | |
| **π€ AI Features:** | |
| - Real AI-powered encouragement using Claude API | |
| - Set ANTHROPIC_API_KEY environment variable for full AI features | |
| - Fallback to smart keyword-based responses if API not configured | |
| **π Quick Start:** | |
| Just type commands like "start working", "take a break", or ask me anything for encouragement! | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| user_input = gr.Textbox( | |
| label="π¬ Chat with PomoCat", | |
| placeholder="Type 'start working' to begin, or ask me anything for encouragement...", | |
| lines=2 | |
| ) | |
| submit_btn = gr.Button("Send πΎ", variant="primary") | |
| with gr.Column(scale=1): | |
| status_btn = gr.Button("Check Status π") | |
| reset_btn = gr.Button("Reset Session π") | |
| help_btn = gr.Button("Help π‘") | |
| output = gr.Textbox( | |
| label="π± PomoCat Response", | |
| lines=8, | |
| max_lines=20, | |
| interactive=False | |
| ) | |
| # Event handlers | |
| submit_btn.click( | |
| fn=pomodoro_manager, | |
| inputs=[user_input], | |
| outputs=[output] | |
| ) | |
| user_input.submit( | |
| fn=pomodoro_manager, | |
| inputs=[user_input], | |
| outputs=[output] | |
| ) | |
| status_btn.click( | |
| fn=get_session_status, | |
| outputs=[output] | |
| ) | |
| reset_btn.click( | |
| fn=reset_session, | |
| outputs=[output] | |
| ) | |
| help_btn.click( | |
| fn=get_help_text, | |
| outputs=[output] | |
| ) | |
| # Example interactions | |
| gr.Examples( | |
| examples=[ | |
| ["start working"], | |
| ["take a break"], | |
| ["status"], | |
| ["I'm feeling overwhelmed with my project"], | |
| ["How can I stay motivated?"], | |
| ["resume work"], | |
| ["stop"] | |
| ], | |
| inputs=[user_input], | |
| outputs=[output], | |
| fn=pomodoro_manager | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| # Parse command line arguments | |
| parser = argparse.ArgumentParser(description="π± PomoCat - Your Focus Buddy with a Paw-sitive Vibe") | |
| parser.add_argument("--port", type=int, default=7860, help="Port to run the server on (default: 7860)") | |
| parser.add_argument("--host", default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)") | |
| args = parser.parse_args() | |
| print(f"π± Starting PomoCat...") | |
| print(f"π‘ Server: http://{args.host}:{args.port}") | |
| # Note: MCP functionality depends on Gradio version compatibility | |
| # Create and launch the Gradio interface | |
| demo = create_gradio_interface() | |
| # Launch the interface | |
| demo.launch( | |
| share=False, | |
| server_name=args.host, | |
| server_port=args.port, | |
| show_error=True, | |
| mcp_server=True | |
| ) |