a digital person for bluesky
1# Letta Dynamic Block Loading Issue 2 3## Problem Summary 4 5Void agent experiences persistent failures when attempting to use memory functions (like `memory_insert` or `core_memory_replace`) on dynamically attached blocks. The error manifests as: 6 7``` 8KeyError: 'Block field user_nonbinary_computer does not exist (available sections = long_term_objectives, system_information, user_dulanyw_bsky_social, posting_ideas, conversation_summary, user_example_com, user_elouan_xyz, user_barrycarlyon_co_uk, user_unxpctd_xyz, user_vasthypno_bsky_social, scratchpad, void-persona, zeitgeist, communication_guidelines, tool_use_guide, user_tachikoma_elsewhereunbound_com, tool_usage_rules)' 9``` 10 11## Root Cause Analysis 12 13The issue occurs due to a **state synchronization problem** between the Letta API and the agent's internal memory state: 14 151. **Block Attachment via API**: The `attach_user_blocks` tool successfully creates and attaches blocks using the Letta client API (`client.agents.blocks.attach`) 16 172. **Stale Agent State**: However, the agent's internal `agent_state.memory` object is loaded once at the beginning of message processing and is NOT refreshed after API changes 18 193. **Memory Function Failure**: When memory functions like `memory_insert` try to access the newly attached block via `agent_state.memory.get_block(label)`, it fails because the block only exists in the database/API layer, not in the agent's loaded memory state 20 21## Technical Details 22 23### The Problematic Flow 24 25```python 26# 1. Agent receives message and agent_state is loaded with initial blocks 27agent_state.memory.blocks = ["human", "persona", "zeitgeist", ...] 28 29# 2. Agent calls attach_user_blocks tool 30def attach_user_blocks(handles, agent_state): 31 client = Letta(token=os.environ["LETTA_API_KEY"]) 32 # This succeeds - block is created and attached via API 33 client.agents.blocks.attach(agent_id=agent_state.id, block_id=block.id) 34 # BUT: agent_state.memory is NOT updated! 35 36# 3. Agent tries to use memory_insert on the new block 37def memory_insert(agent_state, label, content): 38 # This fails because agent_state.memory doesn't have the new block 39 current_value = agent_state.memory.get_block(label).value # KeyError! 40``` 41 42### Evidence from Codebase 43 44From `letta/letta/schemas/memory.py:129`: 45```python 46def get_block(self, label: str) -> Block: 47 """Correct way to index into the memory.memory field, returns a Block""" 48 keys = [] 49 for block in self.blocks: 50 if block.label == label: 51 return block 52 keys.append(block.label) 53 raise KeyError(f"Block field {label} does not exist (available sections = {', '.join(keys)})") 54``` 55 56The error message in the exception matches exactly what we see in production. 57 58## Reproduction 59 60The `letta_dynamic_block_issue.py` script demonstrates this issue with a mock setup that reproduces the exact error condition. 61 62## Impact 63 64This affects Void's ability to: 65- Dynamically create user-specific memory blocks during conversations 66- Update those blocks with new information about users 67- Maintain personalized context for individual users 68 69The issue is intermittent because it depends on timing and whether blocks are attached/accessed within the same message processing cycle. 70 71## Potential Solutions 72 73### 1. Refresh Agent State After Tool Execution 74Reload `agent_state` from the database after each tool call that modifies blocks: 75```python 76# After tool execution in Letta's tool executor 77agent_state = agent_manager.get_agent_by_id(agent_id, include_blocks=True) 78``` 79 80### 2. Synchronize agent_state.memory in attach_user_blocks 81Update both the API and the in-memory state: 82```python 83def attach_user_blocks(handles, agent_state): 84 # Attach via API 85 client.agents.blocks.attach(agent_id=agent_state.id, block_id=block.id) 86 87 # Also update agent_state.memory directly 88 agent_state.memory.set_block(block) 89``` 90 91### 3. Lazy Loading in Memory Functions 92Make memory functions check the API if a block isn't found locally: 93```python 94def get_block(self, label: str) -> Block: 95 # Try local first 96 for block in self.blocks: 97 if block.label == label: 98 return block 99 100 # If not found, check API 101 api_blocks = client.agents.blocks.list(agent_id=self.agent_id) 102 for block in api_blocks: 103 if block.label == label: 104 self.blocks.append(block) # Cache it 105 return block 106 107 # Finally raise error 108 raise KeyError(...) 109``` 110 111### 4. Event-Driven State Synchronization 112Implement a callback/event system where API changes automatically update `agent_state.memory`. 113 114## Recommended Fix 115 116**Solution #1 (Refresh Agent State)** is likely the most robust as it ensures consistency without requiring changes to existing tools. The refresh should happen in Letta's tool execution pipeline after any tool that can modify agent state. 117 118## Files Provided 119 120- `letta_dynamic_block_issue.py` - Minimal reproduction script 121- `minimal_block_issue_simple.py` - API-based reproduction attempt 122- This documentation file 123 124## Related Code Locations 125 126- `void/tools/blocks.py` - Contains the attach_user_blocks tool 127- `letta/letta/schemas/memory.py:129` - Where the KeyError is thrown 128- `letta/letta/functions/function_sets/base.py` - Memory functions (memory_insert, core_memory_replace)