diff --git a/letta/constants.py b/letta/constants.py index 3cd7ae0b..6f3e2094 100644 --- a/letta/constants.py +++ b/letta/constants.py @@ -170,7 +170,7 @@ CONVERSATION_SEARCH_TOOL_NAME = "conversation_search" PRE_EXECUTION_MESSAGE_ARG = "pre_exec_msg" REQUEST_HEARTBEAT_PARAM = "request_heartbeat" -REQUEST_HEARTBEAT_DESCRIPTION = "Request an immediate heartbeat after function execution. Set to `True` if you want to send a follow-up message or run a follow-up function." +REQUEST_HEARTBEAT_DESCRIPTION = "Request an immediate heartbeat after function execution. You MUST set this value to `True` if you want to send a follow-up message or run a follow-up tool call (chain multiple tools together). If set to `False` (the default), then the chain of execution will end immediately after this function call." # Structured output models diff --git a/letta/functions/function_sets/base.py b/letta/functions/function_sets/base.py index a281a61d..623663fb 100644 --- a/letta/functions/function_sets/base.py +++ b/letta/functions/function_sets/base.py @@ -223,6 +223,25 @@ def memory_replace(agent_state: "AgentState", label: str, old_str: str, new_str: old_str (str): The text to replace (must match exactly, including whitespace and indentation). new_str (str): The new text to insert in place of the old text. Do not include line number prefixes. + Examples: + # Update a block containing information about the user + memory_replace(label="human", old_str="Their name is Alice", new_str="Their name is Bob") + + # Update a block containing a todo list + memory_replace(label="todos", old_str="- [ ] Step 5: Search the web", new_str="- [x] Step 5: Search the web") + + # Pass an empty string to + memory_replace(label="human", old_str="Their name is Alice", new_str="") + + # Bad example - do NOT add (view-only) line numbers to the args + memory_replace(label="human", old_str="Line 1: Their name is Alice", new_str="Line 1: Their name is Bob") + + # Bad example - do NOT include the number number warning either + memory_replace(label="human", old_str="# NOTE: Line numbers shown below are to help during editing. Do NOT include line number prefixes in your memory edit tool calls.\\nLine 1: Their name is Alice", new_str="Line 1: Their name is Bob") + + # Good example - no line numbers or line number warning (they are view-only), just the text + memory_replace(label="human", old_str="Their name is Alice", new_str="Their name is Bob") + Returns: str: The success message """ @@ -263,11 +282,11 @@ def memory_replace(agent_state: "AgentState", label: str, old_str: str, new_str: agent_state.memory.update_block_value(label=label, value=new_value) # Create a snippet of the edited section - SNIPPET_LINES = 3 - replacement_line = current_value.split(old_str)[0].count("\n") - start_line = max(0, replacement_line - SNIPPET_LINES) - end_line = replacement_line + SNIPPET_LINES + new_str.count("\n") - snippet = "\n".join(new_value.split("\n")[start_line : end_line + 1]) + # SNIPPET_LINES = 3 + # replacement_line = current_value.split(old_str)[0].count("\n") + # start_line = max(0, replacement_line - SNIPPET_LINES) + # end_line = replacement_line + SNIPPET_LINES + new_str.count("\n") + # snippet = "\n".join(new_value.split("\n")[start_line : end_line + 1]) # Prepare the success message success_msg = f"The core memory block with label `{label}` has been edited. " @@ -290,6 +309,13 @@ def memory_insert(agent_state: "AgentState", label: str, new_str: str, insert_li new_str (str): The text to insert. Do not include line number prefixes. insert_line (int): The line number after which to insert the text (0 for beginning of file). Defaults to -1 (end of the file). + Examples: + # Update a block containing information about the user (append to the end of the block) + memory_insert(label="customer", new_str="The customer's ticket number is 12345") + + # Update a block containing information about the user (insert at the beginning of the block) + memory_insert(label="customer", new_str="The customer's ticket number is 12345", insert_line=0) + Returns: Optional[str]: None is always returned as this function does not produce a response. """ @@ -328,7 +354,7 @@ def memory_insert(agent_state: "AgentState", label: str, new_str: str, insert_li # Collate into the new value to update new_value = "\n".join(new_value_lines) - snippet = "\n".join(snippet_lines) + # snippet = "\n".join(snippet_lines) # Write into the block agent_state.memory.update_block_value(label=label, value=new_value) diff --git a/letta/services/tool_executor/core_tool_executor.py b/letta/services/tool_executor/core_tool_executor.py index 04bac420..a2d0b09b 100644 --- a/letta/services/tool_executor/core_tool_executor.py +++ b/letta/services/tool_executor/core_tool_executor.py @@ -71,15 +71,6 @@ class LettaCoreToolExecutor(ToolExecutor): ) async def send_message(self, agent_state: AgentState, actor: User, message: str) -> Optional[str]: - """ - Sends a message to the human user. - - Args: - message (str): Message contents. All unicode (including emojis) are supported. - - Returns: - Optional[str]: None is always returned as this function does not produce a response. - """ return "Sent message successfully." async def conversation_search( @@ -92,19 +83,6 @@ class LettaCoreToolExecutor(ToolExecutor): start_date: Optional[str] = None, end_date: Optional[str] = None, ) -> Optional[str]: - """ - Search prior conversation history using hybrid search (text + semantic similarity). - - Args: - query (str): String to search for using both text matching and semantic similarity. - roles (Optional[List[Literal["assistant", "user", "tool"]]]): Optional list of message roles to filter by. - limit (Optional[int]): Maximum number of results to return. Uses system default if not specified. - start_date (Optional[str]): Filter results to messages created after this date. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-15", "2024-01-15T14:30". - end_date (Optional[str]): Filter results to messages created before this date. ISO 8601 format: "YYYY-MM-DD" or "YYYY-MM-DDTHH:MM". Examples: "2024-01-20", "2024-01-20T17:00". - - Returns: - str: Query result string containing matching messages with timestamps and content. - """ try: # Parse datetime parameters if provided start_datetime = None @@ -285,20 +263,6 @@ class LettaCoreToolExecutor(ToolExecutor): start_datetime: Optional[str] = None, end_datetime: Optional[str] = None, ) -> Optional[str]: - """ - Search archival memory using semantic (embedding-based) search with optional temporal filtering. - - Args: - query (str): String to search for using semantic similarity. - tags (Optional[list[str]]): Optional list of tags to filter search results. Only passages with these tags will be returned. - tag_match_mode (Literal["any", "all"]): How to match tags - "any" to match passages with any of the tags, "all" to match only passages with all tags. Defaults to "any". - top_k (Optional[int]): Maximum number of results to return. Uses system default if not specified. - start_datetime (Optional[str]): Filter results to passages created after this datetime. ISO 8601 format. - end_datetime (Optional[str]): Filter results to passages created before this datetime. ISO 8601 format. - - Returns: - str: Query result string containing matching passages with timestamps, content, and tags. - """ try: # Use the shared service method to get results formatted_results = await self.agent_manager.search_agent_archival_memory_async( @@ -320,16 +284,6 @@ class LettaCoreToolExecutor(ToolExecutor): async def archival_memory_insert( self, agent_state: AgentState, actor: User, content: str, tags: Optional[list[str]] = None ) -> Optional[str]: - """ - Add to archival memory. Make sure to phrase the memory contents such that it can be easily queried later. - - Args: - content (str): Content to write to the memory. All unicode (including emojis) are supported. - tags (Optional[list[str]]): Optional list of tags to associate with this memory for better organization and filtering. - - Returns: - Optional[str]: None is always returned as this function does not produce a response. - """ await self.passage_manager.insert_passage( agent_state=agent_state, text=content, @@ -340,16 +294,6 @@ class LettaCoreToolExecutor(ToolExecutor): return None async def core_memory_append(self, agent_state: AgentState, actor: User, label: str, content: str) -> Optional[str]: - """ - Append to the contents of core memory. - - Args: - label (str): Section of the memory to be edited. - content (str): Content to write to the memory. All unicode (including emojis) are supported. - - Returns: - Optional[str]: None is always returned as this function does not produce a response. - """ if agent_state.memory.get_block(label).read_only: raise ValueError(f"{READ_ONLY_BLOCK_EDIT_ERROR}") current_value = str(agent_state.memory.get_block(label).value) @@ -366,17 +310,6 @@ class LettaCoreToolExecutor(ToolExecutor): old_content: str, new_content: str, ) -> Optional[str]: - """ - Replace the contents of core memory. To delete memories, use an empty string for new_content. - - Args: - label (str): Section of the memory to be edited. - old_content (str): String to replace. Must be an exact match. - new_content (str): Content to write to the memory. All unicode (including emojis) are supported. - - Returns: - Optional[str]: None is always returned as this function does not produce a response. - """ if agent_state.memory.get_block(label).read_only: raise ValueError(f"{READ_ONLY_BLOCK_EDIT_ERROR}") current_value = str(agent_state.memory.get_block(label).value) @@ -388,20 +321,6 @@ class LettaCoreToolExecutor(ToolExecutor): return None async def memory_replace(self, agent_state: AgentState, actor: User, label: str, old_str: str, new_str: str) -> str: - """ - The memory_replace command allows you to replace a specific string in a memory - block with a new string. This is used for making precise edits. - - Args: - label (str): Section of the memory to be edited, identified by its label. - old_str (str): The text to replace (must match exactly, including whitespace - and indentation). Do not include line number prefixes. - new_str (str): The new text to insert in place of the old text. Do not include line number prefixes. - - Returns: - str: The success message - """ - if agent_state.memory.get_block(label).read_only: raise ValueError(f"{READ_ONLY_BLOCK_EDIT_ERROR}") @@ -478,20 +397,6 @@ class LettaCoreToolExecutor(ToolExecutor): new_str: str, insert_line: int = -1, ) -> str: - """ - The memory_insert command allows you to insert text at a specific location - in a memory block. - - Args: - label (str): Section of the memory to be edited, identified by its label. - new_str (str): The text to insert. Do not include line number prefixes. - insert_line (int): The line number after which to insert the text (0 for - beginning of file). Defaults to -1 (end of the file). - - Returns: - str: The success message - """ - if agent_state.memory.get_block(label).read_only: raise ValueError(f"{READ_ONLY_BLOCK_EDIT_ERROR}") @@ -558,20 +463,6 @@ class LettaCoreToolExecutor(ToolExecutor): return success_msg async def memory_rethink(self, agent_state: AgentState, actor: User, label: str, new_memory: str) -> str: - """ - The memory_rethink command allows you to completely rewrite the contents of a - memory block. Use this tool to make large sweeping changes (e.g. when you want - to condense or reorganize the memory blocks), do NOT use this tool to make small - precise edits (e.g. add or remove a line, replace a specific string, etc). - - Args: - label (str): The memory block to be rewritten, identified by its label. - new_memory (str): The new memory contents with information integrated from - existing memory blocks and the conversation context. Do not include line number prefixes. - - Returns: - str: The success message - """ if agent_state.memory.get_block(label).read_only: raise ValueError(f"{READ_ONLY_BLOCK_EDIT_ERROR}") @@ -610,12 +501,4 @@ class LettaCoreToolExecutor(ToolExecutor): return success_msg async def memory_finish_edits(self, agent_state: AgentState, actor: User) -> None: - """ - Call the memory_finish_edits command when you are finished making edits - (integrating all new information) into the memory blocks. This function - is called when the agent is done rethinking the memory. - - Returns: - Optional[str]: None is always returned as this function does not produce a response. - """ return None