```
#!/bin/python
import os
import json
import subprocess
import threading
from datetime import datetime
import ollama
import time
import sys
import signal

# Configuration
ALARM_SOUND_FILE = "~/Music/166467_rocket_race.mp3"
PERSISTENT_STORAGE_FILE = "alarm_chat_history.json"
SCHEDULE_FILE = "user_schedule.json"
OLLAMA_MODEL = "gemma3:1b"
SUMMARY_INTERVAL = 600  # 10 minutes in seconds

# Escape sequences
ESCAPE_SEQUENCES = {
    "stop_alarm": "<<STOP_ALARM>>",
    "snooze": "<<SNOOZE>>",
    "add_to_schedule": "<<ADD_TO_SCHEDULE>>",
    "remove_from_schedule": "<<REMOVE_FROM_SCHEDULE>>",
}

class AlarmChatApp:
    def __init__(self):
        # Initialize all attributes first
        self.alarm_active = False
        self.mpv_process = None
        self.tts_process = None
        self.chat_history = []
        self.current_conversation = None
        self.last_summary_time = time.time()
        self.tts_available = self.check_tts_available()

        # Verify requirements before proceeding
        self.verify_system_requirements()
        self.load_persistent_data()

        # Setup signal handlers
        signal.signal(signal.SIGINT, self.signal_handler)
        signal.signal(signal.SIGTERM, self.signal_handler)
        signal.signal(signal.SIGHUP, self.signal_handler)
        signal.signal(signal.SIGQUIT, self.signal_handler)
        signal.signal(signal.SIGTRAP, self.signal_handler)
        signal.signal(signal.SIGSYS, self.signal_handler)

    def check_tts_available(self):
        """Check if spd-say is available"""
        try:
            subprocess.run(['spd-say', '--version'],
                          stdout=subprocess.DEVNULL,
                          stderr=subprocess.DEVNULL)
            return True
        except (FileNotFoundError, subprocess.SubprocessError):
            print("Warning: spd-say not found. Falling back to text-only mode.")
            return False

    def load_persistent_data(self):
        """Load chat history and user schedule from files"""
        try:
            with open(PERSISTENT_STORAGE_FILE, 'r') as f:
                self.chat_history = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError) as e:
            print(f"Warning: Could not load chat history - {str(e)}")
            self.chat_history = []

        try:
            with open(SCHEDULE_FILE, 'r') as f:
                self.user_schedule = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError) as e:
            print(f"Warning: Could not load schedule - {str(e)}")
            self.user_schedule = {
                "wakeup_times": [],
                "preferred_wakeup_questions": [],
                "emergency_contacts": []
            }

    def verify_system_requirements(self):
        """Check for all required components"""
        # Verify Ollama is installed
        try:
            subprocess.run(['ollama', '--version'],
                         stdout=subprocess.DEVNULL,
                         stderr=subprocess.DEVNULL)
        except FileNotFoundError:
            print("Error: Ollama is required but not found. Please install Ollama.")
            sys.exit(1)

        # Verify model is available
        if not self.verify_model():
            print(f"Error: Model '{OLLAMA_MODEL}' is not available.")
            print(f"Please install it with: ollama pull {OLLAMA_MODEL}")
            sys.exit(1)

    def verify_model(self):
        """Check if the required model is available"""
        try:
            models = ollama.list()
            return any(m['model'] == OLLAMA_MODEL for m in models.get('models', []))
        except Exception as e:
            print(f"Error checking for model: {str(e)}")
            return False

    def text_to_speech(self, text):
        """Convert text to speech using spd-say"""
        if not self.tts_available or not text.strip():
            return

        # Stop any previous TTS
        if self.tts_process and self.tts_process.poll() is None:
            self.tts_process.terminate()

        try:
            self.tts_process = subprocess.Popen(
                ['spd-say','-w', text],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
        except Exception as e:
            print(f"Error in TTS: {str(e)}")
            self.tts_available = False

    def generate_system_prompt(self):
        """Generate the system prompt for the LLM"""
        try:
            current_time = datetime.now()
            weekday = current_time.strftime("%A")
            time_str = current_time.strftime("%H:%M")

            today_schedule = next((day for day in self.user_schedule["wakeup_times"]
                                 if day["weekday"] == weekday), None)

            schedule_info = f"Today is {weekday}. "
            if today_schedule:
                schedule_info += f"Scheduled wake-up time: {today_schedule['time']}. "
                if today_schedule["important_events"]:
                    schedule_info += "Important events: " + ", ".join(today_schedule["important_events"])
            else:
                schedule_info += "No specific wake-up time scheduled for today."

            return f"""You are an intelligent alarm clock helping the user wake up. Your primary goal is to determine if the user is truly awake and alert. Talk like police officer interrogating a suspect, or military officer to subordinate.

Current time: {time_str}
{schedule_info}

Special commands available:
{ESCAPE_SEQUENCES["stop_alarm"]} - Stop the alarm (only when absolutely sure user is awake)
{ESCAPE_SEQUENCES["snooze"]} - Snooze for 5 minutes (if user seems groggy)

Assessment criteria for wakefulness:
1. Responses should be coherent and specific
2. Should demonstrate awareness of time/surroundings
3. Should show intention to start the day

Start by asking a specific question that requires a thoughtful answer.
"""
        except Exception as e:
            print(f"Error generating system prompt: {str(e)}")
            return """You are an alarm clock assistant. Determine if the user is awake by asking thoughtful questions.
Use <<STOP_ALARM>> when convinced they're awake, or <<SNOOZE>> if they need more time."""

    def generate_periodic_summary(self):
        """Generate a periodic summary message"""
        try:
            current_time = datetime.now().strftime("%H:%M")
            conv_duration = int(time.time() - self.current_conversation["start_time"])

            summary = f"""<<PERIODIC_SUMMARY>>
Time: {current_time}
Conversation duration: {conv_duration} seconds
Messages exchanged: {len(self.current_conversation["messages"])//2}

Assessment: {'User appears awake' if self.check_wakefulness() else 'User still seems sleepy'}
Recommended action: {ESCAPE_SEQUENCES["stop_alarm"] if self.check_wakefulness() else ESCAPE_SEQUENCES["snooze"]}
"""
            return summary
        except Exception as e:
            print(f"Error generating summary: {str(e)}")
            return "<<PERIODIC_SUMMARY>>\nError generating summary\n"

    def check_wakefulness(self):
        """Analyze conversation to determine if user is awake"""
        try:
            if not self.current_conversation or len(self.current_conversation["messages"]) < 4:
                return False

            last_responses = [msg["user"] for msg in self.current_conversation["messages"]
                             if isinstance(msg, dict) and "user" in msg][-2:]

            return all(len(response.split()) > 5 for response in last_responses)
        except Exception as e:
            print(f"Error checking wakefulness: {str(e)}")
            return False

    def chat_with_llm(self, user_input=None):
        """Send input to LLM and process response"""
        try:
            messages = [{"role": "system", "content": self.generate_system_prompt()}]

            if user_input:
                messages.append({"role": "user", "content": user_input})

            response = ollama.chat(
                model=OLLAMA_MODEL,
                messages=messages,
                options={"temperature": 0.7}
            )

            llm_response = response['message']['content']

            action_taken = None
            for cmd, seq in ESCAPE_SEQUENCES.items():
                if seq in llm_response:
                    llm_response = llm_response.replace(seq, "").strip()
                    action_taken = cmd
                    self.handle_escape_sequence(cmd)

            # Speak the response
            if llm_response.strip():
                self.text_to_speech(llm_response)

            return llm_response, action_taken
        except Exception as e:
            print(f"Error communicating with LLM: {str(e)}")
            return "I'm having trouble understanding. Can you repeat that?", None

    def handle_escape_sequence(self, cmd):
        """Handle LLM escape sequences"""
        try:
            print(f"\n[System executing command: {cmd}]")

            if cmd == "stop_alarm":
                self.stop_alarm()
                if self.current_conversation:
                    self.current_conversation["woke_up"] = True
            elif cmd == "snooze":
                self.stop_alarm()
                print("Snoozing for 5 minutes...")
                try:
                    threading.Timer(300, self.play_alarm).start()
                except Exception as e:
                    print(f"Error setting up snooze timer: {str(e)}")
        except Exception as e:
            print(f"Error handling command {cmd}: {str(e)}")

    def play_alarm(self):
        """Play alarm sound in a loop using mpv"""
        if not os.path.exists(ALARM_SOUND_FILE):
            print("Warning: Alarm sound file not found. Continuing without audio.")
            self.alarm_active = True
            return

        try:
            self.alarm_active = True
            self.mpv_process = subprocess.Popen(
                ['mpv', '--no-video', ALARM_SOUND_FILE],
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
                preexec_fn=os.setsid
            )
        except Exception as e:
            print(f"Error starting alarm sound: {str(e)}")
            self.alarm_active = True  # Continue without sound

    def stop_alarm(self):
        """Stop the alarm sound and ensure process is cleaned up"""
        if not self.alarm_active:
            return

        self.alarm_active = False

        if self.mpv_process:
            try:
                # Kill the entire process group
                os.killpg(os.getpgid(self.mpv_process.pid), signal.SIGTERM)
                self.mpv_process.wait(timeout=2)
            except ProcessLookupError:
                pass  # Process already dead
            except subprocess.TimeoutExpired:
                try:
                    os.killpg(os.getpgid(self.mpv_process.pid), signal.SIGKILL)
                except ProcessLookupError:
                    pass
            except Exception as e:
                print(f"Error stopping alarm process: {str(e)}")
            finally:
                self.mpv_process = None

    def signal_handler(self, signum, frame):
        """Handle interrupt signals"""
        # print(f"\nReceived signal {signum}, shutting down...")
        print(f"\nReceived signal {signum}, doing nothing")
        # self.cleanup()
        # sys.exit(1)

    def cleanup(self):
        """Ensure all resources are properly cleaned up"""
        self.stop_alarm()
        # Stop TTS if running
        if self.tts_process and self.tts_process.poll() is None:
            self.tts_process.terminate()
            self.tts_process = None

        if self.current_conversation and not self.current_conversation.get("woke_up", False):
            self.current_conversation["woke_up"] = False
            self.chat_history.append(self.current_conversation)
            self.save_persistent_data()

    def save_persistent_data(self):
        """Save chat history to file"""
        try:
            with open(PERSISTENT_STORAGE_FILE, 'w') as f:
                json.dump(self.chat_history[-10:], f)
        except IOError as e:
            print(f"Error saving chat history: {str(e)}")

    def run(self):
        """Main chat loop"""
        print("\n=== Alarm Chat App ===")
        print("The alarm will sound until you demonstrate you're fully awake.")
        print("Type 'quit' to exit (but the alarm will keep ringing!)\n")

        try:
            # Initialize conversation
            self.current_conversation = {
                "start_time": time.time(),
                "timestamp": datetime.now().isoformat(),
                "messages": [],
                "woke_up": False
            }

            self.play_alarm()

            # Start with AI message
            try:
                initial_msg, _ = self.chat_with_llm()
                print(f"Assistant: {initial_msg}")
                self.current_conversation["messages"].append({"assistant": initial_msg})
                self.last_summary_time = time.time()
            except Exception as e:
                print(f"Error starting conversation: {str(e)}")
                initial_msg = "Are you awake? Please respond with more than just 'yes'."
                print(f"Assistant: {initial_msg}")
                self.current_conversation["messages"].append({"assistant": initial_msg})

            while self.alarm_active:
                try:
                    # Check if it's time for a periodic summary
                    if time.time() - self.last_summary_time >= SUMMARY_INTERVAL:
                        summary = self.generate_periodic_summary()
                        print(f"\n[Periodic Summary]\n{summary}")
                        self.last_summary_time = time.time()

                        # Process any commands in the summary
                        for cmd, seq in ESCAPE_SEQUENCES.items():
                            if seq in summary:
                                self.handle_escape_sequence(cmd)

                    # Get user input
                    try:
                        user_input = input("You: ")
                    except EOFError:
                        print("\nEnd of input received")
                        continue
                        # break
                    except KeyboardInterrupt:
                        print("\nInterrupt received")
                        raise

                    # if user_input.lower() == 'quit':
                        # break

                    # Add to conversation
                    self.current_conversation["messages"].append({"user": user_input})

                    # Get LLM response
                    llm_response, action_taken = self.chat_with_llm(user_input)

                    if llm_response:
                        print(f"Assistant: {llm_response}")
                        self.current_conversation["messages"].append({"assistant": llm_response})

                    if action_taken == "stop_alarm":
                        break

                except Exception as e:
                    print(f"Error in main loop: {str(e)}")
                    time.sleep(1)  # Prevent tight error loop

        except Exception as e:
            print(f"Fatal error: {str(e)}")
        finally:
            self.cleanup()
            if self.alarm_active:
                print("\nWarning: Alarm was not properly stopped!")
            else:
                print("\nAlarm stopped. Have a productive day!")

if __name__ == "__main__":
    app = AlarmChatApp()
    app.run()
```
Modify this code to make it use persistent chat to generate hint prompt that says of past mistakes instead of appending whole past chat to current chat. Also add support of modifying schedule with escape sequences.