this repo has no description
at e8ed148d7ade2d6da8c22b0d84160fce90d7bcbc 207 lines 7.6 kB view raw
1#!/usr/bin/env python3 2"""Register all Void tools with a Letta agent.""" 3import os 4import sys 5import logging 6from typing import List 7from dotenv import load_dotenv 8from letta_client import Letta 9from rich.console import Console 10from rich.table import Table 11 12# Import standalone functions and their schemas 13from tools.search import search_bluesky_posts, SearchArgs 14from tools.post import create_new_bluesky_post, PostArgs 15from tools.feed import get_bluesky_feed, FeedArgs 16from tools.blocks import attach_user_blocks, detach_user_blocks, user_note_append, user_note_replace, user_note_set, user_note_view, AttachUserBlocksArgs, DetachUserBlocksArgs, UserNoteAppendArgs, UserNoteReplaceArgs, UserNoteSetArgs, UserNoteViewArgs 17from tools.halt import halt_activity, HaltArgs 18from tools.thread import add_post_to_bluesky_reply_thread, ReplyThreadPostArgs 19 20load_dotenv() 21logging.basicConfig(level=logging.INFO) 22logger = logging.getLogger(__name__) 23console = Console() 24 25 26# Tool configurations: function paired with its args_schema and metadata 27TOOL_CONFIGS = [ 28 { 29 "func": search_bluesky_posts, 30 "args_schema": SearchArgs, 31 "description": "Search for posts on Bluesky matching the given criteria", 32 "tags": ["bluesky", "search", "posts"] 33 }, 34 { 35 "func": create_new_bluesky_post, 36 "args_schema": PostArgs, 37 "description": "Create a new Bluesky post or thread", 38 "tags": ["bluesky", "post", "create", "thread"] 39 }, 40 { 41 "func": get_bluesky_feed, 42 "args_schema": FeedArgs, 43 "description": "Retrieve a Bluesky feed (home timeline or custom feed)", 44 "tags": ["bluesky", "feed", "timeline"] 45 }, 46 { 47 "func": attach_user_blocks, 48 "args_schema": AttachUserBlocksArgs, 49 "description": "Attach user-specific memory blocks to the agent. Creates blocks if they don't exist.", 50 "tags": ["memory", "blocks", "user"] 51 }, 52 { 53 "func": detach_user_blocks, 54 "args_schema": DetachUserBlocksArgs, 55 "description": "Detach user-specific memory blocks from the agent. Blocks are preserved for later use.", 56 "tags": ["memory", "blocks", "user"] 57 }, 58 { 59 "func": user_note_append, 60 "args_schema": UserNoteAppendArgs, 61 "description": "Append a note to a user's memory block. Creates the block if it doesn't exist.", 62 "tags": ["memory", "blocks", "user", "append"] 63 }, 64 { 65 "func": user_note_replace, 66 "args_schema": UserNoteReplaceArgs, 67 "description": "Replace text in a user's memory block.", 68 "tags": ["memory", "blocks", "user", "replace"] 69 }, 70 { 71 "func": user_note_set, 72 "args_schema": UserNoteSetArgs, 73 "description": "Set the complete content of a user's memory block.", 74 "tags": ["memory", "blocks", "user", "set"] 75 }, 76 { 77 "func": user_note_view, 78 "args_schema": UserNoteViewArgs, 79 "description": "View the content of a user's memory block.", 80 "tags": ["memory", "blocks", "user", "view"] 81 }, 82 { 83 "func": halt_activity, 84 "args_schema": HaltArgs, 85 "description": "Signal to halt all bot activity and terminate bsky.py", 86 "tags": ["control", "halt", "terminate"] 87 }, 88 { 89 "func": add_post_to_bluesky_reply_thread, 90 "args_schema": ReplyThreadPostArgs, 91 "description": "Add a single post to the current Bluesky reply thread atomically", 92 "tags": ["bluesky", "reply", "thread", "atomic"] 93 }, 94] 95 96 97def register_tools(agent_name: str = "void", tools: List[str] = None): 98 """Register tools with a Letta agent. 99 100 Args: 101 agent_name: Name of the agent to attach tools to 102 tools: List of tool names to register. If None, registers all tools. 103 """ 104 try: 105 # Initialize Letta client with API key 106 client = Letta(token=os.environ["LETTA_API_KEY"]) 107 108 # Find the agent 109 agents = client.agents.list() 110 agent = None 111 for a in agents: 112 if a.name == agent_name: 113 agent = a 114 break 115 116 if not agent: 117 console.print(f"[red]Error: Agent '{agent_name}' not found[/red]") 118 console.print("\nAvailable agents:") 119 for a in agents: 120 console.print(f" - {a.name}") 121 return 122 123 # Filter tools if specific ones requested 124 tools_to_register = TOOL_CONFIGS 125 if tools: 126 tools_to_register = [t for t in TOOL_CONFIGS if t["func"].__name__ in tools] 127 if len(tools_to_register) != len(tools): 128 missing = set(tools) - {t["func"].__name__ for t in tools_to_register} 129 console.print(f"[yellow]Warning: Unknown tools: {missing}[/yellow]") 130 131 # Create results table 132 table = Table(title=f"Tool Registration for Agent '{agent_name}'") 133 table.add_column("Tool", style="cyan") 134 table.add_column("Status", style="green") 135 table.add_column("Description") 136 137 # Register each tool 138 for tool_config in tools_to_register: 139 func = tool_config["func"] 140 tool_name = func.__name__ 141 142 try: 143 # Create or update the tool using the standalone function 144 created_tool = client.tools.upsert_from_function( 145 func=func, 146 args_schema=tool_config["args_schema"], 147 tags=tool_config["tags"] 148 ) 149 150 # Get current agent tools 151 current_tools = client.agents.tools.list(agent_id=str(agent.id)) 152 tool_names = [t.name for t in current_tools] 153 154 # Check if already attached 155 if created_tool.name in tool_names: 156 table.add_row(tool_name, "Already Attached", tool_config["description"]) 157 else: 158 # Attach to agent 159 client.agents.tools.attach( 160 agent_id=str(agent.id), 161 tool_id=str(created_tool.id) 162 ) 163 table.add_row(tool_name, "✓ Attached", tool_config["description"]) 164 165 except Exception as e: 166 table.add_row(tool_name, f"✗ Error: {str(e)}", tool_config["description"]) 167 logger.error(f"Error registering tool {tool_name}: {e}") 168 169 console.print(table) 170 171 except Exception as e: 172 console.print(f"[red]Error: {str(e)}[/red]") 173 logger.error(f"Fatal error: {e}") 174 175 176def list_available_tools(): 177 """List all available tools.""" 178 table = Table(title="Available Void Tools") 179 table.add_column("Tool Name", style="cyan") 180 table.add_column("Description") 181 table.add_column("Tags", style="dim") 182 183 for tool_config in TOOL_CONFIGS: 184 table.add_row( 185 tool_config["func"].__name__, 186 tool_config["description"], 187 ", ".join(tool_config["tags"]) 188 ) 189 190 console.print(table) 191 192 193if __name__ == "__main__": 194 import argparse 195 196 parser = argparse.ArgumentParser(description="Register Void tools with a Letta agent") 197 parser.add_argument("agent", nargs="?", default="void", help="Agent name (default: void)") 198 parser.add_argument("--tools", nargs="+", help="Specific tools to register (default: all)") 199 parser.add_argument("--list", action="store_true", help="List available tools") 200 201 args = parser.parse_args() 202 203 if args.list: 204 list_available_tools() 205 else: 206 console.print(f"\n[bold]Registering tools for agent: {args.agent}[/bold]\n") 207 register_tools(args.agent, args.tools)