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