a digital person for bluesky
1#!/usr/bin/env python3 2"""Platform-specific tool management for Void agent.""" 3import logging 4from typing import List, Set 5from letta_client import Letta 6from config_loader import get_letta_config, get_agent_config 7 8logger = logging.getLogger(__name__) 9 10# Define platform-specific tool sets 11BLUESKY_TOOLS = { 12 'search_bluesky_posts', 13 'create_new_bluesky_post', 14 'get_bluesky_feed', 15 'add_post_to_bluesky_reply_thread', 16 'attach_user_blocks', 17 'detach_user_blocks', 18 'user_note_append', 19 'user_note_replace', 20 'user_note_set', 21 'user_note_view', 22} 23 24X_TOOLS = { 25 'add_post_to_x_thread', 26 'attach_x_user_blocks', 27 'detach_x_user_blocks', 28 'x_user_note_append', 29 'x_user_note_replace', 30 'x_user_note_set', 31 'x_user_note_view', 32} 33 34# Common tools shared across platforms 35COMMON_TOOLS = { 36 'halt_activity', 37 'ignore_notification', 38 'annotate_ack', 39 'create_whitewind_blog_post', 40 'fetch_webpage', 41} 42 43 44def ensure_platform_tools(platform: str, agent_id: str = None) -> None: 45 """ 46 Ensure the correct tools are attached for the specified platform. 47 48 This function will: 49 1. Detach tools that belong to other platforms 50 2. Keep common tools attached 51 3. Ensure platform-specific tools are attached 52 53 Args: 54 platform: Either 'bluesky' or 'x' 55 agent_id: Agent ID to manage tools for (uses config default if None) 56 """ 57 if platform not in ['bluesky', 'x']: 58 raise ValueError(f"Platform must be 'bluesky' or 'x', got '{platform}'") 59 60 letta_config = get_letta_config() 61 agent_config = get_agent_config() 62 63 # Use agent ID from config if not provided 64 if agent_id is None: 65 agent_id = letta_config.get('agent_id', agent_config.get('id')) 66 67 try: 68 # Initialize Letta client 69 client = Letta(token=letta_config['api_key']) 70 71 # Get the agent 72 try: 73 agent = client.agents.retrieve(agent_id=agent_id) 74 logger.info(f"Managing tools for agent '{agent.name}' ({agent_id}) for platform '{platform}'") 75 except Exception as e: 76 logger.error(f"Could not retrieve agent {agent_id}: {e}") 77 return 78 79 # Get current attached tools 80 current_tools = client.agents.tools.list(agent_id=str(agent.id)) 81 current_tool_names = {tool.name for tool in current_tools} 82 current_tool_mapping = {tool.name: tool for tool in current_tools} 83 84 # Determine which tools to keep and which to remove 85 if platform == 'bluesky': 86 tools_to_keep = BLUESKY_TOOLS | COMMON_TOOLS 87 tools_to_remove = X_TOOLS 88 required_tools = BLUESKY_TOOLS 89 else: # platform == 'x' 90 tools_to_keep = X_TOOLS | COMMON_TOOLS 91 tools_to_remove = BLUESKY_TOOLS 92 required_tools = X_TOOLS 93 94 # Detach tools that shouldn't be on this platform 95 tools_to_detach = tools_to_remove & current_tool_names 96 for tool_name in tools_to_detach: 97 try: 98 tool = current_tool_mapping[tool_name] 99 client.agents.tools.detach( 100 agent_id=str(agent.id), 101 tool_id=str(tool.id) 102 ) 103 logger.info(f"Detached {tool_name} (not needed for {platform})") 104 except Exception as e: 105 logger.error(f"Failed to detach {tool_name}: {e}") 106 107 # Check which required tools are missing 108 missing_tools = required_tools - current_tool_names 109 110 if missing_tools: 111 logger.info(f"Missing {len(missing_tools)} {platform} tools: {missing_tools}") 112 logger.info(f"Please run the appropriate registration script:") 113 if platform == 'bluesky': 114 logger.info(" python register_tools.py") 115 else: 116 logger.info(" python register_x_tools.py") 117 else: 118 logger.info(f"All required {platform} tools are already attached") 119 120 # Log final state 121 remaining_tools = (current_tool_names - tools_to_detach) & tools_to_keep 122 logger.info(f"Tools configured for {platform}: {len(remaining_tools)} tools active") 123 124 except Exception as e: 125 logger.error(f"Error managing platform tools: {e}") 126 raise 127 128 129def get_attached_tools(agent_id: str = None) -> Set[str]: 130 """ 131 Get the currently attached tools for an agent. 132 133 Args: 134 agent_id: Agent ID to check (uses config default if None) 135 136 Returns: 137 Set of tool names currently attached 138 """ 139 letta_config = get_letta_config() 140 agent_config = get_agent_config() 141 142 # Use agent ID from config if not provided 143 if agent_id is None: 144 agent_id = letta_config.get('agent_id', agent_config.get('id')) 145 146 try: 147 client = Letta(token=letta_config['api_key']) 148 agent = client.agents.retrieve(agent_id=agent_id) 149 current_tools = client.agents.tools.list(agent_id=str(agent.id)) 150 return {tool.name for tool in current_tools} 151 except Exception as e: 152 logger.error(f"Error getting attached tools: {e}") 153 return set() 154 155 156if __name__ == "__main__": 157 import argparse 158 159 parser = argparse.ArgumentParser(description="Manage platform-specific tools for Void agent") 160 parser.add_argument("platform", choices=['bluesky', 'x'], nargs='?', help="Platform to configure tools for") 161 parser.add_argument("--agent-id", help="Agent ID (default: from config)") 162 parser.add_argument("--list", action="store_true", help="List current tools without making changes") 163 164 args = parser.parse_args() 165 166 if args.list: 167 tools = get_attached_tools(args.agent_id) 168 print(f"\nCurrently attached tools ({len(tools)}):") 169 for tool in sorted(tools): 170 platform_indicator = "" 171 if tool in BLUESKY_TOOLS: 172 platform_indicator = " [Bluesky]" 173 elif tool in X_TOOLS: 174 platform_indicator = " [X]" 175 elif tool in COMMON_TOOLS: 176 platform_indicator = " [Common]" 177 print(f" - {tool}{platform_indicator}") 178 else: 179 if not args.platform: 180 parser.error("platform is required when not using --list") 181 ensure_platform_tools(args.platform, args.agent_id)