a digital person for bluesky

Add agent_id to config and update codebase to use config-based agent loading

- Add agent_id field to letta section in config.yaml and config.example.yaml
- Update get_letta_config() to include agent_id as required field
- Modify initialize_void() in bsky.py to load agent by ID instead of name lookup
- Update ensure_x_user_blocks_attached() to use config agent_id by default
- Update test_x_blocks.py to use configured agent_id
- Ensures consistent agent usage across all components instead of name-based lookups

This centralizes agent management and prevents issues with agent name mismatches
between different environments or when agent names change.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+13 -16
bsky.py
··· 182 182 description = "A block to store your understanding of users you talk to or observe on the bluesky social network." 183 183 ) 184 184 185 - # Create the agent if it doesn't exist 186 - logger.info("Creating/updating void agent...") 187 - void_agent = upsert_agent( 188 - CLIENT, 189 - name = "void", 190 - block_ids = [ 191 - persona_block.id, 192 - human_block.id, 193 - zeigeist_block.id, 194 - ], 195 - tags = ["social agent", "bluesky"], 196 - model="openai/gpt-4o-mini", 197 - embedding="openai/text-embedding-3-small", 198 - description = "A social media agent trapped in the void.", 199 - project_id = PROJECT_ID 200 - ) 185 + # Get the configured void agent by ID 186 + logger.info("Loading void agent from config...") 187 + from config_loader import get_letta_config 188 + letta_config = get_letta_config() 189 + agent_id = letta_config['agent_id'] 190 + 191 + try: 192 + void_agent = CLIENT.agents.get(agent_id=agent_id) 193 + logger.info(f"Successfully loaded void agent: {void_agent.name} ({agent_id})") 194 + except Exception as e: 195 + logger.error(f"Failed to load void agent {agent_id}: {e}") 196 + logger.error("Please ensure the agent_id in config.yaml is correct") 197 + raise e 201 198 202 199 # Export agent state 203 200 logger.info("Exporting agent state...")
+1
config.example.yaml
··· 6 6 api_key: "your-letta-api-key-here" 7 7 timeout: 600 # 10 minutes timeout for API calls 8 8 project_id: "c82faea2-3ce8-4aa9-a220-b56433e62c92" # Use your specific project ID 9 + agent_id: "agent-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Your void agent ID 9 10 10 11 # Bluesky Configuration 11 12 bluesky:
+1
config_loader.py
··· 175 175 'api_key': config.get_required('letta.api_key', 'LETTA_API_KEY'), 176 176 'timeout': config.get('letta.timeout', 600), 177 177 'project_id': config.get_required('letta.project_id'), 178 + 'agent_id': config.get_required('letta.agent_id'), 178 179 } 179 180 180 181 def get_bluesky_config() -> Dict[str, Any]:
+79
test_x_blocks.py
··· 1 + #!/usr/bin/env python3 2 + """Test script for X user block management functionality.""" 3 + 4 + import sys 5 + import json 6 + from pathlib import Path 7 + 8 + # Add the current directory to path so we can import modules 9 + sys.path.append(str(Path(__file__).parent)) 10 + 11 + from x import ensure_x_user_blocks_attached, get_cached_thread_context 12 + from tools.blocks import x_user_note_view 13 + 14 + def test_x_user_blocks(): 15 + """Test X user block creation and attachment.""" 16 + 17 + # Ensure we're using config file, not environment variable 18 + import os 19 + if 'LETTA_API_KEY' in os.environ: 20 + print("⚠️ Removing LETTA_API_KEY environment variable to use config file") 21 + del os.environ['LETTA_API_KEY'] 22 + 23 + # Use the cached thread data for testing 24 + conversation_id = "1950690566909710618" 25 + thread_data = get_cached_thread_context(conversation_id) 26 + 27 + if not thread_data: 28 + print("❌ No cached thread data found") 29 + return False 30 + 31 + print(f"✅ Loaded cached thread data for conversation {conversation_id}") 32 + print(f"📊 Users in thread: {list(thread_data.get('users', {}).keys())}") 33 + 34 + # Get the configured void agent ID 35 + from config_loader import get_letta_config 36 + letta_config = get_letta_config() 37 + agent_id = letta_config['agent_id'] 38 + print(f"🎯 Using configured void agent {agent_id} for testing") 39 + 40 + try: 41 + # Test user block attachment 42 + print(f"\n🔗 Testing X user block attachment for agent {agent_id}") 43 + ensure_x_user_blocks_attached(thread_data, agent_id) 44 + print("✅ X user block attachment completed") 45 + 46 + # Test viewing created blocks 47 + print("\n👀 Testing X user block viewing:") 48 + for user_id in thread_data.get('users', {}): 49 + try: 50 + # Create mock agent state for testing 51 + class MockAgentState: 52 + def __init__(self, agent_id): 53 + self.id = agent_id 54 + 55 + agent_state = MockAgentState(agent_id) 56 + content = x_user_note_view(user_id, agent_state) 57 + print(f"📋 Block content for user {user_id}:") 58 + print(content) 59 + print("-" * 50) 60 + except Exception as e: 61 + print(f"❌ Error viewing block for user {user_id}: {e}") 62 + 63 + return True 64 + 65 + except Exception as e: 66 + print(f"❌ Error testing X user blocks: {e}") 67 + return False 68 + 69 + if __name__ == "__main__": 70 + print("🧪 Testing X User Block Management") 71 + print("=" * 40) 72 + 73 + success = test_x_user_blocks() 74 + 75 + if success: 76 + print("\n✅ All X user block tests completed successfully!") 77 + else: 78 + print("\n❌ X user block tests failed!") 79 + sys.exit(1)
+7 -3
x.py
··· 404 404 return yaml.dump(simplified_thread, default_flow_style=False, sort_keys=False) 405 405 406 406 407 - def ensure_x_user_blocks_attached(thread_data: Dict, agent_id: str) -> None: 407 + def ensure_x_user_blocks_attached(thread_data: Dict, agent_id: Optional[str] = None) -> None: 408 408 """ 409 409 Ensure all users in the thread have their X user blocks attached. 410 410 Creates blocks with initial content including their handle if they don't exist. 411 411 412 412 Args: 413 413 thread_data: Dict with 'tweets' and 'users' keys from get_thread_context() 414 - agent_id: The Letta agent ID to attach blocks to 414 + agent_id: The Letta agent ID to attach blocks to (defaults to config agent_id) 415 415 """ 416 416 if not thread_data or "users" not in thread_data: 417 417 return ··· 421 421 from config_loader import get_letta_config 422 422 from letta_client import Letta 423 423 424 - # Get Letta client 424 + # Get Letta client and agent_id from config 425 425 config = get_letta_config() 426 426 client = Letta(token=config['api_key'], timeout=config['timeout']) 427 + 428 + # Use provided agent_id or get from config 429 + if agent_id is None: 430 + agent_id = config['agent_id'] 427 431 428 432 # Get agent info to create a mock agent_state for the functions 429 433 class MockAgentState: