a digital person for bluesky
1"""Block management tools for user-specific memory blocks.""" 2from pydantic import BaseModel, Field 3from typing import List, Dict, Any 4 5def get_letta_client(): 6 """Get a Letta client using configuration.""" 7 try: 8 from config_loader import get_letta_config 9 from letta_client import Letta 10 config = get_letta_config() 11 client_params = { 12 'token': config['api_key'], 13 'timeout': config['timeout'] 14 } 15 if config.get('base_url'): 16 client_params['base_url'] = config['base_url'] 17 return Letta(**client_params) 18 except (ImportError, FileNotFoundError, KeyError): 19 # Fallback to environment variable 20 import os 21 from letta_client import Letta 22 return Letta(token=os.environ["LETTA_API_KEY"]) 23 24def get_x_letta_client(): 25 """Get a Letta client using X configuration.""" 26 try: 27 import yaml 28 from pathlib import Path 29 from letta_client import Letta 30 31 # Load x_config.yaml 32 config_path = Path("x_config.yaml") 33 if not config_path.exists(): 34 # Fall back to regular client if x_config.yaml doesn't exist 35 return get_letta_client() 36 37 with open(config_path, 'r') as f: 38 config = yaml.safe_load(f) 39 40 letta_config = config.get('letta', {}) 41 client_params = { 42 'token': letta_config['api_key'], 43 'timeout': letta_config.get('timeout', 600) 44 } 45 if letta_config.get('base_url'): 46 client_params['base_url'] = letta_config['base_url'] 47 return Letta(**client_params) 48 except (ImportError, FileNotFoundError, KeyError, yaml.YAMLError): 49 # Fall back to regular client 50 return get_letta_client() 51 52def get_platform_letta_client(is_x_function=False): 53 """Get a Letta client based on the platform context.""" 54 if is_x_function: 55 return get_x_letta_client() 56 return get_letta_client() 57 58 59class AttachUserBlocksArgs(BaseModel): 60 handles: List[str] = Field(..., description="List of user Bluesky handles (e.g., ['user1.bsky.social', 'user2.bsky.social'])") 61 62 63class DetachUserBlocksArgs(BaseModel): 64 handles: List[str] = Field(..., description="List of user Bluesky handles (e.g., ['user1.bsky.social', 'user2.bsky.social'])") 65 66 67class UserNoteAppendArgs(BaseModel): 68 handle: str = Field(..., description="User Bluesky handle (e.g., 'cameron.pfiffer.org')") 69 note: str = Field(..., description="Note to append to the user's memory block (e.g., '\\n- Cameron is a person')") 70 71 72class UserNoteReplaceArgs(BaseModel): 73 handle: str = Field(..., description="User Bluesky handle (e.g., 'cameron.pfiffer.org')") 74 old_text: str = Field(..., description="Text to find and replace in the user's memory block") 75 new_text: str = Field(..., description="Text to replace the old_text with") 76 77 78class UserNoteSetArgs(BaseModel): 79 handle: str = Field(..., description="User Bluesky handle (e.g., 'cameron.pfiffer.org')") 80 content: str = Field(..., description="Complete content to set for the user's memory block") 81 82 83class UserNoteViewArgs(BaseModel): 84 handle: str = Field(..., description="User Bluesky handle (e.g., 'cameron.pfiffer.org')") 85 86 87# X (Twitter) User Block Management 88class AttachXUserBlocksArgs(BaseModel): 89 user_ids: List[str] = Field(..., description="List of X user IDs (e.g., ['1232326955652931584', '1950680610282094592'])") 90 91 92class DetachXUserBlocksArgs(BaseModel): 93 user_ids: List[str] = Field(..., description="List of X user IDs (e.g., ['1232326955652931584', '1950680610282094592'])") 94 95 96class XUserNoteAppendArgs(BaseModel): 97 user_id: str = Field(..., description="X user ID (e.g., '1232326955652931584')") 98 note: str = Field(..., description="Note to append to the user's memory block (e.g., '\\\\n- Cameron is a person')") 99 100 101class XUserNoteReplaceArgs(BaseModel): 102 user_id: str = Field(..., description="X user ID (e.g., '1232326955652931584')") 103 old_text: str = Field(..., description="Text to find and replace in the user's memory block") 104 new_text: str = Field(..., description="Text to replace the old_text with") 105 106 107class XUserNoteSetArgs(BaseModel): 108 user_id: str = Field(..., description="X user ID (e.g., '1232326955652931584')") 109 content: str = Field(..., description="Complete content to set for the user's memory block") 110 111 112class XUserNoteViewArgs(BaseModel): 113 user_id: str = Field(..., description="X user ID (e.g., '1232326955652931584')") 114 115 116 117def attach_user_blocks(handles: list, agent_state: "AgentState") -> str: 118 """ 119 Attach user-specific memory blocks to the agent. Creates blocks if they don't exist. 120 121 Args: 122 handles: List of user Bluesky handles (e.g., ['user1.bsky.social', 'user2.bsky.social']) 123 agent_state: The agent state object containing agent information 124 125 Returns: 126 String with attachment results for each handle 127 """ 128 129 handles = list(set(handles)) 130 131 try: 132 # Try to get client the local way first, fall back to inline for cloud execution 133 try: 134 client = get_letta_client() 135 except (ImportError, NameError): 136 # Create Letta client inline for cloud execution 137 import os 138 from letta_client import Letta 139 client = Letta(token=os.environ["LETTA_API_KEY"]) 140 results = [] 141 142 # Get current blocks using the API 143 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 144 current_block_labels = set() 145 current_block_ids = set() 146 147 for block in current_blocks: 148 current_block_labels.add(block.label) 149 current_block_ids.add(str(block.id)) 150 151 for handle in handles: 152 # Sanitize handle for block label - completely self-contained 153 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 154 block_label = f"user_{clean_handle}" 155 156 # Skip if already attached 157 if block_label in current_block_labels: 158 results.append(f"{handle}: Already attached") 159 continue 160 161 # Check if block exists or create new one 162 try: 163 blocks = client.blocks.list(label=block_label) 164 if blocks and len(blocks) > 0: 165 block = blocks[0] 166 167 # Double-check if this block is already attached by ID 168 if str(block.id) in current_block_ids: 169 results.append(f"{handle}: Already attached (by ID)") 170 continue 171 else: 172 block = client.blocks.create( 173 label=block_label, 174 value=f"# User: {handle}\n\nNo information about this user yet.", 175 limit=5000 176 ) 177 178 # Attach block atomically 179 try: 180 client.agents.blocks.attach( 181 agent_id=str(agent_state.id), 182 block_id=str(block.id) 183 ) 184 results.append(f"{handle}: Block attached") 185 except Exception as attach_error: 186 # Check if it's a duplicate constraint error 187 error_str = str(attach_error) 188 if "duplicate key value violates unique constraint" in error_str and "unique_label_per_agent" in error_str: 189 # Block is already attached, possibly with this exact label 190 results.append(f"{handle}: Already attached (verified)") 191 else: 192 # Re-raise other errors 193 raise attach_error 194 195 except Exception as e: 196 results.append(f"{handle}: Error - {str(e)}") 197 198 return f"Attachment results:\n" + "\n".join(results) 199 200 except Exception as e: 201 raise Exception(f"Error attaching user blocks: {str(e)}") 202 203 204def detach_user_blocks(handles: list, agent_state: "AgentState") -> str: 205 """ 206 Detach user-specific memory blocks from the agent. Blocks are preserved for later use. 207 208 Args: 209 handles: List of user Bluesky handles (e.g., ['user1.bsky.social', 'user2.bsky.social']) 210 agent_state: The agent state object containing agent information 211 212 Returns: 213 String with detachment results for each handle 214 """ 215 216 try: 217 # Try to get client the local way first, fall back to inline for cloud execution 218 try: 219 client = get_letta_client() 220 except (ImportError, NameError): 221 # Create Letta client inline for cloud execution 222 import os 223 from letta_client import Letta 224 client = Letta(token=os.environ["LETTA_API_KEY"]) 225 results = [] 226 227 # Build mapping of block labels to IDs using the API 228 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 229 block_label_to_id = {} 230 231 for block in current_blocks: 232 block_label_to_id[block.label] = str(block.id) 233 234 # Process each handle and detach atomically 235 for handle in handles: 236 # Sanitize handle for block label - completely self-contained 237 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 238 block_label = f"user_{clean_handle}" 239 240 if block_label in block_label_to_id: 241 try: 242 # Detach block atomically 243 client.agents.blocks.detach( 244 agent_id=str(agent_state.id), 245 block_id=block_label_to_id[block_label] 246 ) 247 results.append(f"{handle}: Detached") 248 except Exception as e: 249 results.append(f"{handle}: Error during detachment - {str(e)}") 250 else: 251 results.append(f"{handle}: Not attached") 252 253 return f"Detachment results:\n" + "\n".join(results) 254 255 except Exception as e: 256 raise Exception(f"Error detaching user blocks: {str(e)}") 257 258 259def user_note_append(handle: str, note: str, agent_state: "AgentState") -> str: 260 """ 261 Append a note to a user's memory block. Creates the block if it doesn't exist. 262 263 Args: 264 handle: User Bluesky handle (e.g., 'cameron.pfiffer.org') 265 note: Note to append to the user's memory block 266 agent_state: The agent state object containing agent information 267 268 Returns: 269 String confirming the note was appended 270 """ 271 272 try: 273 # Try to get X client first, fall back to inline for cloud execution 274 try: 275 client = get_x_letta_client() 276 except (ImportError, NameError): 277 # Create Letta client inline for cloud execution 278 import os 279 from letta_client import Letta 280 client = Letta(token=os.environ["LETTA_API_KEY"]) 281 282 # Sanitize handle for block label 283 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 284 block_label = f"user_{clean_handle}" 285 286 # Check if block exists 287 blocks = client.blocks.list(label=block_label) 288 289 if blocks and len(blocks) > 0: 290 # Block exists, append to it 291 block = blocks[0] 292 current_value = block.value 293 new_value = current_value + note 294 295 # Update the block 296 client.blocks.modify( 297 block_id=str(block.id), 298 value=new_value 299 ) 300 return f"✓ Appended note to {handle}'s memory block" 301 302 else: 303 # Block doesn't exist, create it with the note 304 initial_value = f"# User: {handle}\n\n{note}" 305 block = client.blocks.create( 306 label=block_label, 307 value=initial_value, 308 limit=5000 309 ) 310 311 # Check if block needs to be attached to agent 312 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 313 current_block_labels = {block.label for block in current_blocks} 314 315 if block_label not in current_block_labels: 316 # Attach the new block to the agent 317 client.agents.blocks.attach( 318 agent_id=str(agent_state.id), 319 block_id=str(block.id) 320 ) 321 return f"✓ Created and attached {handle}'s memory block with note" 322 else: 323 return f"✓ Created {handle}'s memory block with note" 324 325 except Exception as e: 326 raise Exception(f"Error appending note to user block: {str(e)}") 327 328 329def user_note_replace(handle: str, old_text: str, new_text: str, agent_state: "AgentState") -> str: 330 """ 331 Replace text in a user's memory block. 332 333 Args: 334 handle: User Bluesky handle (e.g., 'cameron.pfiffer.org') 335 old_text: Text to find and replace 336 new_text: Text to replace the old_text with 337 agent_state: The agent state object containing agent information 338 339 Returns: 340 String confirming the text was replaced 341 """ 342 343 try: 344 # Try to get X client first, fall back to inline for cloud execution 345 try: 346 client = get_x_letta_client() 347 except (ImportError, NameError): 348 # Create Letta client inline for cloud execution 349 import os 350 from letta_client import Letta 351 client = Letta(token=os.environ["LETTA_API_KEY"]) 352 353 # Sanitize handle for block label 354 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 355 block_label = f"user_{clean_handle}" 356 357 # Check if block exists 358 blocks = client.blocks.list(label=block_label) 359 360 if not blocks or len(blocks) == 0: 361 raise Exception(f"No memory block found for user: {handle}") 362 363 block = blocks[0] 364 current_value = block.value 365 366 # Check if old_text exists in the block 367 if old_text not in current_value: 368 raise Exception(f"Text '{old_text}' not found in {handle}'s memory block") 369 370 # Replace the text 371 new_value = current_value.replace(old_text, new_text) 372 373 # Update the block 374 client.blocks.modify( 375 block_id=str(block.id), 376 value=new_value 377 ) 378 return f"✓ Replaced text in {handle}'s memory block" 379 380 except Exception as e: 381 raise Exception(f"Error replacing text in user block: {str(e)}") 382 383 384def user_note_set(handle: str, content: str, agent_state: "AgentState") -> str: 385 """ 386 Set the complete content of a user's memory block. 387 388 Args: 389 handle: User Bluesky handle (e.g., 'cameron.pfiffer.org') 390 content: Complete content to set for the memory block 391 agent_state: The agent state object containing agent information 392 393 Returns: 394 String confirming the content was set 395 """ 396 397 try: 398 # Try to get X client first, fall back to inline for cloud execution 399 try: 400 client = get_x_letta_client() 401 except (ImportError, NameError): 402 # Create Letta client inline for cloud execution 403 import os 404 from letta_client import Letta 405 client = Letta(token=os.environ["LETTA_API_KEY"]) 406 407 # Sanitize handle for block label 408 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 409 block_label = f"user_{clean_handle}" 410 411 # Check if block exists 412 blocks = client.blocks.list(label=block_label) 413 414 if blocks and len(blocks) > 0: 415 # Block exists, update it 416 block = blocks[0] 417 client.blocks.modify( 418 block_id=str(block.id), 419 value=content 420 ) 421 return f"✓ Set content for {handle}'s memory block" 422 423 else: 424 # Block doesn't exist, create it 425 block = client.blocks.create( 426 label=block_label, 427 value=content, 428 limit=5000 429 ) 430 431 # Check if block needs to be attached to agent 432 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 433 current_block_labels = {block.label for block in current_blocks} 434 435 if block_label not in current_block_labels: 436 # Attach the new block to the agent 437 client.agents.blocks.attach( 438 agent_id=str(agent_state.id), 439 block_id=str(block.id) 440 ) 441 return f"✓ Created and attached {handle}'s memory block" 442 else: 443 return f"✓ Created {handle}'s memory block" 444 445 except Exception as e: 446 raise Exception(f"Error setting user block content: {str(e)}") 447 448 449def user_note_view(handle: str, agent_state: "AgentState") -> str: 450 """ 451 View the content of a user's memory block. 452 453 Args: 454 handle: User Bluesky handle (e.g., 'cameron.pfiffer.org') 455 agent_state: The agent state object containing agent information 456 457 Returns: 458 String containing the user's memory block content 459 """ 460 461 try: 462 # Try to get X client first, fall back to inline for cloud execution 463 try: 464 client = get_x_letta_client() 465 except (ImportError, NameError): 466 # Create Letta client inline for cloud execution 467 import os 468 from letta_client import Letta 469 client = Letta(token=os.environ["LETTA_API_KEY"]) 470 471 # Sanitize handle for block label 472 clean_handle = handle.lstrip('@').replace('.', '_').replace('-', '_').replace(' ', '_') 473 block_label = f"user_{clean_handle}" 474 475 # Check if block exists 476 blocks = client.blocks.list(label=block_label) 477 478 if not blocks or len(blocks) == 0: 479 return f"No memory block found for user: {handle}" 480 481 block = blocks[0] 482 483 return f"Memory block for {handle}:\n\n{block.value}" 484 485 except Exception as e: 486 raise Exception(f"Error viewing user block: {str(e)}") 487 488 489# X (Twitter) User Block Management Functions 490 491def attach_x_user_blocks(user_ids: list, agent_state: "AgentState") -> str: 492 """ 493 Attach X user-specific memory blocks to the agent. Creates blocks if they don't exist. 494 495 Args: 496 user_ids: List of X user IDs (e.g., ['1232326955652931584', '1950680610282094592']) 497 agent_state: The agent state object containing agent information 498 499 Returns: 500 String with attachment results for each user ID 501 """ 502 503 user_ids = list(set(user_ids)) 504 505 try: 506 # Try to get X client first, fall back to inline for cloud execution 507 try: 508 client = get_x_letta_client() 509 except (ImportError, NameError): 510 # Create Letta client inline for cloud execution 511 import os 512 from letta_client import Letta 513 client = Letta(token=os.environ["LETTA_API_KEY"]) 514 results = [] 515 516 # Get current blocks using the API 517 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 518 current_block_labels = set() 519 520 for block in current_blocks: 521 current_block_labels.add(block.label) 522 523 for user_id in user_ids: 524 # Create block label with x_user_ prefix 525 block_label = f"x_user_{user_id}" 526 527 # Skip if already attached 528 if block_label in current_block_labels: 529 results.append(f"{user_id}: Already attached") 530 continue 531 532 # Check if block exists or create new one 533 try: 534 blocks = client.blocks.list(label=block_label) 535 if blocks and len(blocks) > 0: 536 block = blocks[0] 537 else: 538 block = client.blocks.create( 539 label=block_label, 540 value=f"# X User: {user_id}\n\nNo information about this user yet.", 541 limit=5000 542 ) 543 544 # Attach block atomically 545 client.agents.blocks.attach( 546 agent_id=str(agent_state.id), 547 block_id=str(block.id) 548 ) 549 550 results.append(f"{user_id}: Block attached") 551 552 except Exception as e: 553 results.append(f"{user_id}: Error - {str(e)}") 554 555 return f"X user attachment results:\n" + "\n".join(results) 556 557 except Exception as e: 558 raise Exception(f"Error attaching X user blocks: {str(e)}") 559 560 561def detach_x_user_blocks(user_ids: list, agent_state: "AgentState") -> str: 562 """ 563 Detach X user-specific memory blocks from the agent. Blocks are preserved for later use. 564 565 Args: 566 user_ids: List of X user IDs (e.g., ['1232326955652931584', '1950680610282094592']) 567 agent_state: The agent state object containing agent information 568 569 Returns: 570 String with detachment results for each user ID 571 """ 572 573 try: 574 # Try to get X client first, fall back to inline for cloud execution 575 try: 576 client = get_x_letta_client() 577 except (ImportError, NameError): 578 # Create Letta client inline for cloud execution 579 import os 580 from letta_client import Letta 581 client = Letta(token=os.environ["LETTA_API_KEY"]) 582 results = [] 583 584 # Build mapping of block labels to IDs using the API 585 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 586 block_label_to_id = {} 587 588 for block in current_blocks: 589 block_label_to_id[block.label] = str(block.id) 590 591 # Process each user ID and detach atomically 592 for user_id in user_ids: 593 block_label = f"x_user_{user_id}" 594 595 if block_label in block_label_to_id: 596 try: 597 # Detach block atomically 598 client.agents.blocks.detach( 599 agent_id=str(agent_state.id), 600 block_id=block_label_to_id[block_label] 601 ) 602 results.append(f"{user_id}: Detached") 603 except Exception as e: 604 results.append(f"{user_id}: Error during detachment - {str(e)}") 605 else: 606 results.append(f"{user_id}: Not attached") 607 608 return f"X user detachment results:\n" + "\n".join(results) 609 610 except Exception as e: 611 raise Exception(f"Error detaching X user blocks: {str(e)}") 612 613 614def x_user_note_append(user_id: str, note: str, agent_state: "AgentState") -> str: 615 """ 616 Append a note to an X user's memory block. Creates the block if it doesn't exist. 617 618 Args: 619 user_id: X user ID (e.g., '1232326955652931584') 620 note: Note to append to the user's memory block 621 agent_state: The agent state object containing agent information 622 623 Returns: 624 String confirming the note was appended 625 """ 626 try: 627 # Try to get X client first, fall back to inline for cloud execution 628 try: 629 client = get_x_letta_client() 630 except (ImportError, NameError): 631 # Create Letta client inline for cloud execution 632 import os 633 from letta_client import Letta 634 client = Letta(token=os.environ["LETTA_API_KEY"]) 635 636 block_label = f"x_user_{user_id}" 637 638 # Check if block exists 639 blocks = client.blocks.list(label=block_label) 640 641 if blocks and len(blocks) > 0: 642 # Block exists, append to it 643 block = blocks[0] 644 current_value = block.value 645 new_value = current_value + note 646 647 # Update the block 648 client.blocks.modify( 649 block_id=str(block.id), 650 value=new_value 651 ) 652 return f"✓ Appended note to X user {user_id}'s memory block" 653 654 else: 655 # Block doesn't exist, create it with the note 656 initial_value = f"# X User: {user_id}\n\n{note}" 657 block = client.blocks.create( 658 label=block_label, 659 value=initial_value, 660 limit=5000 661 ) 662 663 # Check if block needs to be attached to agent 664 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 665 current_block_labels = {block.label for block in current_blocks} 666 667 if block_label not in current_block_labels: 668 # Attach the new block to the agent 669 client.agents.blocks.attach( 670 agent_id=str(agent_state.id), 671 block_id=str(block.id) 672 ) 673 return f"✓ Created and attached X user {user_id}'s memory block with note" 674 else: 675 return f"✓ Created X user {user_id}'s memory block with note" 676 677 except Exception as e: 678 raise Exception(f"Error appending note to X user block: {str(e)}") 679 680 681def x_user_note_replace(user_id: str, old_text: str, new_text: str, agent_state: "AgentState") -> str: 682 """ 683 Replace text in an X user's memory block. 684 685 Args: 686 user_id: X user ID (e.g., '1232326955652931584') 687 old_text: Text to find and replace 688 new_text: Text to replace the old_text with 689 agent_state: The agent state object containing agent information 690 691 Returns: 692 String confirming the text was replaced 693 """ 694 try: 695 # Try to get X client first, fall back to inline for cloud execution 696 try: 697 client = get_x_letta_client() 698 except (ImportError, NameError): 699 # Create Letta client inline for cloud execution 700 import os 701 from letta_client import Letta 702 client = Letta(token=os.environ["LETTA_API_KEY"]) 703 704 block_label = f"x_user_{user_id}" 705 706 # Check if block exists 707 blocks = client.blocks.list(label=block_label) 708 709 if not blocks or len(blocks) == 0: 710 raise Exception(f"No memory block found for X user: {user_id}") 711 712 block = blocks[0] 713 current_value = block.value 714 715 # Check if old_text exists in the block 716 if old_text not in current_value: 717 raise Exception(f"Text '{old_text}' not found in X user {user_id}'s memory block") 718 719 # Replace the text 720 new_value = current_value.replace(old_text, new_text) 721 722 # Update the block 723 client.blocks.modify( 724 block_id=str(block.id), 725 value=new_value 726 ) 727 return f"✓ Replaced text in X user {user_id}'s memory block" 728 729 except Exception as e: 730 raise Exception(f"Error replacing text in X user block: {str(e)}") 731 732 733def x_user_note_set(user_id: str, content: str, agent_state: "AgentState") -> str: 734 """ 735 Set the complete content of an X user's memory block. 736 737 Args: 738 user_id: X user ID (e.g., '1232326955652931584') 739 content: Complete content to set for the memory block 740 agent_state: The agent state object containing agent information 741 742 Returns: 743 String confirming the content was set 744 """ 745 try: 746 # Try to get X client first, fall back to inline for cloud execution 747 try: 748 client = get_x_letta_client() 749 except (ImportError, NameError): 750 # Create Letta client inline for cloud execution 751 import os 752 from letta_client import Letta 753 client = Letta(token=os.environ["LETTA_API_KEY"]) 754 755 block_label = f"x_user_{user_id}" 756 757 # Check if block exists 758 blocks = client.blocks.list(label=block_label) 759 760 if blocks and len(blocks) > 0: 761 # Block exists, update it 762 block = blocks[0] 763 client.blocks.modify( 764 block_id=str(block.id), 765 value=content 766 ) 767 return f"✓ Set content for X user {user_id}'s memory block" 768 769 else: 770 # Block doesn't exist, create it 771 block = client.blocks.create( 772 label=block_label, 773 value=content, 774 limit=5000 775 ) 776 777 # Check if block needs to be attached to agent 778 current_blocks = client.agents.blocks.list(agent_id=str(agent_state.id)) 779 current_block_labels = {block.label for block in current_blocks} 780 781 if block_label not in current_block_labels: 782 # Attach the new block to the agent 783 client.agents.blocks.attach( 784 agent_id=str(agent_state.id), 785 block_id=str(block.id) 786 ) 787 return f"✓ Created and attached X user {user_id}'s memory block" 788 else: 789 return f"✓ Created X user {user_id}'s memory block" 790 791 except Exception as e: 792 raise Exception(f"Error setting X user block content: {str(e)}") 793 794 795def x_user_note_view(user_id: str, agent_state: "AgentState") -> str: 796 """ 797 View the content of an X user's memory block. 798 799 Args: 800 user_id: X user ID (e.g., '1232326955652931584') 801 agent_state: The agent state object containing agent information 802 803 Returns: 804 String containing the user's memory block content 805 """ 806 try: 807 # Try to get X client first, fall back to inline for cloud execution 808 try: 809 client = get_x_letta_client() 810 except (ImportError, NameError): 811 # Create Letta client inline for cloud execution 812 import os 813 from letta_client import Letta 814 client = Letta(token=os.environ["LETTA_API_KEY"]) 815 816 block_label = f"x_user_{user_id}" 817 818 # Check if block exists 819 blocks = client.blocks.list(label=block_label) 820 821 if not blocks or len(blocks) == 0: 822 return f"No memory block found for X user: {user_id}" 823 824 block = blocks[0] 825 826 return f"Memory block for X user {user_id}:\n\n{block.value}" 827 828 except Exception as e: 829 raise Exception(f"Error viewing X user block: {str(e)}") 830 831