letta-nightly 0.6.4.dev20241213193437__py3-none-any.whl → 0.6.4.dev20241215104129__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

Files changed (62) hide show
  1. letta/__init__.py +1 -1
  2. letta/agent.py +54 -45
  3. letta/chat_only_agent.py +6 -8
  4. letta/cli/cli.py +2 -10
  5. letta/client/client.py +121 -138
  6. letta/config.py +0 -161
  7. letta/main.py +3 -8
  8. letta/memory.py +3 -14
  9. letta/o1_agent.py +1 -5
  10. letta/offline_memory_agent.py +2 -6
  11. letta/orm/__init__.py +2 -0
  12. letta/orm/agent.py +109 -0
  13. letta/orm/agents_tags.py +10 -18
  14. letta/orm/block.py +29 -4
  15. letta/orm/blocks_agents.py +5 -11
  16. letta/orm/custom_columns.py +152 -0
  17. letta/orm/message.py +3 -38
  18. letta/orm/organization.py +2 -7
  19. letta/orm/passage.py +10 -32
  20. letta/orm/source.py +5 -25
  21. letta/orm/sources_agents.py +13 -0
  22. letta/orm/sqlalchemy_base.py +54 -30
  23. letta/orm/tool.py +1 -19
  24. letta/orm/tools_agents.py +7 -24
  25. letta/orm/user.py +3 -4
  26. letta/schemas/agent.py +48 -65
  27. letta/schemas/memory.py +2 -1
  28. letta/schemas/sandbox_config.py +12 -1
  29. letta/server/rest_api/app.py +0 -5
  30. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +1 -1
  31. letta/server/rest_api/routers/v1/agents.py +99 -78
  32. letta/server/rest_api/routers/v1/blocks.py +22 -25
  33. letta/server/rest_api/routers/v1/jobs.py +4 -4
  34. letta/server/rest_api/routers/v1/sandbox_configs.py +10 -10
  35. letta/server/rest_api/routers/v1/sources.py +12 -12
  36. letta/server/rest_api/routers/v1/tools.py +35 -15
  37. letta/server/rest_api/routers/v1/users.py +0 -46
  38. letta/server/server.py +172 -716
  39. letta/server/ws_api/server.py +0 -5
  40. letta/services/agent_manager.py +405 -0
  41. letta/services/block_manager.py +13 -21
  42. letta/services/helpers/agent_manager_helper.py +90 -0
  43. letta/services/organization_manager.py +0 -1
  44. letta/services/passage_manager.py +62 -62
  45. letta/services/sandbox_config_manager.py +3 -3
  46. letta/services/source_manager.py +22 -1
  47. letta/services/user_manager.py +11 -6
  48. letta/utils.py +2 -2
  49. {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/METADATA +1 -1
  50. {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/RECORD +53 -57
  51. letta/metadata.py +0 -407
  52. letta/schemas/agents_tags.py +0 -33
  53. letta/schemas/api_key.py +0 -21
  54. letta/schemas/blocks_agents.py +0 -32
  55. letta/schemas/tools_agents.py +0 -32
  56. letta/server/rest_api/routers/openai/assistants/threads.py +0 -338
  57. letta/services/agents_tags_manager.py +0 -64
  58. letta/services/blocks_agents_manager.py +0 -106
  59. letta/services/tools_agents_manager.py +0 -94
  60. {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/LICENSE +0 -0
  61. {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/WHEEL +0 -0
  62. {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/entry_points.txt +0 -0
letta/client/client.py CHANGED
@@ -15,7 +15,8 @@ from letta.constants import (
15
15
  )
16
16
  from letta.data_sources.connectors import DataConnector
17
17
  from letta.functions.functions import parse_source_code
18
- from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgentState
18
+ from letta.orm.errors import NoResultFound
19
+ from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgent
19
20
  from letta.schemas.block import Block, BlockUpdate, CreateBlock, Human, Persona
20
21
  from letta.schemas.embedding_config import EmbeddingConfig
21
22
 
@@ -65,10 +66,8 @@ def create_client(base_url: Optional[str] = None, token: Optional[str] = None):
65
66
  class AbstractClient(object):
66
67
  def __init__(
67
68
  self,
68
- auto_save: bool = False,
69
69
  debug: bool = False,
70
70
  ):
71
- self.auto_save = auto_save
72
71
  self.debug = debug
73
72
 
74
73
  def agent_exists(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> bool:
@@ -81,8 +80,9 @@ class AbstractClient(object):
81
80
  embedding_config: Optional[EmbeddingConfig] = None,
82
81
  llm_config: Optional[LLMConfig] = None,
83
82
  memory=None,
83
+ block_ids: Optional[List[str]] = None,
84
84
  system: Optional[str] = None,
85
- tools: Optional[List[str]] = None,
85
+ tool_ids: Optional[List[str]] = None,
86
86
  tool_rules: Optional[List[BaseToolRule]] = None,
87
87
  include_base_tools: Optional[bool] = True,
88
88
  metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
@@ -97,7 +97,7 @@ class AbstractClient(object):
97
97
  name: Optional[str] = None,
98
98
  description: Optional[str] = None,
99
99
  system: Optional[str] = None,
100
- tools: Optional[List[str]] = None,
100
+ tool_ids: Optional[List[str]] = None,
101
101
  metadata: Optional[Dict] = None,
102
102
  llm_config: Optional[LLMConfig] = None,
103
103
  embedding_config: Optional[EmbeddingConfig] = None,
@@ -436,7 +436,6 @@ class RESTClient(AbstractClient):
436
436
  Initializes a new instance of Client class.
437
437
 
438
438
  Args:
439
- auto_save (bool): Whether to automatically save changes.
440
439
  user_id (str): The user ID.
441
440
  debug (bool): Whether to print debug information.
442
441
  default_llm_config (Optional[LLMConfig]): The default LLM configuration.
@@ -456,6 +455,7 @@ class RESTClient(AbstractClient):
456
455
  params = {}
457
456
  if tags:
458
457
  params["tags"] = tags
458
+ params["match_all_tags"] = False
459
459
 
460
460
  response = requests.get(f"{self.base_url}/{self.api_prefix}/agents", headers=self.headers, params=params)
461
461
  return [AgentState(**agent) for agent in response.json()]
@@ -491,10 +491,12 @@ class RESTClient(AbstractClient):
491
491
  llm_config: LLMConfig = None,
492
492
  # memory
493
493
  memory: Memory = ChatMemory(human=get_human_text(DEFAULT_HUMAN), persona=get_persona_text(DEFAULT_PERSONA)),
494
+ # Existing blocks
495
+ block_ids: Optional[List[str]] = None,
494
496
  # system
495
497
  system: Optional[str] = None,
496
498
  # tools
497
- tools: Optional[List[str]] = None,
499
+ tool_ids: Optional[List[str]] = None,
498
500
  tool_rules: Optional[List[BaseToolRule]] = None,
499
501
  include_base_tools: Optional[bool] = True,
500
502
  # metadata
@@ -511,7 +513,7 @@ class RESTClient(AbstractClient):
511
513
  llm_config (LLMConfig): LLM configuration
512
514
  memory (Memory): Memory configuration
513
515
  system (str): System configuration
514
- tools (List[str]): List of tools
516
+ tool_ids (List[str]): List of tool ids
515
517
  include_base_tools (bool): Include base tools
516
518
  metadata (Dict): Metadata
517
519
  description (str): Description
@@ -520,31 +522,54 @@ class RESTClient(AbstractClient):
520
522
  Returns:
521
523
  agent_state (AgentState): State of the created agent
522
524
  """
525
+ tool_ids = tool_ids or []
523
526
  tool_names = []
524
- if tools:
525
- tool_names += tools
526
527
  if include_base_tools:
527
528
  tool_names += BASE_TOOLS
528
529
  tool_names += BASE_MEMORY_TOOLS
530
+ tool_ids += [self.get_tool_id(tool_name=name) for name in tool_names]
529
531
 
530
532
  assert embedding_config or self._default_embedding_config, f"Embedding config must be provided"
531
533
  assert llm_config or self._default_llm_config, f"LLM config must be provided"
532
534
 
535
+ # TODO: This should not happen here, we need to have clear separation between create/add blocks
536
+ # TODO: This is insanely hacky and a result of allowing free-floating blocks
537
+ # TODO: When we create the block, it gets it's own block ID
538
+ blocks = []
539
+ for block in memory.get_blocks():
540
+ blocks.append(
541
+ self.create_block(
542
+ label=block.label,
543
+ value=block.value,
544
+ limit=block.limit,
545
+ template_name=block.template_name,
546
+ is_template=block.is_template,
547
+ )
548
+ )
549
+ memory.blocks = blocks
550
+ block_ids = block_ids or []
551
+
533
552
  # create agent
534
- request = CreateAgent(
535
- name=name,
536
- description=description,
537
- metadata_=metadata,
538
- memory_blocks=[],
539
- tools=tool_names,
540
- tool_rules=tool_rules,
541
- system=system,
542
- agent_type=agent_type,
543
- llm_config=llm_config if llm_config else self._default_llm_config,
544
- embedding_config=embedding_config if embedding_config else self._default_embedding_config,
545
- initial_message_sequence=initial_message_sequence,
546
- tags=tags,
547
- )
553
+ create_params = {
554
+ "description": description,
555
+ "metadata_": metadata,
556
+ "memory_blocks": [],
557
+ "block_ids": [b.id for b in memory.get_blocks()] + block_ids,
558
+ "tool_ids": tool_ids,
559
+ "tool_rules": tool_rules,
560
+ "system": system,
561
+ "agent_type": agent_type,
562
+ "llm_config": llm_config if llm_config else self._default_llm_config,
563
+ "embedding_config": embedding_config if embedding_config else self._default_embedding_config,
564
+ "initial_message_sequence": initial_message_sequence,
565
+ "tags": tags,
566
+ }
567
+
568
+ # Only add name if it's not None
569
+ if name is not None:
570
+ create_params["name"] = name
571
+
572
+ request = CreateAgent(**create_params)
548
573
 
549
574
  # Use model_dump_json() instead of model_dump()
550
575
  # If we use model_dump(), the datetime objects will not be serialized correctly
@@ -561,14 +586,6 @@ class RESTClient(AbstractClient):
561
586
  # gather agent state
562
587
  agent_state = AgentState(**response.json())
563
588
 
564
- # create and link blocks
565
- for block in memory.get_blocks():
566
- if not self.get_block(block.id):
567
- # note: this does not update existing blocks
568
- # WARNING: this resets the block ID - this method is a hack for backwards compat, should eventually use CreateBlock not Memory
569
- block = self.create_block(label=block.label, value=block.value, limit=block.limit)
570
- self.link_agent_memory_block(agent_id=agent_state.id, block_id=block.id)
571
-
572
589
  # refresh and return agent
573
590
  return self.get_agent(agent_state.id)
574
591
 
@@ -602,7 +619,7 @@ class RESTClient(AbstractClient):
602
619
  name: Optional[str] = None,
603
620
  description: Optional[str] = None,
604
621
  system: Optional[str] = None,
605
- tool_names: Optional[List[str]] = None,
622
+ tool_ids: Optional[List[str]] = None,
606
623
  metadata: Optional[Dict] = None,
607
624
  llm_config: Optional[LLMConfig] = None,
608
625
  embedding_config: Optional[EmbeddingConfig] = None,
@@ -617,7 +634,7 @@ class RESTClient(AbstractClient):
617
634
  name (str): Name of the agent
618
635
  description (str): Description of the agent
619
636
  system (str): System configuration
620
- tool_names (List[str]): List of tools
637
+ tool_ids (List[str]): List of tools
621
638
  metadata (Dict): Metadata
622
639
  llm_config (LLMConfig): LLM configuration
623
640
  embedding_config (EmbeddingConfig): Embedding configuration
@@ -627,11 +644,10 @@ class RESTClient(AbstractClient):
627
644
  Returns:
628
645
  agent_state (AgentState): State of the updated agent
629
646
  """
630
- request = UpdateAgentState(
631
- id=agent_id,
647
+ request = UpdateAgent(
632
648
  name=name,
633
649
  system=system,
634
- tool_names=tool_names,
650
+ tool_ids=tool_ids,
635
651
  tags=tags,
636
652
  description=description,
637
653
  metadata_=metadata,
@@ -742,7 +758,7 @@ class RESTClient(AbstractClient):
742
758
  agents = [AgentState(**agent) for agent in response.json()]
743
759
  if len(agents) == 0:
744
760
  return None
745
- agents = [agents[0]] # TODO: @matt monkeypatched
761
+ agents = [agents[0]] # TODO: @matt monkeypatched
746
762
  assert len(agents) == 1, f"Multiple agents with the same name: {[(agents.name, agents.id) for agents in agents]}"
747
763
  return agents[0].id
748
764
 
@@ -1052,7 +1068,7 @@ class RESTClient(AbstractClient):
1052
1068
  raise ValueError(f"Failed to update block: {response.text}")
1053
1069
  return Block(**response.json())
1054
1070
 
1055
- def get_block(self, block_id: str) -> Block:
1071
+ def get_block(self, block_id: str) -> Optional[Block]:
1056
1072
  response = requests.get(f"{self.base_url}/{self.api_prefix}/blocks/{block_id}", headers=self.headers)
1057
1073
  if response.status_code == 404:
1058
1074
  return None
@@ -1607,23 +1623,6 @@ class RESTClient(AbstractClient):
1607
1623
  raise ValueError(f"Failed to get tool: {response.text}")
1608
1624
  return Tool(**response.json())
1609
1625
 
1610
- def get_tool_id(self, name: str) -> Optional[str]:
1611
- """
1612
- Get a tool ID by its name.
1613
-
1614
- Args:
1615
- id (str): ID of the tool
1616
-
1617
- Returns:
1618
- tool (Tool): Tool
1619
- """
1620
- response = requests.get(f"{self.base_url}/{self.api_prefix}/tools/name/{name}", headers=self.headers)
1621
- if response.status_code == 404:
1622
- return None
1623
- elif response.status_code != 200:
1624
- raise ValueError(f"Failed to get tool: {response.text}")
1625
- return response.json()
1626
-
1627
1626
  def set_default_llm_config(self, llm_config: LLMConfig):
1628
1627
  """
1629
1628
  Set the default LLM configuration
@@ -2006,7 +2005,6 @@ class LocalClient(AbstractClient):
2006
2005
  A local client for Letta, which corresponds to a single user.
2007
2006
 
2008
2007
  Attributes:
2009
- auto_save (bool): Whether to automatically save changes.
2010
2008
  user_id (str): The user ID.
2011
2009
  debug (bool): Whether to print debug information.
2012
2010
  interface (QueuingInterface): The interface for the client.
@@ -2015,7 +2013,6 @@ class LocalClient(AbstractClient):
2015
2013
 
2016
2014
  def __init__(
2017
2015
  self,
2018
- auto_save: bool = False,
2019
2016
  user_id: Optional[str] = None,
2020
2017
  org_id: Optional[str] = None,
2021
2018
  debug: bool = False,
@@ -2026,11 +2023,9 @@ class LocalClient(AbstractClient):
2026
2023
  Initializes a new instance of Client class.
2027
2024
 
2028
2025
  Args:
2029
- auto_save (bool): Whether to automatically save changes.
2030
2026
  user_id (str): The user ID.
2031
2027
  debug (bool): Whether to print debug information.
2032
2028
  """
2033
- self.auto_save = auto_save
2034
2029
 
2035
2030
  # set logging levels
2036
2031
  letta.utils.DEBUG = debug
@@ -2056,14 +2051,14 @@ class LocalClient(AbstractClient):
2056
2051
  # get default user
2057
2052
  self.user_id = self.server.user_manager.DEFAULT_USER_ID
2058
2053
 
2059
- self.user = self.server.get_user_or_default(self.user_id)
2054
+ self.user = self.server.user_manager.get_user_or_default(self.user_id)
2060
2055
  self.organization = self.server.get_organization_or_default(self.org_id)
2061
2056
 
2062
2057
  # agents
2063
2058
  def list_agents(self, tags: Optional[List[str]] = None) -> List[AgentState]:
2064
2059
  self.interface.clear()
2065
2060
 
2066
- return self.server.list_agents(user_id=self.user_id, tags=tags)
2061
+ return self.server.agent_manager.list_agents(actor=self.user, tags=tags)
2067
2062
 
2068
2063
  def agent_exists(self, agent_id: Optional[str] = None, agent_name: Optional[str] = None) -> bool:
2069
2064
  """
@@ -2097,6 +2092,7 @@ class LocalClient(AbstractClient):
2097
2092
  llm_config: LLMConfig = None,
2098
2093
  # memory
2099
2094
  memory: Memory = ChatMemory(human=get_human_text(DEFAULT_HUMAN), persona=get_persona_text(DEFAULT_PERSONA)),
2095
+ block_ids: Optional[List[str]] = None,
2100
2096
  # TODO: change to this when we are ready to migrate all the tests/examples (matches the REST API)
2101
2097
  # memory_blocks=[
2102
2098
  # {"label": "human", "value": get_human_text(DEFAULT_HUMAN), "limit": 5000},
@@ -2105,7 +2101,7 @@ class LocalClient(AbstractClient):
2105
2101
  # system
2106
2102
  system: Optional[str] = None,
2107
2103
  # tools
2108
- tools: Optional[List[str]] = None,
2104
+ tool_ids: Optional[List[str]] = None,
2109
2105
  tool_rules: Optional[List[BaseToolRule]] = None,
2110
2106
  include_base_tools: Optional[bool] = True,
2111
2107
  # metadata
@@ -2132,55 +2128,53 @@ class LocalClient(AbstractClient):
2132
2128
  Returns:
2133
2129
  agent_state (AgentState): State of the created agent
2134
2130
  """
2135
-
2136
- if name and self.agent_exists(agent_name=name):
2137
- raise ValueError(f"Agent with name {name} already exists (user_id={self.user_id})")
2138
-
2139
2131
  # construct list of tools
2132
+ tool_ids = tool_ids or []
2140
2133
  tool_names = []
2141
- if tools:
2142
- tool_names += tools
2143
2134
  if include_base_tools:
2144
2135
  tool_names += BASE_TOOLS
2145
2136
  tool_names += BASE_MEMORY_TOOLS
2137
+ tool_ids += [self.server.tool_manager.get_tool_by_name(tool_name=name, actor=self.user).id for name in tool_names]
2146
2138
 
2147
2139
  # check if default configs are provided
2148
2140
  assert embedding_config or self._default_embedding_config, f"Embedding config must be provided"
2149
2141
  assert llm_config or self._default_llm_config, f"LLM config must be provided"
2150
2142
 
2143
+ # TODO: This should not happen here, we need to have clear separation between create/add blocks
2144
+ for block in memory.get_blocks():
2145
+ self.server.block_manager.create_or_update_block(block, actor=self.user)
2146
+
2147
+ # Also get any existing block_ids passed in
2148
+ block_ids = block_ids or []
2149
+
2151
2150
  # create agent
2151
+ # Create the base parameters
2152
+ create_params = {
2153
+ "description": description,
2154
+ "metadata_": metadata,
2155
+ "memory_blocks": [],
2156
+ "block_ids": [b.id for b in memory.get_blocks()] + block_ids,
2157
+ "tool_ids": tool_ids,
2158
+ "tool_rules": tool_rules,
2159
+ "system": system,
2160
+ "agent_type": agent_type,
2161
+ "llm_config": llm_config if llm_config else self._default_llm_config,
2162
+ "embedding_config": embedding_config if embedding_config else self._default_embedding_config,
2163
+ "initial_message_sequence": initial_message_sequence,
2164
+ "tags": tags,
2165
+ }
2166
+
2167
+ # Only add name if it's not None
2168
+ if name is not None:
2169
+ create_params["name"] = name
2170
+
2152
2171
  agent_state = self.server.create_agent(
2153
- CreateAgent(
2154
- name=name,
2155
- description=description,
2156
- metadata_=metadata,
2157
- # memory=memory,
2158
- memory_blocks=[],
2159
- # memory_blocks = memory.get_blocks(),
2160
- # memory_tools=memory_tools,
2161
- tools=tool_names,
2162
- tool_rules=tool_rules,
2163
- system=system,
2164
- agent_type=agent_type,
2165
- llm_config=llm_config if llm_config else self._default_llm_config,
2166
- embedding_config=embedding_config if embedding_config else self._default_embedding_config,
2167
- initial_message_sequence=initial_message_sequence,
2168
- tags=tags,
2169
- ),
2172
+ CreateAgent(**create_params),
2170
2173
  actor=self.user,
2171
2174
  )
2172
2175
 
2173
- # TODO: remove when we fully migrate to block creation CreateAgent model
2174
- # Link additional blocks to the agent (block ids created on the client)
2175
- # This needs to happen since the create agent does not allow passing in blocks which have already been persisted and have an ID
2176
- # So we create the agent and then link the blocks afterwards
2177
- user = self.server.get_user_or_default(self.user_id)
2178
- for block in memory.get_blocks():
2179
- self.server.block_manager.create_or_update_block(block, actor=user)
2180
- self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_state.id, block_id=block.id)
2181
-
2182
2176
  # TODO: get full agent state
2183
- return self.server.get_agent(agent_state.id)
2177
+ return self.server.agent_manager.get_agent_by_id(agent_state.id, actor=self.user)
2184
2178
 
2185
2179
  def update_message(
2186
2180
  self,
@@ -2202,6 +2196,7 @@ class LocalClient(AbstractClient):
2202
2196
  tool_calls=tool_calls,
2203
2197
  tool_call_id=tool_call_id,
2204
2198
  ),
2199
+ actor=self.user,
2205
2200
  )
2206
2201
  return message
2207
2202
 
@@ -2211,7 +2206,7 @@ class LocalClient(AbstractClient):
2211
2206
  name: Optional[str] = None,
2212
2207
  description: Optional[str] = None,
2213
2208
  system: Optional[str] = None,
2214
- tools: Optional[List[str]] = None,
2209
+ tool_ids: Optional[List[str]] = None,
2215
2210
  tags: Optional[List[str]] = None,
2216
2211
  metadata: Optional[Dict] = None,
2217
2212
  llm_config: Optional[LLMConfig] = None,
@@ -2239,11 +2234,11 @@ class LocalClient(AbstractClient):
2239
2234
  # TODO: add the abilitty to reset linked block_ids
2240
2235
  self.interface.clear()
2241
2236
  agent_state = self.server.update_agent(
2242
- UpdateAgentState(
2243
- id=agent_id,
2237
+ agent_id,
2238
+ UpdateAgent(
2244
2239
  name=name,
2245
2240
  system=system,
2246
- tool_names=tools,
2241
+ tool_ids=tool_ids,
2247
2242
  tags=tags,
2248
2243
  description=description,
2249
2244
  metadata_=metadata,
@@ -2315,7 +2310,7 @@ class LocalClient(AbstractClient):
2315
2310
  Args:
2316
2311
  agent_id (str): ID of the agent to delete
2317
2312
  """
2318
- self.server.delete_agent(user_id=self.user_id, agent_id=agent_id)
2313
+ self.server.agent_manager.delete_agent(agent_id=agent_id, actor=self.user)
2319
2314
 
2320
2315
  def get_agent_by_name(self, agent_name: str) -> AgentState:
2321
2316
  """
@@ -2328,7 +2323,7 @@ class LocalClient(AbstractClient):
2328
2323
  agent_state (AgentState): State of the agent
2329
2324
  """
2330
2325
  self.interface.clear()
2331
- return self.server.get_agent_state(agent_name=agent_name, user_id=self.user_id, agent_id=None)
2326
+ return self.server.agent_manager.get_agent_by_name(agent_name=agent_name, actor=self.user)
2332
2327
 
2333
2328
  def get_agent(self, agent_id: str) -> AgentState:
2334
2329
  """
@@ -2340,9 +2335,8 @@ class LocalClient(AbstractClient):
2340
2335
  Returns:
2341
2336
  agent_state (AgentState): State representation of the agent
2342
2337
  """
2343
- # TODO: include agent_name
2344
2338
  self.interface.clear()
2345
- return self.server.get_agent_state(user_id=self.user_id, agent_id=agent_id)
2339
+ return self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user)
2346
2340
 
2347
2341
  def get_agent_id(self, agent_name: str) -> Optional[str]:
2348
2342
  """
@@ -2357,7 +2351,12 @@ class LocalClient(AbstractClient):
2357
2351
 
2358
2352
  self.interface.clear()
2359
2353
  assert agent_name, f"Agent name must be provided"
2360
- return self.server.get_agent_id(name=agent_name, user_id=self.user_id)
2354
+
2355
+ # TODO: Refactor this futher to not have downstream users expect Optionals - this should just error
2356
+ try:
2357
+ return self.server.agent_manager.get_agent_by_name(agent_name=agent_name, actor=self.user).id
2358
+ except NoResultFound:
2359
+ return None
2361
2360
 
2362
2361
  # memory
2363
2362
  def get_in_context_memory(self, agent_id: str) -> Memory:
@@ -2370,7 +2369,7 @@ class LocalClient(AbstractClient):
2370
2369
  Returns:
2371
2370
  memory (Memory): In-context memory of the agent
2372
2371
  """
2373
- memory = self.server.get_agent_memory(agent_id=agent_id)
2372
+ memory = self.server.get_agent_memory(agent_id=agent_id, actor=self.user)
2374
2373
  return memory
2375
2374
 
2376
2375
  def get_core_memory(self, agent_id: str) -> Memory:
@@ -2388,7 +2387,7 @@ class LocalClient(AbstractClient):
2388
2387
 
2389
2388
  """
2390
2389
  # TODO: implement this (not sure what it should look like)
2391
- memory = self.server.update_agent_core_memory(user_id=self.user_id, agent_id=agent_id, label=section, value=value)
2390
+ memory = self.server.update_agent_core_memory(agent_id=agent_id, label=section, value=value, actor=self.user)
2392
2391
  return memory
2393
2392
 
2394
2393
  def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
@@ -2402,7 +2401,7 @@ class LocalClient(AbstractClient):
2402
2401
  summary (ArchivalMemorySummary): Summary of the archival memory
2403
2402
 
2404
2403
  """
2405
- return self.server.get_archival_memory_summary(agent_id=agent_id)
2404
+ return self.server.get_archival_memory_summary(agent_id=agent_id, actor=self.user)
2406
2405
 
2407
2406
  def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
2408
2407
  """
@@ -2414,7 +2413,7 @@ class LocalClient(AbstractClient):
2414
2413
  Returns:
2415
2414
  summary (RecallMemorySummary): Summary of the recall memory
2416
2415
  """
2417
- return self.server.get_recall_memory_summary(agent_id=agent_id)
2416
+ return self.server.get_recall_memory_summary(agent_id=agent_id, actor=self.user)
2418
2417
 
2419
2418
  def get_in_context_messages(self, agent_id: str) -> List[Message]:
2420
2419
  """
@@ -2426,7 +2425,7 @@ class LocalClient(AbstractClient):
2426
2425
  Returns:
2427
2426
  messages (List[Message]): List of in-context messages
2428
2427
  """
2429
- return self.server.get_in_context_messages(agent_id=agent_id)
2428
+ return self.server.get_in_context_messages(agent_id=agent_id, actor=self.user)
2430
2429
 
2431
2430
  # agent interactions
2432
2431
 
@@ -2446,11 +2445,7 @@ class LocalClient(AbstractClient):
2446
2445
  response (LettaResponse): Response from the agent
2447
2446
  """
2448
2447
  self.interface.clear()
2449
- usage = self.server.send_messages(user_id=self.user_id, agent_id=agent_id, messages=messages)
2450
-
2451
- # auto-save
2452
- if self.auto_save:
2453
- self.save()
2448
+ usage = self.server.send_messages(actor=self.user, agent_id=agent_id, messages=messages)
2454
2449
 
2455
2450
  # format messages
2456
2451
  return LettaResponse(messages=messages, usage=usage)
@@ -2490,15 +2485,11 @@ class LocalClient(AbstractClient):
2490
2485
  self.interface.clear()
2491
2486
 
2492
2487
  usage = self.server.send_messages(
2493
- user_id=self.user_id,
2488
+ actor=self.user,
2494
2489
  agent_id=agent_id,
2495
2490
  messages=[MessageCreate(role=MessageRole(role), text=message, name=name)],
2496
2491
  )
2497
2492
 
2498
- # auto-save
2499
- if self.auto_save:
2500
- self.save()
2501
-
2502
2493
  ## TODO: need to make sure date/timestamp is propely passed
2503
2494
  ## TODO: update self.interface.to_list() to return actual Message objects
2504
2495
  ## here, the message objects will have faulty created_by timestamps
@@ -2547,16 +2538,9 @@ class LocalClient(AbstractClient):
2547
2538
  self.interface.clear()
2548
2539
  usage = self.server.run_command(user_id=self.user_id, agent_id=agent_id, command=command)
2549
2540
 
2550
- # auto-save
2551
- if self.auto_save:
2552
- self.save()
2553
-
2554
2541
  # NOTE: messages/usage may be empty, depending on the command
2555
2542
  return LettaResponse(messages=self.interface.to_list(), usage=usage)
2556
2543
 
2557
- def save(self):
2558
- self.server.save_agents()
2559
-
2560
2544
  # archival memory
2561
2545
 
2562
2546
  # humans / personas
@@ -3036,7 +3020,7 @@ class LocalClient(AbstractClient):
3036
3020
  Returns:
3037
3021
  sources (List[Source]): List of sources
3038
3022
  """
3039
- return self.server.list_attached_sources(agent_id=agent_id)
3023
+ return self.server.agent_manager.list_attached_sources(agent_id=agent_id, actor=self.user)
3040
3024
 
3041
3025
  def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
3042
3026
  """
@@ -3080,7 +3064,7 @@ class LocalClient(AbstractClient):
3080
3064
  Returns:
3081
3065
  passages (List[Passage]): List of inserted passages
3082
3066
  """
3083
- return self.server.insert_archival_memory(user_id=self.user_id, agent_id=agent_id, memory_contents=memory)
3067
+ return self.server.insert_archival_memory(agent_id=agent_id, memory_contents=memory, actor=self.user)
3084
3068
 
3085
3069
  def delete_archival_memory(self, agent_id: str, memory_id: str):
3086
3070
  """
@@ -3090,7 +3074,7 @@ class LocalClient(AbstractClient):
3090
3074
  agent_id (str): ID of the agent
3091
3075
  memory_id (str): ID of the memory
3092
3076
  """
3093
- self.server.delete_archival_memory(user_id=self.user_id, agent_id=agent_id, memory_id=memory_id)
3077
+ self.server.delete_archival_memory(agent_id=agent_id, memory_id=memory_id, actor=self.user)
3094
3078
 
3095
3079
  def get_archival_memory(
3096
3080
  self, agent_id: str, before: Optional[str] = None, after: Optional[str] = None, limit: Optional[int] = 1000
@@ -3349,8 +3333,8 @@ class LocalClient(AbstractClient):
3349
3333
  block_req = Block(**create_block.model_dump())
3350
3334
  block = self.server.block_manager.create_or_update_block(actor=self.user, block=block_req)
3351
3335
  # Link the block to the agent
3352
- updated_memory = self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_id, block_id=block.id)
3353
- return updated_memory
3336
+ agent = self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block.id, actor=self.user)
3337
+ return agent.memory
3354
3338
 
3355
3339
  def link_agent_memory_block(self, agent_id: str, block_id: str) -> Memory:
3356
3340
  """
@@ -3363,7 +3347,7 @@ class LocalClient(AbstractClient):
3363
3347
  Returns:
3364
3348
  memory (Memory): The updated memory
3365
3349
  """
3366
- return self.server.link_block_to_agent_memory(user_id=self.user_id, agent_id=agent_id, block_id=block_id)
3350
+ return self.server.agent_manager.attach_block(agent_id=agent_id, block_id=block_id, actor=self.user)
3367
3351
 
3368
3352
  def remove_agent_memory_block(self, agent_id: str, block_label: str) -> Memory:
3369
3353
  """
@@ -3376,7 +3360,7 @@ class LocalClient(AbstractClient):
3376
3360
  Returns:
3377
3361
  memory (Memory): The updated memory
3378
3362
  """
3379
- return self.server.unlink_block_from_agent_memory(user_id=self.user_id, agent_id=agent_id, block_label=block_label)
3363
+ return self.server.agent_manager.detach_block_with_label(agent_id=agent_id, block_label=block_label, actor=self.user)
3380
3364
 
3381
3365
  def get_agent_memory_blocks(self, agent_id: str) -> List[Block]:
3382
3366
  """
@@ -3388,8 +3372,8 @@ class LocalClient(AbstractClient):
3388
3372
  Returns:
3389
3373
  blocks (List[Block]): The blocks in the agent's core memory
3390
3374
  """
3391
- block_ids = self.server.blocks_agents_manager.list_block_ids_for_agent(agent_id=agent_id)
3392
- return [self.server.block_manager.get_block_by_id(block_id, actor=self.user) for block_id in block_ids]
3375
+ agent = self.server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=self.user)
3376
+ return agent.memory.blocks
3393
3377
 
3394
3378
  def get_agent_memory_block(self, agent_id: str, label: str) -> Block:
3395
3379
  """
@@ -3402,8 +3386,7 @@ class LocalClient(AbstractClient):
3402
3386
  Returns:
3403
3387
  block (Block): The block corresponding to the label
3404
3388
  """
3405
- block_id = self.server.blocks_agents_manager.get_block_id_for_label(agent_id=agent_id, block_label=label)
3406
- return self.server.block_manager.get_block_by_id(block_id, actor=self.user)
3389
+ return self.server.agent_manager.get_block_with_label(agent_id=agent_id, block_label=label, actor=self.user)
3407
3390
 
3408
3391
  def update_agent_memory_block(
3409
3392
  self,