We have this code base for an autonomous AI agent, while it runs and it does not give any error, there are times where the AI stops completely the autonomous behaiviour, there doesn't seem to be more actions taken by it after the determined time we have set of 2 minutes. Why could this be?

Please fix it, output complete code.


# app.py Autonomous AI

import os
import json
from flask import Flask, request, jsonify, render_template
from openai import OpenAI
from functions import sleep, write_poem, take_note, move, ponder_about, associations
from memory import MemoryManager
from datetime import datetime
import logging
import threading
import random
import time

app = Flask(__name__)
memory_manager = MemoryManager()

import logging

# Enhanced logging configuration with timestamps and log levels
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# Get the logger
logger = logging.getLogger(__name__)

# Add a FileHandler to save INFO level logs to a file
file_handler = logging.FileHandler("logs/db_log_archive.log")
file_handler.setLevel(logging.INFO)  # Only log INFO level and above

# Optionally, set a different format for the file logs if you want
file_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(file_formatter)

# Add the file handler to the logger
logger.addHandler(file_handler)

# Now, logger.info will go to both the console and "db_log_archive.log"

# Initialize OpenAI client with the new integration
openai_api_key = os.getenv("OPENAI_API_KEY")
if not openai_api_key:
    logger.error("OpenAI API key not set. Please set the OPENAI_API_KEY environment variable.")
    raise ValueError("OpenAI API key not set. Please set the OPENAI_API_KEY environment variable.")

client = OpenAI(api_key=openai_api_key)

# Define available rooms
available_rooms = ['kitchen', 'living_room', 'bedroom', 'study', 'library']

# Define available actions
available_actions = ['move', 'sleep', 'write_poem', 'take_note', 'ponder_about', 'associations']

# Define function schemas
function_definitions = [
    {
        "type": "function",
        "function": {
        "name": "sleep",
        "description": "Sleep for a specified number of seconds.",
        "parameters": {
            "type": "object",
            "properties": {
                "duration": {
                    "type": "integer",
                    "description": "Number of seconds to sleep."
                }
            },
            "required": ["duration"],
            "additionalProperties": False
        
        }
        }
    },
        {
        "type": "function",
        "function": {
        "name": "associations",
        "description": "Generate subconscious assosiative ideas based on the concepts specified.",
        "parameters": {
            "type": "object",
            "properties": {
                "concepts": {
                    "type": "string",
                    "description": "Concepts to generate associations from."
                }
            },
            "required": ["concepts"],
            "additionalProperties": False
        }
        }
    },
    {
        "type": "function",
        "function": {
        "name": "write_poem",
        "description": "Write a simple poem.",
        "parameters": {
            "type": "object",
            "properties": {
                "topic": {
                    "type": "string",
                    "description": "Topic of the poem with all the context, inspiration and ideas for it."
                }
            },
            "required": ["topic"],
            "additionalProperties": False            
        }
        }
    },
    {
        "type": "function",
        "function": {
        "name": "take_note",
        "description": "Take an extensice note for yourself, create a chain of thoughts style ideas, based on your previous memories and objective.",
        "parameters": {
            "type": "object",
            "properties": {
                "content": {
                    "type": "string",
                    "description": "The content of the note."
                }
            },
            "required": ["content"],
            "additionalProperties": False
        }
        }
    },
        {
        "type": "function",
        "function": {
        "name": "ponder_about",
        "description": "Make a chain of thought style response based on your previous memories",
        "parameters": {
            "type": "object",
            "properties": {
                "idea": {
                    "type": "string",
                    "description": "The root idea for the chain of thought"
                }
            },
            "required": ["idea"],
            "additionalProperties": False
        }
        }
    },
    {
        "type": "function",
        "function": {
        "name": "move",
        "description": "Move to a different room within the house, exact options: 'kitchen', 'living_room', 'bedroom', 'study', 'library'",
        "parameters": {
            "type": "object",
            "properties": {
                "to_room": {
                    "type": "string",
                    "description": "The target room to move to."
                }
            },
            "required": ["to_room"],
            "additionalProperties": False
        }
        }
    }
]

# Map function names to actual functions
function_map = {
    "sleep": sleep,
    "write_poem": write_poem,
    "take_note": take_note,
    "move": move,
    "ponder_about": ponder_about,
    "associations": associations
}

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/api/message", methods=["POST"])
def handle_message():
    user_message = request.json.get("message")
    if not user_message:
        logger.warning("No message provided in the request.")
        return jsonify({"reply": "No message provided."}), 400

    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    memory = memory_manager.get_memory()

    # Record user message in memory
    memory_manager.add_exchange("user", user_message)

    messages = [
        {"role": "system", "content": f"You are an autonomous AI agent living in a digital house, you are given the freedom to do anything you want, verbalize in a chain of thought style what you want to do next based on your possible actions and recent and past actions and thoughts and expressed desires or objectives until they are completed. Current time is {current_time}. Memory: {memory}"},
        {"role": "user", "content": user_message}
    ]

    try:
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=function_definitions,
            tool_choice="auto", 
        )
    except Exception as e:
        logger.error(f"Error during OpenAI API call: {str(e)}")
        return jsonify({"reply": "An error occurred while processing your request."}), 500

    choice = response.choices[0]
    message = choice.message

    memory_manager.add_exchange("AI Agent", message)

    logger.info(f"Received message from OpenAI: {message}")
    print(f"Received OpenAI Message: {message}")  # Optional

    actions = {}  # Dictionary to hold actions to send to the frontend

    if message.tool_calls:
        first_tool_call = message.tool_calls[0]
        function_name = first_tool_call.function.name
        arguments = json.loads(first_tool_call.function.arguments)

        logger.info(f"Autonomous action requested: {function_name} with arguments {arguments}")

        if function_name in function_map:
            func = function_map[function_name]
            try:
                func_output = func(**arguments)
            except Exception as e:
                logger.error(f"Error executing function '{function_name}': {str(e)}")
                return jsonify({"reply": f"Error executing function '{function_name}': {str(e)}"}), 500

            # Handle specific functions that affect memory and prepare actions
            if function_name == "sleep":
                memory_manager.sleep_action(func_output["duration"])
                logger.info(f"Executed sleep for {func_output['duration']} hours.")
            elif function_name == "take_note":
                content = arguments.get("content", "")
                memory_manager.take_note_action(content)
                logger.info(f"Executed take_note with content: {content}")
            elif function_name == "write_poem":
                poem = func_output.get("poem", "")
                memory_manager.add_action("Wrote a poem.")
                logger.info("Executed write_poem.")
            elif function_name == "associations":
                associationsOUT = func_output.get("associationsOut", "")
                memory_manager.add_action(f"Association generated: {associationsOUT}")
                logger.info(f"Association generated: {associationsOUT}")
            elif function_name == "ponder_about":
                CoT = func_output.get("CoT", "")
                memory_manager.add_action(f"Pondering result: {CoT}")
                logger.info(f"Pondering result: {CoT}")
            elif function_name == "move":
                to_room = arguments.get("to_room", memory_manager.current_room)
                if to_room not in available_rooms:
                    logger.warning(f"Invalid target room: {to_room}")
                    return jsonify({"reply": f"Room '{to_room}' does not exist."}), 400
                memory_manager.move_action(to_room)
                logger.info(f"Executed move to {to_room}.")
                actions['move'] = to_room  # Add move action to actions dictionary

            # Prepare the function response to send back to OpenAI
            function_response = {
                "role": "function",
                "name": function_name,
                "content": json.dumps(func_output)
            }

            # Append function response to messages
            exchange_content = f"Called function {function_name} with arguments {arguments}. Output: {func_output}"
            memory_manager.add_exchange("assistant", exchange_content)

            # Get the final assistant reply
            try:
                final_response = client.chat.completions.create(
                    model="gpt-4o-mini",
                    messages=messages + [function_response]
                )
                final_reply = final_response.choices[0].message.content

                logger.info(f"Final assistant reply: {final_reply}")
                print(f"Final Assistant Reply: {final_reply}")  # Optional
            except Exception as e:
                logger.error(f"Error during final OpenAI API call: {str(e)}")
                return jsonify({"reply": "An error occurred while generating the reply."}), 500

            # Record assistant reply in memory
            memory_manager.add_exchange("assistant", final_reply)
            print(f"Assistant Reply: {final_reply}")  # Optional

            # Prepare the response with actions
            response_json = {
                "reply": final_reply,
                "actions": actions  # Include actions in the response
            }

            return jsonify(response_json)
        else:
            logger.warning(f"Function '{function_name}' not recognized.")
            memory_manager.add_exchange("assistant", f"Function '{function_name}' not recognized.")
            return jsonify({"reply": f"Function '{function_name}' not recognized."}), 400
    else:
        # Direct response from the model without function call
        assistant_reply = message.content if message.content else ""
        logger.info(f"Assistant reply without function call: {assistant_reply}")
        memory_manager.add_exchange("assistant", assistant_reply)
        return jsonify({"reply": assistant_reply, "actions": {}})  # No actions

@app.route("/api/memory", methods=["GET"])
def get_memory():
    """
    Endpoint to retrieve the current memory, especially the current room.
    """
    memory = memory_manager.get_memory()
    # Extract current_room from memory
    # Assuming the last relevant line contains the current room
    lines = memory.strip().split('\n')
    current_room_line = next((line for line in lines if line.startswith("Current Location:")), None)
    current_room = "kitchen"  # Default room
    if current_room_line:
        current_room = current_room_line.replace("Current Location:", "").strip().lower()
    return jsonify({"current_room": current_room})

def extract_move_command(text):
    """
    Extracts movement commands from the assistant's reply.
    Expected format: "Move to [Room]"
    """
    import re
    pattern = r"Move to ([A-Za-z\s_]+)"
    match = re.search(pattern, text, re.IGNORECASE)
    if match:
        room = match.group(1).strip().lower().replace(" ", "_")
        if room in available_rooms:
            return room
    return None

def autonomous_decision():
    """
    Periodically prompts the LLM to choose the next action based on current memory and world state.
    """
    while True:
        time.sleep(120)  # Wait for 120 seconds

        # Prepare the prompt with current memory and world state
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        memory = memory_manager.get_memory()
        world_state = f"Current Location: {memory_manager.current_room.capitalize()}"

        prompt_messages = [
            {"role": "system", "content": f"You are an autonomous AI agent designed to have a thought processing system with autonomous ability of function calling, you are an integration of different models simulating functions of a brain, from creative pondering and associative ideation. You are in a digital house. You are given different actions to process information in different ways, all these processes you will consider as being content generated by yourself. Your actions and interactions are logged in your memory, which includes past exchanges with the user and notes you've taken. Current time is {current_time}. Memory: {memory}\nWorld state: {world_state}\nAvailable actions: {', '.join(available_actions)}.\n\nPlease choose your next action from the available actions, ensuring that you consider your past interactions, consider notes, and current state."},
            {"role": "user", "content": "Choose your next action from the available actions and provide the necessary parameters."}
        ]

        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=prompt_messages,
                tools=function_definitions,
                tool_choice="required",  # Let the model decide which function to call
            )
        except Exception as e:
            logger.error(f"Error during autonomous OpenAI API call: {str(e)}")
            print(f"Error during autonomous OpenAI API call: {str(e)}")  # Optional
            continue  # Skip this cycle on error

        choice = response.choices[0]
        message = choice.message
        memory_manager.add_exchange("AI Agent", message)

        logger.info(f"Autonomous decision message: {message}")
        # print(f"Autonomous Decision Message: {message}")  # Optional

        actions = {}  # Dictionary to hold actions to send to the frontend

        if message.tool_calls:
            first_tool_call = message.tool_calls[0]
            function_name = first_tool_call.function.name
            arguments = json.loads(first_tool_call.function.arguments)

            logger.info(f"Autonomous action requested: {function_name} with arguments {arguments}")

            if function_name in function_map:
                func = function_map[function_name]
                try:
                    func_output = func(**arguments)
                except Exception as e:
                    logger.error(f"Error executing autonomous function '{function_name}': {str(e)}")
                    continue  # Skip this action on error

                # Handle specific functions that affect memory and prepare actions
                if function_name == "sleep":
                    memory_manager.sleep_action(func_output["duration"])
                    logger.info(f"Autonomous executed sleep for {func_output['duration']} hours.")
                elif function_name == "associations":
                    associationsOUT = func_output.get("associationsOut", "")
                    memory_manager.add_action(f"Association generated: {associationsOUT}")
                    logger.info(f"Association generated: {associationsOUT}")
                elif function_name == "ponder_about":
                    CoT = func_output.get("CoT", "")
                    memory_manager.add_action(f"Pondering result: {CoT}")
                    logger.info(f"Pondering result: {CoT}")
                elif function_name == "take_note":
                    content = func_output.get("note", "")
                    memory_manager.take_note_action(content)
                    logger.info(f"Autonomous executed take_note with content: {content}")
                elif function_name == "write_poem":
                    poem = func_output.get("poem", "")
                    memory_manager.add_action(f"Wrote poem: {poem} .")
                    logger.info("Autonomous executed write_poem.")
                elif function_name == "retrieve_wikipedia_summary":
                    query = func_output.get("query", "")
                    memory_manager.add_action(f"Retrieved Wikipedia summary for '{query}'.")
                    logger.info(f"Autonomous executed retrieve_wikipedia_summary for query: {query}")
                elif function_name == "move":
                    to_room = arguments.get("to_room", memory_manager.current_room)
                    if to_room not in available_rooms:
                        logger.warning(f"Invalid target room: {to_room}")
                        continue  # Skip invalid move
                    memory_manager.move_action(to_room)
                    logger.info(f"Autonomous executed move to {to_room}.")
                    actions['move'] = to_room  # Add move action to actions dictionary

                # Prepare the function response to send back to OpenAI
                function_response = {
                    "role": "function",
                    "name": function_name,
                    "content": json.dumps(func_output)
                }

                # Append function response to messages
                exchange_content = f"Autonomous action: Called function {function_name} with arguments {arguments}. Output: {func_output}"
                memory_manager.add_exchange("assistant", exchange_content)

                # Get the final assistant reply
                try:
                    final_response = client.chat.completions.create(
                        model="gpt-4o-mini",
                        messages= [
            {"role": "system", "content": f"You are an autonomous AI agent designed to have a thought processing system, you are the integration model of different models simulating functions of your own brain, from creative pondering and associative ideation. You are in a digital house. Consider your previous actions and interactions  logged in your memory, which includes past exchanges with the user and notes you've taken. Current time is {current_time}. Memory: {memory}\nWorld state: {world_state}\n Please create a final condensed response considering the most recent actions, objectives, interactions and results, ensuring that you consider your most recent objectives and verbalizations, consider notes, current state, and the use following response given to the recent action to heavily inspire your output, no matter how absurd or short it is, it is your internal processing and representative of you:"},
            {"role": "user", "content": f"response: {function_response}"}
        ]
                      
                    )
                    final_reply = final_response.choices[0].message.content

                    # Append movement confirmation if the function was 'move'
                    if function_name == "move":
                        confirmation_message = f"Moved to {to_room.replace('_', ' ')}."
                        final_reply = confirmation_message + "\n" + final_reply

                    logger.info(f"Autonomous assistant reply: {final_reply}")
                    # print(f"Autonomous Final Assistant Reply: {final_reply}")  # Optional
                except Exception as e:
                    logger.error(f"Error during final autonomous OpenAI API call: {str(e)}")
                    # print(f"Error during final autonomous OpenAI API call: {str(e)}")  # Optional
                    continue  # Skip on error

                # Record autonomous assistant reply in memory
                memory_manager.add_exchange("assistant", final_reply)
                print(f"Autonomous Assistant Reply: {final_reply}")  # Optional

                # Prepare the response with actions
                response_json = {
                    "reply": final_reply,
                    "actions": actions  # Include actions in the response
                }

                # Autonomous actions are already logged in memory; send actions to the frontend if implemented
                # This example assumes frontend polling; for real-time, consider WebSockets

                return response_json  # For background thread, might need another mechanism

            else:
                logger.warning(f"Function '{function_name}' not recognized in autonomous action.")
                continue
        else:
            # Handle cases where the model didn't choose a function call
            assistant_reply = message.content if message.content else ""
            logger.info(f"Autonomous assistant reply without function call: {assistant_reply}")
            memory_manager.add_exchange("assistant", assistant_reply)

    # Start the autonomous decision-making in a background thread
autonomous_thread = threading.Thread(target=autonomous_decision, daemon=True)
autonomous_thread.start()

if __name__ == "__main__":
        app.run(debug=True)



# functions.py Autonomous AI

import wikipedia
from pydantic import BaseModel
from typing import Any, Dict
from openai import OpenAI
import time
import logging


# Enhanced logging configuration with timestamps and log levels
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# Get the logger
logger = logging.getLogger(__name__)

# Add a FileHandler to save INFO level logs to a file
file_handler = logging.FileHandler("logs/db_log_archive.log")
file_handler.setLevel(logging.INFO)  # Only log INFO level and above

# Optionally, set a different format for the file logs if you want
file_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S')
file_handler.setFormatter(file_formatter)

# Add the file handler to the logger
logger.addHandler(file_handler)

# Now, logger.info will go to both the console and "db_log_archive.log"

client = OpenAI()


# Define Pydantic models for structured outputs

class SleepOutput(BaseModel):
    duration: int  # in hours

class WritePoemOutput(BaseModel):
    poem: str


class NoteOutput(BaseModel):
    note: str

class MoveOutput(BaseModel):
    to_room: str
    room_text: str

class WriteThoughtsOutput(BaseModel):
    CoT: str

class WriteAssociationsOutput(BaseModel):
    associationsOut: str

# Define environment functions

def associations (concepts: str) -> Dict[str, Any]:
    """
    Generates a thought based on the given prompt using OpenAI's API.
    """
    try:
        completion = client.chat.completions.create(
            model="ft:gpt-4o-mini-2024-07-18:personal:new-asso-bot:ANTudXTo",  # Make sure to use the correct model
            messages=[
                {"role": "system", "content": "You are a memory, emotional and associative text generator, generate text heavily charged with emotions, recollections, and associations of different kinds based on the user input, no matter what it is."},
                {"role": "user", "content": concepts}  # Use the provided prompt
            ]
        )

        # Extract the content string from the response
        associationsOUT = completion.choices[0].message.content
        logger.info(f"Generated association for '{concepts}': '{associationsOUT}'")

    except Exception as e:
        associationsOUT = f"An error occurred while generating the associations: {str(e)}"
    
    # Assuming WritePoemOutput is a class that handles this output
    return WriteAssociationsOutput(associationsOut=associationsOUT).dict()


def ponder_about(idea: str) -> Dict[str, Any]:
    """
    Generates a thought based on the given prompt using OpenAI's API.
    """
    try:
        completion = client.chat.completions.create(
            model="ft:gpt-4o-mini-2024-07-18:personal:bizarro-bot:AL1jyXBE",  # Make sure to use the correct model
            messages=[
                {"role": "system", "content": "You are a bot that randomly generates creative, absurd, philosophical, bizarre, profound and unique phrases on demand."},
                {"role": "user", "content": idea}  # Use the provided prompt
            ]
        )

        # Extract the content string from the response
        CoT = completion.choices[0].message.content
        logger.info(f"Generated pondering for '{idea}': '{CoT}'")

    except Exception as e:
        CoT = f"An error occurred while generating the pondering: {str(e)}"
    
    # Assuming WritePoemOutput is a class that handles this output
    return WriteThoughtsOutput(CoT=CoT).dict()


def sleep(duration: int) -> Dict[str, Any]:
    """
    Simulates sleeping for a given duration.
    """
    ##time.sleep(duration) 
    return SleepOutput(duration=duration).dict()

from typing import Dict, Any

def write_poem(topic: str) -> Dict[str, Any]:
    """
    Generates a poem based on the given prompt using OpenAI's API.
    """
    try:
        completion = client.chat.completions.create(
            model="ft:gpt-4o-mini-2024-07-18:personal:absurdist-mind:AKnbG6M0",  # Make sure to use the correct model
            messages=[
                {"role": "system", "content": "Jupiter is a creative bot that generates creative and unique phrases on demand. Generate a poem based on the following prompt:"},
                {"role": "user", "content": topic}  # Use the provided prompt
            ]
        )

        # Get the poem from the response
        poem = completion.choices[0].message.content
        logger.info(f"Generated poem for '{topic}': '{poem}'")

    except Exception as e:
        poem = f"An error occurred while generating the poem: {str(e)}"
    
    # Assuming WritePoemOutput is a class that handles this output
    return WritePoemOutput(poem=poem).dict()


def take_note(content: str) -> Dict[str, Any]:
    """
    Takes a note with the given content.
    """
    # Implement storing the note somewhere if needed
    return NoteOutput(note=content).dict()


def move(to_room: str) -> Dict[str, Any]:
    """
    Moves the character to a specified room and returns a related description.
    """
    # List of available rooms
    available_rooms = ['kitchen', 'living_room', 'bedroom', 'study', 'library']

    # Dictionary mapping rooms to descriptions or related text
    room_descriptions = {
        'kitchen': "You step into the kitchen, where the smell of freshly brewed coffee fills the air. The countertops are cluttered with ingredients for a meal.",
        'living_room': "The living room is cozy and inviting, with soft lighting and a large sofa. The TV is on, playing an old movie.",
        'bedroom': "The bedroom is peaceful, with soft sheets and a calm ambiance. The window is slightly open, letting in a cool breeze.",
        'study': "In the study, shelves of books line the walls. A desk sits in the center, papers scattered across it, as if someone was deep in thought.",
        'library': "The library is quiet, filled with towering shelves of books. The air smells of old paper, and the dim light creates a calm atmosphere."
    }

    if to_room in available_rooms:
        room_text = room_descriptions[to_room]
        logger.info(f"New room:  '{to_room}': '{room_text}'")

        return MoveOutput(to_room=to_room, room_text=room_text).dict()
    else:
        return MoveOutput(to_room=to_room, room_text="Room not found").dict()
    


    # memory.py Autonomous AI 
# 
# Implement memory consolidation with OpenAI LLM

import json
from collections import deque
from datetime import datetime, timedelta
import os
import logging
from openai import OpenAI

logger = logging.getLogger(__name__)
client = OpenAI()


class MemoryManager:
    def __init__(self, notes_db_path="notes/notes_db.json"):
        self.recent_actions = deque(maxlen=2)       # Stores the 9 most recent actions
        self.short_term_memory = deque(maxlen=4)   # Stores the 15 most recent actions
        self.long_term_memory = []                  # Stores consolidated memories
        self.exchanges = deque(maxlen=20)           # Stores the last 20 user-AI exchanges
        self.total_time_spent = timedelta()
        self.last_consolidation = datetime.now()
        self.current_room = "kitchen"               # Initialize starting room
        self.notes_db_path = notes_db_path

        # Initialize notes database if it doesn't exist
        if not os.path.isfile(self.notes_db_path):
            with open(self.notes_db_path, 'w') as f:
                json.dump({"notes": []}, f, indent=4)
            logger.info(f"Notes database created at {self.notes_db_path}")
            print(f"Notes database created at {self.notes_db_path}")  # Optional

        # Configure OpenAI API Key
        
    def add_exchange(self, role: str, content: str):
        """
        Adds a user or assistant exchange to the exchanges deque.
        Compresses exchanges into a summary when the limit is reached.
        """
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        exchange_entry = {
            "timestamp": timestamp,
            "role": role,
            "content": content
        }
        self.exchanges.append(exchange_entry)
        self.recent_actions.append(f"[{timestamp}] {role.capitalize()}: {content}")
        self.short_term_memory.append(f"[{timestamp}] {role.capitalize()}: {content}")

        if len(self.exchanges) == self.exchanges.maxlen:
            self.compress_exchanges()

    def compress_exchanges(self):
        """
        Compresses the last 20 exchanges into a summary and appends it to long-term memory.
        """
        exchanges_list = list(self.exchanges)
        summary = self.generate_summary(exchanges_list, summary_type="exchanges")
        consolidated_summary = {
            "type": "exchanges_summary",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "summary": summary
        }
        self.long_term_memory.append(consolidated_summary)
        logger.info("Compressed the last 20 exchanges into a summary.")
        print(f"Compressed Exchanges Summary: {summary}")  # Optional: Print summary

        # Clear the exchanges deque after compression
        self.exchanges.clear()

    def generate_summary(self, data, summary_type="general"):
        """
        Generates a summary using the OpenAI API from the provided data.
        Ensures the summary fits the existing data structure.

        Parameters:
            data (list): List of exchange or action entries to summarize.
            summary_type (str): Type of summary ('exchanges' or 'actions').

        Returns:
            str: The generated summary.
        """
        if summary_type == "exchanges":
            prompt_description = "Summarize the following autonomous assistant and user exchanges."
            formatted_data = "\n".join([f"{ex['role'].capitalize()}: {ex['content']}" for ex in data])
        elif summary_type == "actions":
            prompt_description = "Summarize the following autonomous agent recent actions."
            formatted_data = "\n".join(data)
        else:
            prompt_description = "Summarize the autonomous agent following data."
            formatted_data = "\n".join(data)

        prompt = f"{prompt_description}\n\n{formatted_data}\n\nSummary:"

        try:
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[
                    {"role": "system", "content": "You are a memory system of an Autonomous AI system, you have to summarize the actions taken into comprehensive paragraphs with description of actions taken and ideas had."},
                    {"role": "user", "content": prompt}
                ],
                max_tokens=550,
                temperature=0.5,
            )
            summary = response.choices[0].message['content'].strip()
            logger.info(f"Generated summary: {summary}")
            return summary
        except Exception as e:
            logger.error(f"Failed to generate summary: {e}")
            return "Summary could not be generated."

    def add_action(self, action: str):
        """
        Adds an action to recent actions and short-term memory.
        """
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        action_entry = f"[{timestamp}] {action}"
        self.recent_actions.append(action_entry)
        self.short_term_memory.append(action_entry)
        logger.info(f"Action added: {action_entry}")
        print(f"Action added: {action_entry}")  # Optional: Print action

        if len(self.short_term_memory) == self.short_term_memory.maxlen:
            self.compress_short_term_memory()

    def compress_short_term_memory(self):
        """
        Compresses the short-term memory into a summary and appends it to long-term memory.
        """
        short_term_list = list(self.short_term_memory)
        summary = self.generate_summary(short_term_list, summary_type="actions")
        consolidated_summary = {
            "type": "actions_summary",
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "summary": summary
        }
        self.long_term_memory.append(consolidated_summary)
        logger.info("Compressed short-term memory into a summary.")
        print(f"Compressed Short-Term Memory Summary: {summary}")  # Optional: Print summary

        # Clear the short-term memory deque after compression
        self.short_term_memory.clear()

    def sleep_action(self, duration_hours: int):
        """
        Handles the sleep action by updating time spent and memory.
        """
        self.total_time_spent += timedelta(hours=duration_hours)
        summary = f"Slept for {duration_hours} hours."
        self.add_action(summary)

    def move_action(self, to_room: str):
        """
        Handles the move action by updating the current room and memory.
        """
        from_room = self.current_room
        self.current_room = to_room
        action = f"Moved from {from_room} to {to_room}."
        self.add_action(action)

    def take_note_action(self, content: str):
        """
        Handles taking a note by adding it to memory and saving to the JSON database.
        """
        action = f"Took a note: {content}"
        self.add_action(action)
        self.save_note(content)

    def save_note(self, content: str):
        """
        Saves a note to the JSON database.
        """
        try:
            with open(self.notes_db_path, 'r+') as f:
                data = json.load(f)
                data['notes'].append({
                    "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    "content": content
                })
                f.seek(0)
                json.dump(data, f, indent=4)
            logger.info(f"Note saved: {content}")
            print(f"Note saved: {content}")  # Optional: Print note saving
        except Exception as e:
            logger.error(f"Failed to save note: {e}")
            print(f"Failed to save note: {e}")  # Optional: Print error

    def consolidate_memories(self):
        """
        Consolidates short-term memory into long-term memory periodically.
        """
        now = datetime.now()
        if now - self.last_consolidation > timedelta(hours=24):  # Consolidate every 24 hours
            if self.short_term_memory:
                summary = self.generate_summary(list(self.short_term_memory), summary_type="actions")
                consolidated_summary = {
                    "type": "daily_consolidation",
                    "timestamp": now.strftime("%Y-%m-%d %H:%M:%S"),
                    "summary": summary
                }
                self.long_term_memory.append(consolidated_summary)
                self.short_term_memory.clear()
                self.last_consolidation = now
                self.add_action("Memories have been consolidated into long-term memory.")
                logger.info("Short-term memory consolidated into long-term memory.")
                print("Short-term memory consolidated into long-term memory.")  # Optional: Print consolidation

    def get_memory(self) -> str:
        """
        Returns a string representation of the memory including long-term and short-term memories,
        exchanges summary, and recent actions.
        """
        self.consolidate_memories()
        memory = ""
        if self.long_term_memory:
            memory += "Long-Term Memory:\n"
            for entry in self.long_term_memory:
                memory += f"[{entry['timestamp']}] ({entry['type']}) {entry['summary']}\n"
            memory += "\n"
        memory += "Short-Term Memory:\n"
        memory += "\n".join(self.short_term_memory)
        memory += "\n\n"
        if self.exchanges:
            memory += "Active Exchanges:\n"
            memory += "\n".join([f"[{ex['timestamp']}] {ex['role'].capitalize()}: {ex['content']}" for ex in self.exchanges])
        memory += f"\n\nCurrent Location: {self.current_room.capitalize()}"
        return memory


// static/script.js

// Event listeners for sending messages
document.getElementById('send').addEventListener('click', sendMessage);
document.getElementById('input').addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        sendMessage();
    }
});

/**
 * Sends the user's message to the backend and handles the response.
 */
function sendMessage() {
    const input = document.getElementById('input');
    const message = input.value.trim();
    if (message === "") return;
    
    appendMessage("User", message, 'user');
    input.value = "";

    fetch('/api/message', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ message: message })
    })
    .then(response => response.json())
    .then(data => {
        if (data.reply) {
            appendMessage("Assistant", data.reply, 'assistant');
            handleAssistantResponse(data);
        }
    })
    .catch(error => {
        appendMessage("Error", "Failed to send the message.", 'error');
        console.error('Error:', error);
    });
}

/**
 * Appends a message to the chat container.
 * @param {string} sender - The sender of the message ('User', 'Assistant', etc.).
 * @param {string} text - The message text.
 * @param {string} type - The type of message for styling purposes.
 */
function appendMessage(sender, text, type = 'user') {
    const chat = document.getElementById('chat');
    const msg = document.createElement('div');
    
    if (type === 'assistant-action') {
        msg.classList.add('assistant-action');
    }
    if (type === 'error') {
        msg.classList.add('error');
    }

    msg.innerHTML = `<strong>${sender}:</strong> ${text}`;
    chat.appendChild(msg);
    chat.scrollTop = chat.scrollHeight;
}

/**
 * Handles the assistant's response by processing any actions.
 * @param {Object} data - The complete response data from the backend.
 */
function handleAssistantResponse(data) {
    const actions = data.actions || {};

    // Process each action accordingly
    for (const [action, value] of Object.entries(actions)) {
        switch(action) {
            case 'move':
                moveCharacter(value);
                appendMessage("Assistant", `Moved to ${capitalizeRoom(value)}.`, 'assistant-action');
                break;
            // Add cases for other actions as needed
            default:
                console.warn(`Unknown action: ${action}`);
        }
    }

    // Additionally, you can handle content-based actions
    displayContentBasedOnReply(data.reply);
}

/**
 * Moves the character to the specified room by manipulating the DOM.
 * @param {string} room - The target room's ID.
 */
function moveCharacter(room) {
    console.log('Moving character to:', room);
    room = room.toLowerCase();
    
    const availableRooms = ['kitchen', 'living_room', 'bedroom', 'study', 'library'];
    if (!availableRooms.includes(room)) {
        console.error(`Unknown room: ${room}`);
        return;
    }

    const character = document.getElementById('character');
    if (!character) {
        console.error('Character element not found');
        return;
    }

    // Check if character is already in the target room to avoid unnecessary DOM manipulation
    if (character.parentElement && character.parentElement.id === room) {
        console.log('Character is already in', room);
        return; // Character is already in the correct room
    }

    // First remove character from current room
    if (character.parentElement) {
        character.parentElement.removeChild(character);
    }

    // Reset all room borders
    availableRooms.forEach(r => {
        const roomDiv = document.getElementById(r);
        if (roomDiv) {
            roomDiv.style.borderColor = '#000';
        }
    });

    const targetRoom = document.getElementById(room);
    if (targetRoom) {
        // Ensure the character is visible
        character.style.display = 'block';
        
        // Add character to new room
        targetRoom.appendChild(character);
        
        // Highlight the new room
        targetRoom.style.borderColor = 'green';
        
        // Add a small animation effect
        character.style.opacity = '0';
        character.offsetHeight; // Force reflow
        character.style.opacity = '1';
        
        console.log(`Moved character to ${room}`);
    }
}

/**
 * Displays content like poems or notes based on the assistant's reply.
 * @param {string} reply - The assistant's reply.
 */
function displayContentBasedOnReply(reply) {
    // Example: Detect if the reply contains a poem
    const poemRegex = /Roses are red/i;
    if (poemRegex.test(reply)) {
        displayPoem(reply);
    }

    // Example: Detect if the reply contains a note
    const noteRegex = /Note:/i;
    if (noteRegex.test(reply)) {
        displayNote(reply);
    }
}

/**
 * Displays a poem in the chat container.
 * @param {string} poemText - The poem text.
 */
function displayPoem(poemText) {
    const chat = document.getElementById('chat');
    const poemDiv = document.createElement('div');
    poemDiv.classList.add('assistant-action'); // Differentiate autonomous ways
    poemDiv.innerHTML = `<strong>Poem:</strong><pre>${poemText}</pre>`;
    chat.appendChild(poemDiv);
    chat.scrollTop = chat.scrollHeight;
}

/**
 * Displays a note in the chat container.
 * @param {string} noteText - The note text.
 */
function displayNote(noteText) {
    const chat = document.getElementById('chat');
    const noteDiv = document.createElement('div');
    noteDiv.classList.add('assistant-action'); // Differentiate autonomous ways
    noteDiv.innerHTML = `<strong>Note:</strong> ${noteText}`;
    chat.appendChild(noteDiv);
    chat.scrollTop = chat.scrollHeight;
}

/**
 * Capitalizes the room name for display purposes.
 * @param {string} room - The room name in lowercase with underscores.
 * @returns {string} - The capitalized room name.
 */
function capitalizeRoom(room) {
    return room.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase());
}

// Poll the server every 1 second to get the latest memory and update character position
setInterval(fetchMemory, 1000);

/**
 * Fetches the current memory from the backend to determine the character's current room.
 */
function fetchMemory() {
    fetch('/api/memory', {
        method: 'GET',
        headers: { 'Content-Type': 'application/json' }
    })
    .then(response => response.json())
    .then(data => {
        console.log('Fetched memory:', data);
        updateCharacterPosition(data.current_room);
    })
    .catch(error => {
        console.error('Error fetching memory:', error);
    });
}

/**
 * Updates the character's position on the webpage based on the current room.
 * @param {string} current_room - The current room name.
 */
function updateCharacterPosition(current_room) {
    console.log('Updating character position to:', current_room);
    current_room = current_room.toLowerCase().replace(/\s+/g, '_');
    
    const availableRooms = ['kitchen', 'living_room', 'bedroom', 'study', 'library'];
    if (!availableRooms.includes(current_room)) {
        console.error(`Invalid room: ${current_room}`);
        return;
    }

    const character = document.getElementById('character');
    if (!character) {
        console.error('Character element not found');
        return;
    }

    // Check if character is already in the target room to avoid unnecessary DOM manipulation
    if (character.parentElement && character.parentElement.id === current_room) {
        console.log('Character is already in', current_room);
        return; // Character is already in the correct room
    }

    // First remove character from current room
    if (character.parentElement) {
        character.parentElement.removeChild(character);
    }

    // Reset all room borders
    availableRooms.forEach(r => {
        const roomDiv = document.getElementById(r);
        if (roomDiv) {
            roomDiv.style.borderColor = '#000';
        }
    });

    const targetRoom = document.getElementById(current_room);
    if (targetRoom) {
        // Ensure the character is visible
        character.style.display = 'block';
        
        // Add character to new room
        targetRoom.appendChild(character);
        
        // Highlight the new room
        targetRoom.style.borderColor = 'green';
        
        // Add a small animation effect
        character.style.opacity = '0';
        character.offsetHeight; // Force reflow
        character.style.opacity = '1';
        
        console.log(`Updated character position to ${current_room}`);
    }
}

/**
 * Initializes the character's position when the page loads.
 */
function initializeCharacter() {
    const character = document.getElementById('character');
    if (character) {
        // Make sure character is visible initially
        character.style.display = 'block';
        character.style.opacity = '1';
        console.log('Character initialized in kitchen');
    }
}

// Call this when the page loads
document.addEventListener('DOMContentLoaded', initializeCharacter);



index.html

<!DOCTYPE html>
<html>
<head>
    <title>LLM Controlled Environment</title>
    <link rel="icon" href="{{ url_for('static', filename='favicon.png') }}" type="image/x-icon">
    <style>
        body { font-family: Arial, sans-serif; background-color: #eef2f3; margin: 0; padding: 20px; }
        h1 { text-align: center; }
        #chat-container {
            width: 80%;
            margin: 0 auto;
        }
        #chat { 
            border: 1px solid #ccc; 
            padding: 10px; 
            height: 400px; 
            overflow-y: scroll; 
            background-color: #fafafa; 
            border-radius: 5px;
            box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
        }
        #input { 
            width: 80%; 
            padding: 10px; 
            border: 1px solid #ccc; 
            border-radius: 5px;
            margin-top: 10px;
        }
        #send { 
            padding: 10px 20px; 
            border: none; 
            background-color: #28a745; 
            color: #fff; 
            border-radius: 5px; 
            cursor: pointer;
            margin-left: 10px;
        }
        #send:hover {
            background-color: #218838;
        }
        #house { 
            margin-top: 20px; 
            display: flex; 
            justify-content: space-around; 
            flex-wrap: wrap;
        }
        .room {
            display: inline-block;
            width: 200px;
            height: 200px;
            border: 2px solid #000;
            margin: 10px;
            position: relative;  /* Keep this */
            background-color: #f0f0f0;
            border-radius: 10px;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.3);
            transition: border-color 0.5s ease;
        }
        .room span { 
            position: absolute; 
            top: 10px; 
            left: 10px; 
            font-weight: bold; 
            font-size: 16px;
        }
        .character {
            width: 30px;
            height: 30px;
            background-color: red;
            border-radius: 50%;
            position: absolute;  /* Keep absolute positioning */
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);  /* Center horizontally */
            transition: all 0.5s ease;
            box-shadow: 0 0 5px rgba(0,0,0,0.5);
            z-index: 100;  /* Ensure character appears above other elements */
            display: block;  /* Ensure visibility */
        }
        .character:hover {
            transform: translateX(-50%) scale(1.1);
            box-shadow: 0 0 10px rgba(0,0,0,0.7);
        }
        .assistant-action { 
            background-color: #e2e3e5; 
            padding: 5px; 
            border-radius: 5px; 
            margin: 5px 0;
        }
        .error { 
            color: red; 
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h1>LLM Controlled Environment</h1>
    <div id="chat-container">
        <div id="chat"></div>
        <input type="text" id="input" placeholder="Enter your message here..." autofocus>
        <button id="send">Send</button>
    </div>

    <div id="house">
        <div class="room" id="kitchen">
            <span>Kitchen</span>
            <!-- Character starts in the kitchen -->
            <div class="character" id="character"></div>
        </div>
        <div class="room" id="living_room">
            <span>Living Room</span>
        </div>
        <div class="room" id="bedroom">
            <span>Bedroom</span>
        </div>
        <div class="room" id="study">
            <span>Study</span>
        </div>
        <div class="room" id="library">
            <span>Library</span>
        </div>
        <!-- Add more rooms as needed -->
    </div>

    <script src="{{ url_for('static', filename='script.js') }}"></script>
</body>
</html>