From 2401c4a2dc6d831c66403964fa3ef603320539dc Mon Sep 17 00:00:00 2001 From: cpacker Date: Sat, 27 Jul 2024 21:43:12 -0700 Subject: [PATCH 1/4] add update system prompt method to server.py --- memgpt/agent.py | 50 ++++++++++++++++++++++++++++------------- memgpt/server/server.py | 30 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/memgpt/agent.py b/memgpt/agent.py index 47ecff7d01..335227707d 100644 --- a/memgpt/agent.py +++ b/memgpt/agent.py @@ -298,16 +298,6 @@ def append_to_messages(self, added_messages: List[dict]): ] self._append_to_messages(added_messages_objs) - def _swap_system_message(self, new_system_message: Message): - assert isinstance(new_system_message, Message) - assert new_system_message.role == "system", new_system_message - assert self._messages[0].role == "system", self._messages - - self.persistence_manager.swap_system_message(new_system_message) - - new_messages = [new_system_message] + self._messages[1:] # swap index 0 (system) - self._messages = new_messages - def _get_ai_reply( self, message_sequence: List[Message], @@ -862,7 +852,26 @@ def heartbeat_is_paused(self): elapsed_time = get_utc_time() - self.pause_heartbeats_start return elapsed_time.total_seconds() < self.pause_heartbeats_minutes * 60 - def rebuild_memory(self): + def _swap_system_message_in_buffer(self, new_system_message: str): + """Update the system message (NOT prompt) of the Agent (requires updating the internal buffer)""" + assert isinstance(new_system_message, str) + new_system_message_obj = Message.dict_to_message( + agent_id=self.agent_state.id, + user_id=self.agent_state.user_id, + model=self.model, + openai_message_dict=new_system_message, + ) + + assert new_system_message_obj.role == "system", new_system_message_obj + assert self._messages[0].role == "system", self._messages + + self.persistence_manager.swap_system_message(new_system_message_obj) + + new_messages = [new_system_message_obj] + self._messages[1:] # swap index 0 (system) + self._messages = new_messages + + # TODO need to enable running rebuild_memory to keep the old timestamp + still update when the memory is the same (if the sys prompt changed) + def rebuild_memory(self, force=True, update_timestamp=True): """Rebuilds the system message with the latest memory object""" curr_system_message = self.messages[0] # this is the system + memory bank, not just the system prompt @@ -886,16 +895,25 @@ def rebuild_memory(self): printd(f"Rebuilding system with new memory...\nDiff:\n{diff}") # Swap the system message out (only if there is a diff) - self._swap_system_message( - Message.dict_to_message( - agent_id=self.agent_state.id, user_id=self.agent_state.user_id, model=self.model, openai_message_dict=new_system_message - ) - ) + self._swap_system_message_in_buffer(new_system_message=new_system_message) assert self.messages[0]["content"] == new_system_message["content"], ( self.messages[0]["content"], new_system_message["content"], ) + def update_system_prompt(self, new_system_prompt: str): + """Update the system prompt of the agent (requires rebuilding the memory block if there's a difference)""" + if new_system_prompt == self.system: + return + + self.system = new_system_prompt + + # updating the system prompt requires rebuilding the memory block inside the compiled system message + self.rebuild_memory() + + # make sure to persist the change + _ = self.update_state() + def add_function(self, function_name: str) -> str: # TODO: refactor raise NotImplementedError diff --git a/memgpt/server/server.py b/memgpt/server/server.py index 0dae358a08..daca5855af 100644 --- a/memgpt/server/server.py +++ b/memgpt/server/server.py @@ -1301,6 +1301,36 @@ def update_agent_core_memory(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_ "modified": modified, } + def update_agent_system_prompt(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_system_prompt: str) -> dict: + """Update the agents system message, return the new state""" + if self.ms.get_user(user_id=user_id) is None: + raise ValueError(f"User user_id={user_id} does not exist") + if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None: + raise ValueError(f"Agent agent_id={agent_id} does not exist") + + # Get the agent object (loaded in memory) + memgpt_agent = self._get_or_load_agent(user_id=user_id, agent_id=agent_id) + + # Cache the old prompt + old_system_prompt = str(memgpt_agent.system) + + if new_system_prompt != memgpt_agent.system: + memgpt_agent.update_system_prompt(new_system_prompt=new_system_prompt) + modified = True + else: + modified = False + + # If we modified the memory contents, we need to rebuild the memory block inside the system message + if modified: + # save agent + save_agent(memgpt_agent, self.ms) + + return { + "old_system_prompt": old_system_prompt, + "new_system_prompt": new_system_prompt, + "modified": modified, + } + def rename_agent(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_agent_name: str) -> AgentState: """Update the name of the agent in the database""" if self.ms.get_user(user_id=user_id) is None: From 7a0364571e664560438d52a354393798048ce4a8 Mon Sep 17 00:00:00 2001 From: cpacker Date: Sun, 28 Jul 2024 18:16:51 -0700 Subject: [PATCH 2/4] revert addition to server.py --- memgpt/server/server.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/memgpt/server/server.py b/memgpt/server/server.py index daca5855af..0dae358a08 100644 --- a/memgpt/server/server.py +++ b/memgpt/server/server.py @@ -1301,36 +1301,6 @@ def update_agent_core_memory(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_ "modified": modified, } - def update_agent_system_prompt(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_system_prompt: str) -> dict: - """Update the agents system message, return the new state""" - if self.ms.get_user(user_id=user_id) is None: - raise ValueError(f"User user_id={user_id} does not exist") - if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None: - raise ValueError(f"Agent agent_id={agent_id} does not exist") - - # Get the agent object (loaded in memory) - memgpt_agent = self._get_or_load_agent(user_id=user_id, agent_id=agent_id) - - # Cache the old prompt - old_system_prompt = str(memgpt_agent.system) - - if new_system_prompt != memgpt_agent.system: - memgpt_agent.update_system_prompt(new_system_prompt=new_system_prompt) - modified = True - else: - modified = False - - # If we modified the memory contents, we need to rebuild the memory block inside the system message - if modified: - # save agent - save_agent(memgpt_agent, self.ms) - - return { - "old_system_prompt": old_system_prompt, - "new_system_prompt": new_system_prompt, - "modified": modified, - } - def rename_agent(self, user_id: uuid.UUID, agent_id: uuid.UUID, new_agent_name: str) -> AgentState: """Update the name of the agent in the database""" if self.ms.get_user(user_id=user_id) is None: From 3dad3e56fc21f5a72429f73c92a2657378109e72 Mon Sep 17 00:00:00 2001 From: cpacker Date: Sun, 28 Jul 2024 18:38:21 -0700 Subject: [PATCH 3/4] added CLI command + fixed bugs in message swapping --- memgpt/agent.py | 27 +++++++++++++++++++++------ memgpt/main.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/memgpt/agent.py b/memgpt/agent.py index 6b379147e5..b2d4fb2b96 100644 --- a/memgpt/agent.py +++ b/memgpt/agent.py @@ -391,6 +391,10 @@ def _get_ai_reply( # putting inner thoughts in func args or not inner_thoughts_in_kwargs=inner_thoughts_in_kwargs, ) + + if len(response.choices) == 0: + raise Exception(f"API call didn't return a message: {response}") + # special case for 'length' if response.choices[0].finish_reason == "length": raise Exception("Finish reason was length (maximum context length)") @@ -926,7 +930,7 @@ def _swap_system_message_in_buffer(self, new_system_message: str): agent_id=self.agent_state.id, user_id=self.agent_state.user_id, model=self.model, - openai_message_dict=new_system_message, + openai_message_dict={"role": "system", "content": new_system_message}, ) assert new_system_message_obj.role == "system", new_system_message_obj @@ -938,21 +942,29 @@ def _swap_system_message_in_buffer(self, new_system_message: str): self._messages = new_messages # TODO need to enable running rebuild_memory to keep the old timestamp + still update when the memory is the same (if the sys prompt changed) - def rebuild_memory(self, force=True, update_timestamp=True): + def rebuild_memory(self, force=False, update_timestamp=True): """Rebuilds the system message with the latest memory object""" curr_system_message = self.messages[0] # this is the system + memory bank, not just the system prompt # NOTE: This is a hacky way to check if the memory has changed memory_repr = str(self.memory) - if memory_repr == curr_system_message["content"][-(len(memory_repr)) :]: + if not force and memory_repr == curr_system_message["content"][-(len(memory_repr)) :]: printd(f"Memory has not changed, not rebuilding system") return + # If the memory didn't update, we probably don't want to update the timestamp inside + # For example, if we're doing a system prompt swap, this should probably be False + if update_timestamp: + memory_edit_timestamp = get_utc_time() + else: + # NOTE: a bit of a hack - we pull the timestamp from the message created_by + memory_edit_timestamp = self._messages[0].created_at + # update memory (TODO: potentially update recall/archival stats seperately) new_system_message_str = compile_system_message( system_prompt=self.system, in_context_memory=self.memory, - in_context_memory_last_edit=get_utc_time(), # NOTE: new timestamp + in_context_memory_last_edit=memory_edit_timestamp, archival_memory=self.persistence_manager.archival_memory, recall_memory=self.persistence_manager.recall_memory, user_defined_variables=None, @@ -968,7 +980,7 @@ def rebuild_memory(self, force=True, update_timestamp=True): printd(f"Rebuilding system with new memory...\nDiff:\n{diff}") # Swap the system message out (only if there is a diff) - self._swap_system_message_in_buffer(new_system_message=new_system_message) + self._swap_system_message_in_buffer(new_system_message=new_system_message_str) assert self.messages[0]["content"] == new_system_message["content"], ( self.messages[0]["content"], new_system_message["content"], @@ -976,13 +988,16 @@ def rebuild_memory(self, force=True, update_timestamp=True): def update_system_prompt(self, new_system_prompt: str): """Update the system prompt of the agent (requires rebuilding the memory block if there's a difference)""" + assert isinstance(new_system_prompt, str) + if new_system_prompt == self.system: + input("same???") return self.system = new_system_prompt # updating the system prompt requires rebuilding the memory block inside the compiled system message - self.rebuild_memory() + self.rebuild_memory(force=True, update_timestamp=False) # make sure to persist the change _ = self.update_state() diff --git a/memgpt/main.py b/memgpt/main.py index 1de91bd69a..fcf392ddb6 100644 --- a/memgpt/main.py +++ b/memgpt/main.py @@ -377,6 +377,41 @@ def run_agent_loop( questionary.print(f" {desc}") continue + elif user_input.lower().startswith("/systemswap"): + if len(user_input) < len("/systemswap "): + print("Missing new system prompt after the command") + continue + old_system_prompt = memgpt_agent.system + new_system_prompt = user_input[len("/systemswap ") :].strip() + + # Show warning and prompts to user + typer.secho( + "\nWARNING: You are about to change the system prompt.", + # fg=typer.colors.BRIGHT_YELLOW, + bold=True, + ) + typer.secho( + f"\nOld system prompt:\n{old_system_prompt}", + fg=typer.colors.RED, + bold=True, + ) + typer.secho( + f"\nNew system prompt:\n{new_system_prompt}", + fg=typer.colors.GREEN, + bold=True, + ) + + # Ask for confirmation + confirm = questionary.confirm("Do you want to proceed with the swap?").ask() + + if confirm: + memgpt_agent.update_system_prompt(new_system_prompt=new_system_prompt) + print("System prompt updated successfully.") + else: + print("System prompt swap cancelled.") + + continue + else: print(f"Unrecognized command: {user_input}") continue From 336d54210c018104259ab03de094cd219a4f54f8 Mon Sep 17 00:00:00 2001 From: cpacker Date: Sun, 28 Jul 2024 18:43:51 -0700 Subject: [PATCH 4/4] clean old comment --- memgpt/agent.py | 1 - 1 file changed, 1 deletion(-) diff --git a/memgpt/agent.py b/memgpt/agent.py index b2d4fb2b96..7fa6e47d80 100644 --- a/memgpt/agent.py +++ b/memgpt/agent.py @@ -941,7 +941,6 @@ def _swap_system_message_in_buffer(self, new_system_message: str): new_messages = [new_system_message_obj] + self._messages[1:] # swap index 0 (system) self._messages = new_messages - # TODO need to enable running rebuild_memory to keep the old timestamp + still update when the memory is the same (if the sys prompt changed) def rebuild_memory(self, force=False, update_timestamp=True): """Rebuilds the system message with the latest memory object""" curr_system_message = self.messages[0] # this is the system + memory bank, not just the system prompt