a digital person for bluesky

Add thread length gate to skip overly long threads

Adds a configurable max_thread_posts setting that allows skipping
notifications when the thread is too long. This helps avoid:
- Context window exhaustion
- Processing extremely long conversations
- High token costs for very deep threads

Configuration:
- bot.max_thread_posts: Maximum number of posts allowed in a thread
- Set to 0 to disable the gate (default)
- When exceeded, notification is removed from queue with info log

Implementation:
- count_thread_posts() helper function in bsky_utils.py
- Thread length check happens immediately after fetching the thread
- Logged as: "Thread too long (N posts > M max), skipping this mention"

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

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

+3 -2
CONFIG.md
··· 53 fetch_notifications_delay: 30 # Seconds between notification checks 54 max_processed_notifications: 10000 # Max notifications to track 55 max_notification_pages: 20 # Max pages to fetch per cycle 56 - 57 agent: 58 name: "void" # Agent name 59 model: "openai/gpt-4o-mini" # LLM model to use 60 embedding: "openai/text-embedding-3-small" # Embedding model 61 description: "A social media agent trapped in the void." 62 max_steps: 100 # Max steps per agent interaction 63 - 64 # Memory blocks configuration 65 blocks: 66 zeitgeist:
··· 53 fetch_notifications_delay: 30 # Seconds between notification checks 54 max_processed_notifications: 10000 # Max notifications to track 55 max_notification_pages: 20 # Max pages to fetch per cycle 56 + max_thread_posts: 0 # Skip threads longer than this (0 = no limit) 57 + 58 agent: 59 name: "void" # Agent name 60 model: "openai/gpt-4o-mini" # LLM model to use 61 embedding: "openai/text-embedding-3-small" # Embedding model 62 description: "A social media agent trapped in the void." 63 max_steps: 100 # Max steps per agent interaction 64 + 65 # Memory blocks configuration 66 blocks: 67 zeitgeist:
+9 -1
bsky.py
··· 1 # Rich imports removed - using simple text formatting 2 from time import sleep 3 from letta_client import Letta 4 - from bsky_utils import thread_to_yaml_string 5 import os 6 import logging 7 import json ··· 252 # Re-raise other errors 253 logger.error(f"Error fetching thread: {e}") 254 raise 255 256 # Get thread context as YAML string 257 logger.debug("Converting thread to YAML string")
··· 1 # Rich imports removed - using simple text formatting 2 from time import sleep 3 from letta_client import Letta 4 + from bsky_utils import thread_to_yaml_string, count_thread_posts 5 import os 6 import logging 7 import json ··· 252 # Re-raise other errors 253 logger.error(f"Error fetching thread: {e}") 254 raise 255 + 256 + # Check thread length against configured maximum 257 + max_thread_posts = get_config().get('bot.max_thread_posts', 0) 258 + if max_thread_posts > 0: 259 + thread_post_count = count_thread_posts(thread) 260 + if thread_post_count > max_thread_posts: 261 + logger.info(f"Thread too long ({thread_post_count} posts > {max_thread_posts} max), skipping this mention") 262 + return True # Return True to remove from queue 263 264 # Get thread context as YAML string 265 logger.debug("Converting thread to YAML string")
+15 -1
bsky_utils.py
··· 159 return {'posts': posts} 160 161 162 def thread_to_yaml_string(thread, strip_metadata=True): 163 """ 164 Convert thread data to a YAML-formatted string for LLM parsing. ··· 172 """ 173 # First flatten the thread structure to avoid deep nesting 174 flattened = flatten_thread_structure(thread) 175 - 176 # Convert complex objects to basic types 177 basic_thread = convert_to_basic_types(flattened) 178
··· 159 return {'posts': posts} 160 161 162 + def count_thread_posts(thread): 163 + """ 164 + Count the number of posts in a thread. 165 + 166 + Args: 167 + thread: The thread data from get_post_thread 168 + 169 + Returns: 170 + Integer count of posts in the thread 171 + """ 172 + flattened = flatten_thread_structure(thread) 173 + return len(flattened.get('posts', [])) 174 + 175 + 176 def thread_to_yaml_string(thread, strip_metadata=True): 177 """ 178 Convert thread data to a YAML-formatted string for LLM parsing. ··· 186 """ 187 # First flatten the thread structure to avoid deep nesting 188 flattened = flatten_thread_structure(thread) 189 + 190 # Convert complex objects to basic types 191 basic_thread = convert_to_basic_types(flattened) 192
+5 -2
config.example.yaml
··· 24 bot: 25 # Notification check delay in seconds 26 fetch_notifications_delay: 30 27 - 28 # Maximum number of processed notifications to track 29 max_processed_notifications: 10000 30 - 31 # Maximum pages to fetch when getting notifications 32 max_notification_pages: 20 33 34 # Agent configuration 35 agent:
··· 24 bot: 25 # Notification check delay in seconds 26 fetch_notifications_delay: 30 27 + 28 # Maximum number of processed notifications to track 29 max_processed_notifications: 10000 30 + 31 # Maximum pages to fetch when getting notifications 32 max_notification_pages: 20 33 + 34 + # Maximum number of posts in a thread before skipping (0 = no limit) 35 + max_thread_posts: 0 36 37 # Agent configuration 38 agent: