this repo has no description

Add enhanced logging and error handling for Letta API debugging

- Add debug logging configuration with separate INFO level for main logger
- Create queue/errors directory for non-retryable failures
- Enhance error handling to distinguish between retryable (524) and non-retryable (413) errors
- Add detailed logging throughout process_mention for better debugging
- Log agent details including tools on initialization

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

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

+97 -10
+97 -10
bsky.py
··· 18 19 # Configure logging 20 logging.basicConfig( 21 - level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 22 ) 23 logger = logging.getLogger("void_bot") 24 25 26 # Create a client with extended timeout for LLM operations ··· 38 # Queue directory 39 QUEUE_DIR = Path("queue") 40 QUEUE_DIR.mkdir(exist_ok=True) 41 42 def initialize_void(): 43 ··· 80 description = "A social media agent trapped in the void.", 81 project_id = PROJECT_ID 82 ) 83 84 return void_agent 85 86 87 def process_mention(void_agent, atproto_client, notification_data): 88 """Process a mention and generate a reply using the Letta agent. 89 - Returns True if successfully processed, False otherwise.""" 90 try: 91 # Handle both dict and object inputs for backwards compatibility 92 if isinstance(notification_data, dict): 93 uri = notification_data['uri'] ··· 100 mention_text = notification_data.record.text if hasattr(notification_data.record, 'text') else "" 101 author_handle = notification_data.author.handle 102 author_name = notification_data.author.display_name or author_handle 103 104 # Retrieve the entire thread associated with the mention 105 try: ··· 120 raise 121 122 # Get thread context as YAML string 123 - thread_context = thread_to_yaml_string(thread) 124 125 - print(thread_context) 126 127 # Create a prompt for the Letta agent with thread context 128 prompt = f"""You received a mention on Bluesky from @{author_handle} ({author_name or author_handle}). ··· 140 Use the bluesky_reply tool to send a response less than 300 characters.""" 141 142 # Get response from Letta agent 143 - logger.info(f"Generating reply for mention from @{author_handle}") 144 logger.debug(f"Prompt being sent: {prompt}") 145 146 try: 147 message_response = CLIENT.agents.messages.create( ··· 149 messages = [{"role":"user", "content": prompt}] 150 ) 151 except Exception as api_error: 152 error_str = str(api_error) 153 logger.error(f"Letta API error: {api_error}") 154 logger.error(f"Error type: {type(api_error).__name__}") 155 logger.error(f"Mention text was: {mention_text}") 156 logger.error(f"Author: @{author_handle}") 157 logger.error(f"URI: {uri}") 158 159 # Check for specific error types 160 if hasattr(api_error, 'status_code'): 161 logger.error(f"API Status code: {api_error.status_code}") 162 - if api_error.status_code == 524: 163 logger.error("524 error - timeout from Cloudflare, will retry later") 164 return False # Keep in queue for retry 165 166 # Check if error indicates we should remove from queue 167 - if 'status_code: 524' in error_str: 168 logger.warning("524 timeout error, keeping in queue for retry") 169 return False # Keep in queue for retry 170 171 raise 172 173 # Extract the reply text from the agent's response 174 reply_text = "" ··· 315 logger.warning(f"Unknown notification type: {notif_data['reason']}") 316 success = True # Remove unknown types from queue 317 318 - # Remove file only after successful processing 319 if success: 320 filepath.unlink() 321 logger.info(f"Processed and removed: {filepath.name}") 322 else: 323 logger.warning(f"Failed to process {filepath.name}, keeping in queue for retry") 324 ··· 368 # Initialize the Letta agent 369 void_agent = initialize_void() 370 logger.info(f"Void agent initialized: {void_agent.id}") 371 372 # Initialize Bluesky client 373 atproto_client = bsky_utils.default_login() 374 logger.info("Connected to Bluesky") 375 376 # Main loop 377 - logger.info(f"Starting notification monitoring (checking every {FETCH_NOTIFICATIONS_DELAY_SEC} seconds)...") 378 379 while True: 380 try: 381 process_notifications(void_agent, atproto_client) 382 - print("Sleeping") 383 sleep(FETCH_NOTIFICATIONS_DELAY_SEC) 384 385 except KeyboardInterrupt:
··· 18 19 # Configure logging 20 logging.basicConfig( 21 + level=logging.DEBUG, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 22 ) 23 logger = logging.getLogger("void_bot") 24 + logger.setLevel(logging.INFO) 25 26 27 # Create a client with extended timeout for LLM operations ··· 39 # Queue directory 40 QUEUE_DIR = Path("queue") 41 QUEUE_DIR.mkdir(exist_ok=True) 42 + QUEUE_ERROR_DIR = Path("queue/errors") 43 + QUEUE_ERROR_DIR.mkdir(exist_ok=True, parents=True) 44 45 def initialize_void(): 46 ··· 83 description = "A social media agent trapped in the void.", 84 project_id = PROJECT_ID 85 ) 86 + 87 + # Log agent details 88 + logger.info(f"Void agent details - ID: {void_agent.id}") 89 + logger.info(f"Agent name: {void_agent.name}") 90 + if hasattr(void_agent, 'llm_config'): 91 + logger.info(f"Agent model: {void_agent.llm_config.model}") 92 + logger.info(f"Agent project_id: {void_agent.project_id}") 93 + if hasattr(void_agent, 'tools'): 94 + logger.info(f"Agent has {len(void_agent.tools)} tools") 95 + for tool in void_agent.tools[:3]: # Show first 3 tools 96 + logger.info(f" - Tool: {tool.name} (type: {tool.tool_type})") 97 98 return void_agent 99 100 101 def process_mention(void_agent, atproto_client, notification_data): 102 """Process a mention and generate a reply using the Letta agent. 103 + 104 + Returns: 105 + True: Successfully processed, remove from queue 106 + False: Failed but retryable, keep in queue 107 + None: Failed with non-retryable error, move to errors directory 108 + """ 109 try: 110 + logger.info(f"Starting process_mention with notification_data type: {type(notification_data)}") 111 + 112 # Handle both dict and object inputs for backwards compatibility 113 if isinstance(notification_data, dict): 114 uri = notification_data['uri'] ··· 121 mention_text = notification_data.record.text if hasattr(notification_data.record, 'text') else "" 122 author_handle = notification_data.author.handle 123 author_name = notification_data.author.display_name or author_handle 124 + 125 + logger.info(f"Extracted data - URI: {uri}, Author: @{author_handle}, Text: {mention_text[:50]}...") 126 127 # Retrieve the entire thread associated with the mention 128 try: ··· 143 raise 144 145 # Get thread context as YAML string 146 + logger.info("Converting thread to YAML string") 147 + try: 148 + thread_context = thread_to_yaml_string(thread) 149 + logger.info(f"Thread context generated, length: {len(thread_context)} characters") 150 + logger.debug(f"Thread context preview: {thread_context[:500]}...") 151 + except Exception as yaml_error: 152 + import traceback 153 + logger.error(f"Error converting thread to YAML: {yaml_error}") 154 + logger.error(f"Full traceback:\n{traceback.format_exc()}") 155 + logger.error(f"Thread type: {type(thread)}") 156 + if hasattr(thread, '__dict__'): 157 + logger.error(f"Thread attributes: {thread.__dict__}") 158 + # Try to continue with a simple context 159 + thread_context = f"Error processing thread context: {str(yaml_error)}" 160 161 + # print(thread_context) 162 163 # Create a prompt for the Letta agent with thread context 164 prompt = f"""You received a mention on Bluesky from @{author_handle} ({author_name or author_handle}). ··· 176 Use the bluesky_reply tool to send a response less than 300 characters.""" 177 178 # Get response from Letta agent 179 + logger.info(f"Mention from @{author_handle}: {mention_text}") 180 logger.debug(f"Prompt being sent: {prompt}") 181 + 182 + # Log the exact parameters being sent to Letta 183 + logger.debug(f"Calling Letta API with agent_id: {void_agent.id}") 184 + logger.debug(f"Message content length: {len(prompt)} characters") 185 186 try: 187 message_response = CLIENT.agents.messages.create( ··· 189 messages = [{"role":"user", "content": prompt}] 190 ) 191 except Exception as api_error: 192 + import traceback 193 error_str = str(api_error) 194 logger.error(f"Letta API error: {api_error}") 195 logger.error(f"Error type: {type(api_error).__name__}") 196 + logger.error(f"Full traceback:\n{traceback.format_exc()}") 197 logger.error(f"Mention text was: {mention_text}") 198 logger.error(f"Author: @{author_handle}") 199 logger.error(f"URI: {uri}") 200 201 + 202 + # Try to extract more info from different error types 203 + if hasattr(api_error, 'response'): 204 + logger.error(f"Error response object exists") 205 + if hasattr(api_error.response, 'text'): 206 + logger.error(f"Response text: {api_error.response.text}") 207 + if hasattr(api_error.response, 'json') and callable(api_error.response.json): 208 + try: 209 + logger.error(f"Response JSON: {api_error.response.json()}") 210 + except: 211 + pass 212 + 213 # Check for specific error types 214 if hasattr(api_error, 'status_code'): 215 logger.error(f"API Status code: {api_error.status_code}") 216 + if hasattr(api_error, 'body'): 217 + logger.error(f"API Response body: {api_error.body}") 218 + if hasattr(api_error, 'headers'): 219 + logger.error(f"API Response headers: {api_error.headers}") 220 + 221 + if api_error.status_code == 413: 222 + logger.error("413 Payload Too Large - moving to errors directory") 223 + return None # Move to errors directory - payload is too large to ever succeed 224 + elif api_error.status_code == 524: 225 logger.error("524 error - timeout from Cloudflare, will retry later") 226 return False # Keep in queue for retry 227 228 # Check if error indicates we should remove from queue 229 + if 'status_code: 413' in error_str or 'Payload Too Large' in error_str: 230 + logger.warning("Payload too large error, moving to errors directory") 231 + return None # Move to errors directory - cannot be fixed by retry 232 + elif 'status_code: 524' in error_str: 233 logger.warning("524 timeout error, keeping in queue for retry") 234 return False # Keep in queue for retry 235 236 raise 237 + 238 + # Log successful response 239 + logger.debug("Successfully received response from Letta API") 240 + logger.debug(f"Number of messages in response: {len(message_response.messages) if hasattr(message_response, 'messages') else 'N/A'}") 241 242 # Extract the reply text from the agent's response 243 reply_text = "" ··· 384 logger.warning(f"Unknown notification type: {notif_data['reason']}") 385 success = True # Remove unknown types from queue 386 387 + # Handle file based on processing result 388 if success: 389 filepath.unlink() 390 logger.info(f"Processed and removed: {filepath.name}") 391 + elif success is None: # Special case for moving to error directory 392 + error_path = QUEUE_ERROR_DIR / filepath.name 393 + filepath.rename(error_path) 394 + logger.warning(f"Moved {filepath.name} to errors directory") 395 else: 396 logger.warning(f"Failed to process {filepath.name}, keeping in queue for retry") 397 ··· 441 # Initialize the Letta agent 442 void_agent = initialize_void() 443 logger.info(f"Void agent initialized: {void_agent.id}") 444 + 445 + # Check if agent has required tools 446 + if hasattr(void_agent, 'tools') and void_agent.tools: 447 + tool_names = [tool.name for tool in void_agent.tools] 448 + logger.info(f"Agent has tools: {tool_names}") 449 + 450 + # Check for bluesky-related tools 451 + bluesky_tools = [name for name in tool_names if 'bluesky' in name.lower() or 'reply' in name.lower()] 452 + if bluesky_tools: 453 + logger.info(f"Found Bluesky-related tools: {bluesky_tools}") 454 + else: 455 + logger.warning("No Bluesky-related tools found! Agent may not be able to reply.") 456 + else: 457 + logger.warning("Agent has no tools registered!") 458 459 # Initialize Bluesky client 460 atproto_client = bsky_utils.default_login() 461 logger.info("Connected to Bluesky") 462 463 # Main loop 464 + logger.info(f"Starting notification monitoring, checking every {FETCH_NOTIFICATIONS_DELAY_SEC} seconds") 465 466 while True: 467 try: 468 process_notifications(void_agent, atproto_client) 469 + logger.debug("Sleeping, no notifications were detected") 470 sleep(FETCH_NOTIFICATIONS_DELAY_SEC) 471 472 except KeyboardInterrupt: