Please check this code base we have, it mostly works, but we want to focus on the front-end webpage, the issue we are having is that, while the AI correctly calls the function to move, and it correctly gives it the information of a success, the webpage does not reflect this, and the character we have does not change its position visually. Please how can we fix it? give me full code ready to copy and paste if possible. Here is the whole code-base:

# 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)
    #print(f"User Message: {user_message}")  # Optional: Print 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"  # Let the model decide when to call functions
            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)

    # Debugging: Log the message object
    logger.info(f"Received message from OpenAI: {message}")
    print(f"Received OpenAI Message: {message}")  # Optional: Print message object

    if message.tool_calls:
        first_tool_call = message.tool_calls[0]  # Access the first tool call in the list
        function_name = first_tool_call.function.name  # Access the name of the function in the first tool call
        arguments = json.loads(first_tool_call.function.arguments)

        logger.info(f"Autonomous action requested: {function_name} with arguments {arguments}")
        #print(f"Function Call Requested: {function_name} with arguments {arguments}")  # Optional: Print function call details

        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)}")
               # print(f"Error Executing Function '{function_name}': {str(e)}")  # Optional: Print error
                return jsonify({"reply": f"Error executing function '{function_name}': {str(e)}"}), 500

            # Handle specific functions that affect memory
            if function_name == "sleep":
                memory_manager.sleep_action(func_output["duration"])
                logger.info(f"Executed sleep for {func_output['duration']} hours.")
              #  print(f"Executed Sleep for {func_output['duration']} hours.")  # Optional: Print action
            elif function_name == "take_note":
                content = arguments.get("content", "")
                memory_manager.take_note_action(content)
                logger.info(f"Executed take_note with content: {content}")
              #  print(f"Executed Take Note: {content}")  # Optional: Print action
            elif function_name == "write_poem":
                poem = func_output.get("poem", "")
                memory_manager.add_action("Wrote a poem.")
                logger.info("Executed write_poem.")
              #  print("Executed Write Poem.")  # Optional: Print action
            elif function_name == "retrieve_wikipedia_summary":
                query = arguments.get("query", "")
                memory_manager.add_action(f"Retrieved Wikipedia summary for '{query}'.")
                logger.info(f"Executed retrieve_wikipedia_summary for query: {query}")
               # print(f"Executed Retrieve Wikipedia Summary for query: {query}")  # Optional: Print action
            elif function_name == "move":
                to_room = arguments.get("to_room", memory_manager.current_room)  # Default to current room if not specified
                if to_room not in available_rooms:
                    logger.warning(f"Invalid target room: {to_room}")
                   # print(f"Warning: Invalid target room: {to_room}")  # Optional: Print warning
                    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}.")
               # print(f"Executed Move to {to_room}.")  # Optional: Print action

            # 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)
           # print(f"Assistant Function Call: {exchange_content}")  # Optional: Print function call

            # 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: Print final reply
            except Exception as e:
                logger.error(f"Error during final OpenAI API call: {str(e)}")
              #  print(f"Error during final OpenAI API call: {str(e)}")  # Optional: Print error
                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: Print assistant reply

            return jsonify({"reply": final_reply})
        else:
            logger.warning(f"Function '{function_name}' not recognized.")
            memory_manager.add_exchange("assistant", f"Function '{function_name}' not recognized.")
           # print(f"Warning: Function '{function_name}' not recognized.")  # Optional: Print warning
            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}")
       # print(f"Assistant Direct Reply: {assistant_reply}")  # Optional: Print direct reply

        # Record assistant reply in memory
        memory_manager.add_exchange("assistant", assistant_reply)

        return jsonify({"reply": assistant_reply})

@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 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: Print error
            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: Print decision message

        if message.tool_calls:
            first_tool_call = message.tool_calls[0]  # Access the first tool call in the list
            function_name = first_tool_call.function.name  # Access the name of the function in the first tool call
            arguments = json.loads(first_tool_call.function.arguments)

            logger.info(f"Autonomous action requested: {function_name} with arguments {arguments}")
            #print(f"Autonomous Function Call Requested: {function_name} with arguments {arguments}")  # Optional: Print function call details

            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)}")
                    #print(f"Error Executing Autonomous Function '{function_name}': {str(e)}")  # Optional: Print error
                    continue  # Skip this action on error

                # Handle specific functions that affect memory
                if function_name == "sleep":
                    memory_manager.sleep_action(func_output["duration"])
                    logger.info(f"Autonomous executed sleep for {func_output['duration']} hours.")
                    #print(f"Autonomous Executed Sleep for {func_output['duration']} hours.")  # Optional: Print action
                elif function_name == "associations":
                    associationsOUT = arguments.get("associationsOUT", "")
                    memory_manager.add_action(f"Association generated: {associationsOUT}")
                    logger.info(f"Association generated: {associationsOUT}")
                elif function_name == "ponder_about":
                     CoT= arguments.get("CoT", "")
                     memory_manager.add_action(f"Pondering result: {CoT} ")
                     logger.info(f"Pondering result: {CoT}")
                elif function_name == "take_note":
                    content = arguments.get("content", "")
                    memory_manager.take_note_action(content)
                    logger.info(f"Autonomous executed take_note with content: {content}")
                   # print(f"Autonomous Executed Take Note: {content}")  # Optional: Print action
                elif function_name == "write_poem":
                    poem =arguments.get("poem", "")
                    memory_manager.add_action(f"Wrote poem: {poem} .")
                    logger.info("Autonomous executed write_poem.")
                    #print("Autonomous Executed Write Poem.")  # Optional: Print action
                elif function_name == "retrieve_wikipedia_summary":
                    query = arguments.get("query", "")
                    memory_manager.add_action(f"Retrieved Wikipedia summary for '{query}'.")
                    logger.info(f"Autonomous executed retrieve_wikipedia_summary for query: {query}")
                    #print(f"Autonomous Executed Retrieve Wikipedia Summary for query: {query}")  # Optional: Print action
                elif function_name == "move":
                    to_room = arguments.get("to_room", memory_manager.current_room)  # Default to current room if not specified
                    if to_room not in available_rooms:
                        logger.warning(f"Invalid target room: {to_room}")
                        #print(f"Autonomous Warning: Invalid target room: {to_room}")  # Optional: Print warning
                        continue  # Skip invalid move
                    memory_manager.move_action(to_room)
                    logger.info(f"Autonomous executed move to {to_room}.")
                    #print(f"Autonomous Executed Move to {to_room}.")  # Optional: Print action

                # 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)
                #print(f"Autonomous Assistant Function Call: {exchange_content}")  # Optional: Print function call

                # 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

                    logger.info(f"Autonomous assistant reply: {final_reply}")
                    ##print(f"Autonomous Final Assistant Reply: {final_reply}")  # Optional: Print final reply
                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: Print error
                    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: Print assistant reply

                # Autonomous actions are already logged in memory; frontend will display them via polling
            else:
                logger.warning(f"Function '{function_name}' not recognized in autonomous action.")
                #print(f"Autonomous Warning: Function '{function_name}' not recognized.")  # Optional: Print warning
                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}")
            #print(f"Autonomous Assistant Direct Reply: {assistant_reply}")  # Optional: Print direct reply
            # Optionally, handle direct replies or alternative actions here
            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

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

logger = logging.getLogger(__name__)

class MemoryManager:
    def __init__(self, notes_db_path="notes/notes_db.json"):
        self.recent_actions = deque(maxlen=9)       # Stores the 50 most recent actions
        self.short_term_memory = deque(maxlen=15)   # Stores the 100 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

    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.
        """
        summary = self.generate_summary(list(self.exchanges))
        consolidated_summary = f"Compressed Exchanges Summary:\n{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, exchanges):
        """
        Generates a summary from the list of exchanges.
        For simplicity, concatenates the exchanges.
        """
        summary_lines = [f"[{ex['timestamp']}] {ex['role'].capitalize()}: {ex['content']}" for ex in exchanges]
        summary = " | ".join(summary_lines)
        return summary

    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

    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:
                consolidated_summary = "Consolidated Summary:\n"
                consolidated_summary += "\n".join(list(self.short_term_memory))
                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"
            memory += "\n".join(self.long_term_memory)
            memory += "\n\n"
        memory += "Short-Term Memory:\n"
        memory += "\n".join(self.short_term_memory)
        memory += "\n\n"
        if self.exchanges:
            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()}"
        memory += "Recent Exchanges:\n"
        return memory
    


    // static/script.js Autonomous AI

document.getElementById('send').addEventListener('click', sendMessage);
document.getElementById('input').addEventListener('keypress', function(e) {
    if (e.key === 'Enter') {
        sendMessage();
    }
});

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');
            handleAssistantReply(data.reply, 'assistant');
        }
    })
    .catch(error => {
        appendMessage("Error", "Failed to send the message.", 'error');
        console.error('Error:', error);
    });
}

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;
}

function handleAssistantReply(reply, senderType = 'assistant') {
    // Check for movement commands
    const moveTo = extractMoveCommand(reply);
    if (moveTo) {
        moveCharacter(moveTo);
        appendMessage("Assistant", `Moved to ${capitalizeRoom(moveTo)}.`, 'assistant-action');
        return;
    }

    // Handle other types of actions based on assistant's reply
    // For example, display poems or notes
    displayContentBasedOnReply(reply);
}

function extractMoveCommand(text) {
    const regex = /Move to ([A-Za-z\s_]+)/i;
    const match = text.match(regex);
    if (match && match[1]) {
        return match[1].trim().toLowerCase().replace(" ", "_");
    }
    return null;
}


function moveCharacter(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;
    }

    // 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}`);
    }
}

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);
    }
}

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;
}

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;
}

function capitalizeRoom(room) {
    return room.replace('_', ' ').replace(/\b\w/g, char => char.toUpperCase());
}

// Poll the server every 5 seconds to get the latest memory and update character position
setInterval(fetchMemory, 5000);

function fetchMemory() {
    fetch('/api/memory', {
        method: 'GET',
        headers: { 'Content-Type': 'application/json' }
    })
    .then(response => response.json())
    .then(data => {
        updateCharacterPosition(data.current_room);
    })
    .catch(error => {
        console.error('Error fetching memory:', error);
    });
}

function updateCharacterPosition(current_room) {
    current_room = current_room.toLowerCase().replace(' ', '_');
    
    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;
    }

    // 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}`);
    }
}

// Add this function to initialize the character position
function initializeCharacter() {
    const character = document.getElementById('character');
    if (character) {
        // Make sure character is visible initially
        character.style.display = 'block';
        character.style.opacity = '1';
    }
}

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


<!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>