From e7aaf3419bb955406a2f59b5132cb6b51d7eec44 Mon Sep 17 00:00:00 2001 From: dougollerenshaw Date: Tue, 1 Oct 2024 10:20:09 -0700 Subject: [PATCH 1/2] Added session_id, saving code in dedicated folders --- .gitignore | 4 +-- codeaide/logic/chat_handler.py | 12 +++++--- codeaide/ui/chat_window.py | 7 ----- codeaide/utils/constants.py | 4 +-- codeaide/utils/file_handler.py | 49 ++++++++++++++++++++++---------- codeaide/utils/general_utils.py | 9 ++++++ tests/ui/test_chat_window.py | 2 -- tests/utils/test_file_handler.py | 25 +++++++--------- 8 files changed, 64 insertions(+), 48 deletions(-) diff --git a/.gitignore b/.gitignore index 2001404..6fc6be0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -# Exclude generated code folder -generated_code/ +# Exclude session_data folder +session_data/ # Exclude videos folder videos/ diff --git a/codeaide/logic/chat_handler.py b/codeaide/logic/chat_handler.py index aec97c4..e11a5c4 100644 --- a/codeaide/logic/chat_handler.py +++ b/codeaide/logic/chat_handler.py @@ -20,6 +20,7 @@ from codeaide.utils.environment_manager import EnvironmentManager from codeaide.utils.file_handler import FileHandler from codeaide.utils.terminal_manager import TerminalManager +from codeaide.utils.general_utils import generate_session_id class ChatHandler: @@ -33,10 +34,10 @@ def __init__(self): Returns: None """ + self.session_id = generate_session_id() self.cost_tracker = CostTracker() self.conversation_history = [] - self.file_handler = FileHandler() - self.file_handler.clear_output_dir() + self.file_handler = FileHandler(session_id=self.session_id) self.env_manager = EnvironmentManager() self.terminal_manager = TerminalManager() self.latest_version = "0.0" @@ -49,6 +50,7 @@ def __init__(self): ]["max_tokens"] self.api_key_valid, self.api_key_message = self.check_api_key() + print(f"New session started with ID: {self.session_id}") def check_api_key(self): """ @@ -436,9 +438,11 @@ def run_generated_code(self, filename, requirements): None """ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - script_path = os.path.join(project_root, self.file_handler.output_dir, filename) + script_path = os.path.join( + project_root, self.file_handler.session_dir, filename + ) req_path = os.path.join( - project_root, self.file_handler.output_dir, requirements + project_root, self.file_handler.session_dir, requirements ) activation_command = self.env_manager.get_activation_command() diff --git a/codeaide/ui/chat_window.py b/codeaide/ui/chat_window.py index 919ec04..8016b56 100644 --- a/codeaide/ui/chat_window.py +++ b/codeaide/ui/chat_window.py @@ -320,11 +320,6 @@ def update_chat_handler(self): ) return - self.chat_handler.clear_conversation_history() - self.chat_handler.set_latest_version( - current_version - ) # Maintain the version number - new_version = general_utils.increment_version( current_version, major_or_minor="major", increment=1 ) @@ -333,8 +328,6 @@ def update_chat_handler(self): switch_message = MODEL_SWITCH_MESSAGE.format( provider=provider, model=model, - current_version=current_version, - new_version=new_version, ) self.add_to_chat("System", switch_message) diff --git a/codeaide/utils/constants.py b/codeaide/utils/constants.py index 91d35c9..4da7887 100644 --- a/codeaide/utils/constants.py +++ b/codeaide/utils/constants.py @@ -53,9 +53,7 @@ MODEL_SWITCH_MESSAGE = """ ================================================== Switched to {provider} - {model} -Starting a new conversation with this model. -Current code version: {current_version} -Any new code will be versioned starting from {new_version} +Continuing the conversation with this model. ================================================== """ diff --git a/codeaide/utils/file_handler.py b/codeaide/utils/file_handler.py index 9f7768d..2cedc51 100644 --- a/codeaide/utils/file_handler.py +++ b/codeaide/utils/file_handler.py @@ -3,29 +3,34 @@ class FileHandler: - def __init__(self, base_dir=None): + def __init__(self, base_dir=None, session_id=None): if base_dir is None: self.base_dir = os.path.dirname( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) ) else: self.base_dir = base_dir - self.output_dir = os.path.join(self.base_dir, "generated_code") + self.output_dir = os.path.join(self.base_dir, "session_data") + self.session_id = session_id + self.session_dir = ( + os.path.join(self.output_dir, self.session_id) if self.session_id else None + ) self.versions_dict = {} - self._ensure_output_dir_exists() + self._ensure_output_dirs_exist() - def _ensure_output_dir_exists(self): + def _ensure_output_dirs_exist(self): os.makedirs(self.output_dir, exist_ok=True) - - def clear_output_dir(self): - print(f"Clearing output directory: {self.output_dir}") - if os.path.exists(self.output_dir): - shutil.rmtree(self.output_dir) - os.makedirs(self.output_dir) + if self.session_dir: + os.makedirs(self.session_dir, exist_ok=True) def save_code(self, code, version, version_description, requirements=[]): - code_path = os.path.join(self.output_dir, f"generated_script_{version}.py") - requirements_path = os.path.join(self.output_dir, f"requirements_{version}.txt") + if not self.session_dir: + raise ValueError("Session directory not set. Cannot save code.") + + code_path = os.path.join(self.session_dir, f"generated_script_{version}.py") + requirements_path = os.path.join( + self.session_dir, f"requirements_{version}.txt" + ) abs_code_path = os.path.abspath(code_path) abs_req_path = os.path.abspath(requirements_path) print(f"Attempting to save code to: {abs_code_path}") @@ -48,7 +53,10 @@ def save_code(self, code, version, version_description, requirements=[]): return code_path def save_requirements(self, requirements, version): - file_path = os.path.join(self.output_dir, f"requirements_{version}.txt") + if not self.session_dir: + raise ValueError("Session directory not set. Cannot save requirements.") + + file_path = os.path.join(self.session_dir, f"requirements_{version}.txt") with open(file_path, "w") as file: for req in requirements: file.write(f"{req}\n") @@ -58,11 +66,22 @@ def get_versions_dict(self): return self.versions_dict def get_code(self, version): - file_path = os.path.join(self.output_dir, f"generated_script_{version}.py") + if not self.session_dir: + raise ValueError("Session directory not set. Cannot retrieve code.") + + file_path = os.path.join(self.session_dir, f"generated_script_{version}.py") with open(file_path, "r") as file: return file.read() def get_requirements(self, version): - file_path = os.path.join(self.output_dir, f"requirements_{version}.txt") + if not self.session_dir: + raise ValueError("Session directory not set. Cannot retrieve requirements.") + + file_path = os.path.join(self.session_dir, f"requirements_{version}.txt") with open(file_path, "r") as file: return file.read().splitlines() + + def set_session_id(self, session_id): + self.session_id = session_id + self.session_dir = os.path.join(self.output_dir, self.session_id) + self._ensure_output_dirs_exist() diff --git a/codeaide/utils/general_utils.py b/codeaide/utils/general_utils.py index e48a092..79039bd 100644 --- a/codeaide/utils/general_utils.py +++ b/codeaide/utils/general_utils.py @@ -2,6 +2,7 @@ import yaml from PyQt5.QtGui import QFont, QColor +from datetime import datetime # Store the path of the general_utils.py file UTILS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -106,3 +107,11 @@ def increment_version(version, increment=1, major_or_minor="minor"): return f"{major + increment}.{minor}" else: return f"{major}.{minor + increment}" + + +def generate_session_id(): + """ + Generate a unique session ID based on the current timestamp. + Format: YYYYMMDD_HHMMSS + """ + return datetime.now().strftime("%Y%m%d_%H%M%S") diff --git a/tests/ui/test_chat_window.py b/tests/ui/test_chat_window.py index 0876c1b..a5484a4 100644 --- a/tests/ui/test_chat_window.py +++ b/tests/ui/test_chat_window.py @@ -122,8 +122,6 @@ def test_model_switching(chat_window, mock_chat_handler): expected_message = MODEL_SWITCH_MESSAGE.format( provider=test_provider, model=test_model, - current_version=current_version, - new_version=new_version, ) assert expected_message in chat_window.chat_display.toPlainText() diff --git a/tests/utils/test_file_handler.py b/tests/utils/test_file_handler.py index 8f47a70..6ddf791 100644 --- a/tests/utils/test_file_handler.py +++ b/tests/utils/test_file_handler.py @@ -1,30 +1,16 @@ import os import tempfile - import pytest - from codeaide.utils.file_handler import FileHandler @pytest.fixture def file_handler(): with tempfile.TemporaryDirectory() as temp_dir: - handler = FileHandler(base_dir=temp_dir) + handler = FileHandler(base_dir=temp_dir, session_id="test_session") yield handler -def test_clear_output_dir(file_handler): - # Create a file in the output directory - test_file = os.path.join(file_handler.output_dir, "test.txt") - with open(test_file, "w") as f: - f.write("test") - - file_handler.clear_output_dir() - - assert os.path.exists(file_handler.output_dir) - assert len(os.listdir(file_handler.output_dir)) == 0 - - def test_save_code(file_handler): code = "print('Hello, World!')" version = "1.0" @@ -87,3 +73,12 @@ def test_nonexistent_version(file_handler): with pytest.raises(FileNotFoundError): file_handler.get_requirements("nonexistent") + + +def test_set_session_id(file_handler): + new_session_id = "new_test_session" + file_handler.set_session_id(new_session_id) + + assert file_handler.session_id == new_session_id + assert file_handler.session_dir.endswith(new_session_id) + assert os.path.exists(file_handler.session_dir) From 16879e2c256008dd8c1a746056f98e6da1931bb0 Mon Sep 17 00:00:00 2001 From: dougollerenshaw Date: Tue, 1 Oct 2024 10:28:27 -0700 Subject: [PATCH 2/2] Save conversation history --- codeaide/logic/chat_handler.py | 6 +++++- codeaide/utils/file_handler.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/codeaide/logic/chat_handler.py b/codeaide/logic/chat_handler.py index e11a5c4..483f459 100644 --- a/codeaide/logic/chat_handler.py +++ b/codeaide/logic/chat_handler.py @@ -36,8 +36,8 @@ def __init__(self): """ self.session_id = generate_session_id() self.cost_tracker = CostTracker() - self.conversation_history = [] self.file_handler = FileHandler(session_id=self.session_id) + self.conversation_history = self.file_handler.load_chat_history() self.env_manager = EnvironmentManager() self.terminal_manager = TerminalManager() self.latest_version = "0.0" @@ -219,6 +219,7 @@ def add_user_input_to_history(self, user_input): self.conversation_history.append( {"role": "user", "content": user_input + version_info} ) + self.file_handler.save_chat_history(self.conversation_history) def get_ai_response(self): """ @@ -316,6 +317,7 @@ def update_conversation_history(self, response): ) else: raise ValueError(f"Unsupported provider: {provider}") + self.file_handler.save_chat_history(self.conversation_history) def create_questions_response(self, text, questions): """ @@ -393,6 +395,7 @@ def add_error_prompt_to_history(self, error_message): """ error_prompt = f"\n\nThere was an error in your last response: {error_message}. Please ensure you're using proper JSON formatting to avoid this error and others like it. Please don't apologize for the error because it will be hidden from the end user." self.conversation_history[-1]["content"] += error_prompt + self.file_handler.save_chat_history(self.conversation_history) def handle_unexpected_error(self, e): """ @@ -514,6 +517,7 @@ def clear_conversation_history(self): None """ self.conversation_history = [] + self.file_handler.save_chat_history(self.conversation_history) def get_latest_version(self): return self.latest_version diff --git a/codeaide/utils/file_handler.py b/codeaide/utils/file_handler.py index 2cedc51..fb2dce7 100644 --- a/codeaide/utils/file_handler.py +++ b/codeaide/utils/file_handler.py @@ -1,5 +1,6 @@ import os import shutil +import json class FileHandler: @@ -16,6 +17,11 @@ def __init__(self, base_dir=None, session_id=None): os.path.join(self.output_dir, self.session_id) if self.session_id else None ) self.versions_dict = {} + self.chat_history_file = ( + os.path.join(self.session_dir, "chat_history.json") + if self.session_dir + else None + ) self._ensure_output_dirs_exist() def _ensure_output_dirs_exist(self): @@ -81,7 +87,30 @@ def get_requirements(self, version): with open(file_path, "r") as file: return file.read().splitlines() + def save_chat_history(self, conversation_history): + if not self.session_dir: + raise ValueError("Session directory not set. Cannot save chat history.") + + try: + with open(self.chat_history_file, "w", encoding="utf-8") as f: + json.dump(conversation_history, f, ensure_ascii=False, indent=2) + print(f"Chat history saved successfully to: {self.chat_history_file}") + except Exception as e: + print(f"Error saving chat history: {str(e)}") + + def load_chat_history(self): + if not self.session_dir or not os.path.exists(self.chat_history_file): + return [] + + try: + with open(self.chat_history_file, "r", encoding="utf-8") as f: + return json.load(f) + except Exception as e: + print(f"Error loading chat history: {str(e)}") + return [] + def set_session_id(self, session_id): self.session_id = session_id self.session_dir = os.path.join(self.output_dir, self.session_id) + self.chat_history_file = os.path.join(self.session_dir, "chat_history.json") self._ensure_output_dirs_exist()