letta-nightly 0.5.1.dev20241105104128__py3-none-any.whl → 0.5.1.dev20241106104104__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.

letta/agent.py CHANGED
@@ -3,6 +3,7 @@ import inspect
3
3
  import traceback
4
4
  import warnings
5
5
  from abc import ABC, abstractmethod
6
+ from lib2to3.fixer_util import is_list
6
7
  from typing import List, Literal, Optional, Tuple, Union
7
8
 
8
9
  from tqdm import tqdm
@@ -250,6 +251,9 @@ class Agent(BaseAgent):
250
251
  # if there are tool rules, print out a warning
251
252
  warnings.warn("Tool rules only work reliably for the latest OpenAI models that support structured outputs.")
252
253
  # add default rule for having send_message be a terminal tool
254
+
255
+ if not is_list(agent_state.tool_rules):
256
+ agent_state.tool_rules = []
253
257
  agent_state.tool_rules.append(TerminalToolRule(tool_name="send_message"))
254
258
  self.tool_rules_solver = ToolRulesSolver(tool_rules=agent_state.tool_rules)
255
259
 
letta/agent_store/db.py CHANGED
@@ -358,26 +358,26 @@ class PostgresStorageConnector(SQLStorageConnector):
358
358
  # construct URI from enviornment variables
359
359
  if settings.pg_uri:
360
360
  self.uri = settings.pg_uri
361
+
362
+ # use config URI
363
+ # TODO: remove this eventually (config should NOT contain URI)
364
+ if table_type == TableType.ARCHIVAL_MEMORY or table_type == TableType.PASSAGES:
365
+ self.uri = self.config.archival_storage_uri
366
+ self.db_model = PassageModel
367
+ if self.config.archival_storage_uri is None:
368
+ raise ValueError(f"Must specify archival_storage_uri in config {self.config.config_path}")
369
+ elif table_type == TableType.RECALL_MEMORY:
370
+ self.uri = self.config.recall_storage_uri
371
+ self.db_model = MessageModel
372
+ if self.config.recall_storage_uri is None:
373
+ raise ValueError(f"Must specify recall_storage_uri in config {self.config.config_path}")
374
+ elif table_type == TableType.FILES:
375
+ self.uri = self.config.metadata_storage_uri
376
+ self.db_model = FileMetadataModel
377
+ if self.config.metadata_storage_uri is None:
378
+ raise ValueError(f"Must specify metadata_storage_uri in config {self.config.config_path}")
361
379
  else:
362
- # use config URI
363
- # TODO: remove this eventually (config should NOT contain URI)
364
- if table_type == TableType.ARCHIVAL_MEMORY or table_type == TableType.PASSAGES:
365
- self.uri = self.config.archival_storage_uri
366
- self.db_model = PassageModel
367
- if self.config.archival_storage_uri is None:
368
- raise ValueError(f"Must specify archival_storage_uri in config {self.config.config_path}")
369
- elif table_type == TableType.RECALL_MEMORY:
370
- self.uri = self.config.recall_storage_uri
371
- self.db_model = MessageModel
372
- if self.config.recall_storage_uri is None:
373
- raise ValueError(f"Must specify recall_storage_uri in config {self.config.config_path}")
374
- elif table_type == TableType.FILES:
375
- self.uri = self.config.metadata_storage_uri
376
- self.db_model = FileMetadataModel
377
- if self.config.metadata_storage_uri is None:
378
- raise ValueError(f"Must specify metadata_storage_uri in config {self.config.config_path}")
379
- else:
380
- raise ValueError(f"Table type {table_type} not implemented")
380
+ raise ValueError(f"Table type {table_type} not implemented")
381
381
 
382
382
  for c in self.db_model.__table__.columns:
383
383
  if c.name == "embedding":
letta/client/client.py CHANGED
@@ -612,7 +612,12 @@ class RESTClient(AbstractClient):
612
612
  agent_id (str): ID of the agent
613
613
  """
614
614
  # TODO: implement this
615
- raise NotImplementedError
615
+ response = requests.get(f"{self.base_url}/{self.api_prefix}/agents", headers=self.headers, params={"name": agent_name})
616
+ agents = [AgentState(**agent) for agent in response.json()]
617
+ if len(agents) == 0:
618
+ return None
619
+ assert len(agents) == 1, f"Multiple agents with the same name: {agents}"
620
+ return agents[0].id
616
621
 
617
622
  # memory
618
623
  def get_in_context_memory(self, agent_id: str) -> Memory:
@@ -2267,18 +2272,18 @@ class LocalClient(AbstractClient):
2267
2272
  langchain_tool=langchain_tool,
2268
2273
  additional_imports_module_attr_map=additional_imports_module_attr_map,
2269
2274
  )
2270
- return self.server.tool_manager.create_or_update_tool(tool_create, actor=self.user)
2275
+ return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2271
2276
 
2272
2277
  def load_crewai_tool(self, crewai_tool: "CrewAIBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
2273
2278
  tool_create = ToolCreate.from_crewai(
2274
2279
  crewai_tool=crewai_tool,
2275
2280
  additional_imports_module_attr_map=additional_imports_module_attr_map,
2276
2281
  )
2277
- return self.server.tool_manager.create_or_update_tool(tool_create, actor=self.user)
2282
+ return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2278
2283
 
2279
2284
  def load_composio_tool(self, action: "ActionType") -> Tool:
2280
2285
  tool_create = ToolCreate.from_composio(action=action)
2281
- return self.server.tool_manager.create_or_update_tool(tool_create, actor=self.user)
2286
+ return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2282
2287
 
2283
2288
  # TODO: Use the above function `add_tool` here as there is duplicate logic
2284
2289
  def create_tool(
@@ -2310,7 +2315,7 @@ class LocalClient(AbstractClient):
2310
2315
 
2311
2316
  # call server function
2312
2317
  return self.server.tool_manager.create_or_update_tool(
2313
- ToolCreate(
2318
+ Tool(
2314
2319
  source_type=source_type,
2315
2320
  source_code=source_code,
2316
2321
  name=name,
@@ -2738,7 +2743,7 @@ class LocalClient(AbstractClient):
2738
2743
  return self.server.list_embedding_models()
2739
2744
 
2740
2745
  def create_org(self, name: Optional[str] = None) -> Organization:
2741
- return self.server.organization_manager.create_organization(name=name)
2746
+ return self.server.organization_manager.create_organization(pydantic_org=Organization(name=name))
2742
2747
 
2743
2748
  def list_orgs(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
2744
2749
  return self.server.organization_manager.list_organizations(cursor=cursor, limit=limit)
letta/orm/base.py CHANGED
@@ -67,7 +67,7 @@ class CommonSqlalchemyMetaMixins(Base):
67
67
  prop_value = getattr(self, full_prop, None)
68
68
  if not prop_value:
69
69
  return
70
- return f"user-{prop_value}"
70
+ return prop_value
71
71
 
72
72
  def _user_id_setter(self, prop: str, value: str) -> None:
73
73
  """returns the user id for the specified property"""
@@ -75,6 +75,9 @@ class CommonSqlalchemyMetaMixins(Base):
75
75
  if not value:
76
76
  setattr(self, full_prop, None)
77
77
  return
78
+ # Safety check
78
79
  prefix, id_ = value.split("-", 1)
79
80
  assert prefix == "user", f"{prefix} is not a valid id prefix for a user id"
80
- setattr(self, full_prop, id_)
81
+
82
+ # Set the full value
83
+ setattr(self, full_prop, value)
letta/orm/mixins.py CHANGED
@@ -1,11 +1,9 @@
1
- from typing import Optional
2
1
  from uuid import UUID
3
2
 
4
3
  from sqlalchemy import ForeignKey, String
5
4
  from sqlalchemy.orm import Mapped, mapped_column
6
5
 
7
6
  from letta.orm.base import Base
8
- from letta.orm.errors import MalformedIdError
9
7
 
10
8
 
11
9
  def is_valid_uuid4(uuid_string: str) -> bool:
@@ -17,53 +15,12 @@ def is_valid_uuid4(uuid_string: str) -> bool:
17
15
  return False
18
16
 
19
17
 
20
- def _relation_getter(instance: "Base", prop: str) -> Optional[str]:
21
- """Get relation and return id with prefix as a string."""
22
- prefix = prop.replace("_", "")
23
- formatted_prop = f"_{prop}_id"
24
- try:
25
- id_ = getattr(instance, formatted_prop) # Get the string id directly
26
- return f"{prefix}-{id_}"
27
- except AttributeError:
28
- return None
29
-
30
-
31
- def _relation_setter(instance: "Base", prop: str, value: str) -> None:
32
- """Set relation using the id with prefix, ensuring the id is a valid UUIDv4."""
33
- formatted_prop = f"_{prop}_id"
34
- prefix = prop.replace("_", "")
35
- if not value:
36
- setattr(instance, formatted_prop, None)
37
- return
38
- try:
39
- found_prefix, id_ = value.split("-", 1)
40
- except ValueError as e:
41
- raise MalformedIdError(f"{value} is not a valid ID.") from e
42
-
43
- # Ensure prefix matches
44
- assert found_prefix == prefix, f"{found_prefix} is not a valid id prefix, expecting {prefix}"
45
-
46
- # Validate that the id is a valid UUID4 string
47
- if not is_valid_uuid4(id_):
48
- raise MalformedIdError(f"Hash segment of {value} is not a valid UUID4")
49
-
50
- setattr(instance, formatted_prop, id_) # Store id as a string
51
-
52
-
53
18
  class OrganizationMixin(Base):
54
19
  """Mixin for models that belong to an organization."""
55
20
 
56
21
  __abstract__ = True
57
22
 
58
- _organization_id: Mapped[str] = mapped_column(String, ForeignKey("organization._id"))
59
-
60
- @property
61
- def organization_id(self) -> str:
62
- return _relation_getter(self, "organization")
63
-
64
- @organization_id.setter
65
- def organization_id(self, value: str) -> None:
66
- _relation_setter(self, "organization", value)
23
+ organization_id: Mapped[str] = mapped_column(String, ForeignKey("organizations.id"))
67
24
 
68
25
 
69
26
  class UserMixin(Base):
@@ -71,12 +28,4 @@ class UserMixin(Base):
71
28
 
72
29
  __abstract__ = True
73
30
 
74
- _user_id: Mapped[str] = mapped_column(String, ForeignKey("user._id"))
75
-
76
- @property
77
- def user_id(self) -> str:
78
- return _relation_getter(self, "user")
79
-
80
- @user_id.setter
81
- def user_id(self, value: str) -> None:
82
- _relation_setter(self, "user", value)
31
+ user_id: Mapped[str] = mapped_column(String, ForeignKey("users.id"))
letta/orm/organization.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import TYPE_CHECKING, List
2
2
 
3
+ from sqlalchemy import String
3
4
  from sqlalchemy.orm import Mapped, mapped_column, relationship
4
5
 
5
6
  from letta.orm.sqlalchemy_base import SqlalchemyBase
@@ -14,9 +15,10 @@ if TYPE_CHECKING:
14
15
  class Organization(SqlalchemyBase):
15
16
  """The highest level of the object tree. All Entities belong to one and only one Organization."""
16
17
 
17
- __tablename__ = "organization"
18
+ __tablename__ = "organizations"
18
19
  __pydantic_model__ = PydanticOrganization
19
20
 
21
+ id: Mapped[str] = mapped_column(String, primary_key=True)
20
22
  name: Mapped[str] = mapped_column(doc="The display name of the organization.")
21
23
 
22
24
  users: Mapped[List["User"]] = relationship("User", back_populates="organization", cascade="all, delete-orphan")
@@ -1,14 +1,11 @@
1
1
  from typing import TYPE_CHECKING, List, Literal, Optional, Type
2
- from uuid import uuid4
3
2
 
4
- from humps import depascalize
5
- from sqlalchemy import Boolean, String, select
3
+ from sqlalchemy import String, select
6
4
  from sqlalchemy.orm import Mapped, mapped_column
7
5
 
8
6
  from letta.log import get_logger
9
7
  from letta.orm.base import Base, CommonSqlalchemyMetaMixins
10
8
  from letta.orm.errors import NoResultFound
11
- from letta.orm.mixins import is_valid_uuid4
12
9
 
13
10
  if TYPE_CHECKING:
14
11
  from pydantic import BaseModel
@@ -24,27 +21,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
24
21
 
25
22
  __order_by_default__ = "created_at"
26
23
 
27
- _id: Mapped[str] = mapped_column(String, primary_key=True, default=lambda: f"{uuid4()}")
28
-
29
- deleted: Mapped[bool] = mapped_column(Boolean, default=False, doc="Is this record deleted? Used for universal soft deletes.")
30
-
31
- @classmethod
32
- def __prefix__(cls) -> str:
33
- return depascalize(cls.__name__)
34
-
35
- @property
36
- def id(self) -> Optional[str]:
37
- if self._id:
38
- return f"{self.__prefix__()}-{self._id}"
39
-
40
- @id.setter
41
- def id(self, value: str) -> None:
42
- if not value:
43
- return
44
- prefix, id_ = value.split("-", 1)
45
- assert prefix == self.__prefix__(), f"{prefix} is not a valid id prefix for {self.__class__.__name__}"
46
- assert is_valid_uuid4(id_), f"{id_} is not a valid uuid4"
47
- self._id = id_
24
+ id: Mapped[str] = mapped_column(String, primary_key=True)
48
25
 
49
26
  @classmethod
50
27
  def list(
@@ -57,11 +34,10 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
57
34
 
58
35
  # Add a cursor condition if provided
59
36
  if cursor:
60
- cursor_uuid = cls.get_uid_from_identifier(cursor) # Assuming the cursor is an _id value
61
- query = query.where(cls._id > cursor_uuid)
37
+ query = query.where(cls.id > cursor)
62
38
 
63
39
  # Add a limit to the query if provided
64
- query = query.order_by(cls._id).limit(limit)
40
+ query = query.order_by(cls.id).limit(limit)
65
41
 
66
42
  # Handle soft deletes if the class has the 'is_deleted' attribute
67
43
  if hasattr(cls, "is_deleted"):
@@ -70,20 +46,6 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
70
46
  # Execute the query and return the results as a list of model instances
71
47
  return list(session.execute(query).scalars())
72
48
 
73
- @classmethod
74
- def get_uid_from_identifier(cls, identifier: str, indifferent: Optional[bool] = False) -> str:
75
- """converts the id into a uuid object
76
- Args:
77
- identifier: the string identifier, such as `organization-xxxx-xx...`
78
- indifferent: if True, will not enforce the prefix check
79
- """
80
- try:
81
- uuid_string = identifier.split("-", 1)[1] if indifferent else identifier.replace(f"{cls.__prefix__()}-", "")
82
- assert is_valid_uuid4(uuid_string)
83
- return uuid_string
84
- except ValueError as e:
85
- raise ValueError(f"{identifier} is not a valid identifier for class {cls.__name__}") from e
86
-
87
49
  @classmethod
88
50
  def read(
89
51
  cls,
@@ -112,8 +74,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
112
74
 
113
75
  # If an identifier is provided, add it to the query conditions
114
76
  if identifier is not None:
115
- identifier = cls.get_uid_from_identifier(identifier)
116
- query = query.where(cls._id == identifier)
77
+ query = query.where(cls.id == identifier)
117
78
  query_conditions.append(f"id='{identifier}'")
118
79
 
119
80
  if kwargs:
@@ -183,7 +144,7 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
183
144
  org_id = getattr(actor, "organization_id", None)
184
145
  if not org_id:
185
146
  raise ValueError(f"object {actor} has no organization accessor")
186
- return query.where(cls._organization_id == cls.get_uid_from_identifier(org_id, indifferent=True), cls.is_deleted == False)
147
+ return query.where(cls.organization_id == org_id, cls.is_deleted == False)
187
148
 
188
149
  @property
189
150
  def __pydantic_model__(self) -> Type["BaseModel"]:
letta/orm/tool.py CHANGED
@@ -21,13 +21,14 @@ class Tool(SqlalchemyBase, OrganizationMixin):
21
21
  more granular permissions.
22
22
  """
23
23
 
24
- __tablename__ = "tool"
24
+ __tablename__ = "tools"
25
25
  __pydantic_model__ = PydanticTool
26
26
 
27
27
  # Add unique constraint on (name, _organization_id)
28
28
  # An organization should not have multiple tools with the same name
29
- __table_args__ = (UniqueConstraint("name", "_organization_id", name="uix_name_organization"),)
29
+ __table_args__ = (UniqueConstraint("name", "organization_id", name="uix_name_organization"),)
30
30
 
31
+ id: Mapped[str] = mapped_column(String, primary_key=True)
31
32
  name: Mapped[str] = mapped_column(doc="The display name of the tool.")
32
33
  description: Mapped[Optional[str]] = mapped_column(nullable=True, doc="The description of the tool.")
33
34
  tags: Mapped[List] = mapped_column(JSON, doc="Metadata tags used to filter tools.")
letta/orm/user.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ from sqlalchemy import String
3
4
  from sqlalchemy.orm import Mapped, mapped_column, relationship
4
5
 
5
6
  from letta.orm.mixins import OrganizationMixin
@@ -13,9 +14,10 @@ if TYPE_CHECKING:
13
14
  class User(SqlalchemyBase, OrganizationMixin):
14
15
  """User ORM class"""
15
16
 
16
- __tablename__ = "user"
17
+ __tablename__ = "users"
17
18
  __pydantic_model__ = PydanticUser
18
19
 
20
+ id: Mapped[str] = mapped_column(String, primary_key=True)
19
21
  name: Mapped[str] = mapped_column(nullable=False, doc="The display name of the user.")
20
22
 
21
23
  # relationships
letta/schemas/memory.py CHANGED
@@ -106,6 +106,10 @@ class Memory(BaseModel, validate_assignment=True):
106
106
  # New format
107
107
  obj.prompt_template = state["prompt_template"]
108
108
  for key, value in state["memory"].items():
109
+ # TODO: This is migration code, please take a look at a later time to get rid of this
110
+ if "name" in value:
111
+ value["template_name"] = value["name"]
112
+ value.pop("name")
109
113
  obj.memory[key] = Block(**value)
110
114
  else:
111
115
  # Old format (pre-template)
@@ -4,16 +4,16 @@ from typing import Optional
4
4
  from pydantic import Field
5
5
 
6
6
  from letta.schemas.letta_base import LettaBase
7
- from letta.utils import get_utc_time
7
+ from letta.utils import create_random_username, get_utc_time
8
8
 
9
9
 
10
10
  class OrganizationBase(LettaBase):
11
- __id_prefix__ = "organization"
11
+ __id_prefix__ = "org"
12
12
 
13
13
 
14
14
  class Organization(OrganizationBase):
15
- id: str = Field(..., description="The id of the organization.")
16
- name: str = Field(..., description="The name of the organization.")
15
+ id: str = OrganizationBase.generate_id_field()
16
+ name: str = Field(create_random_username(), description="The name of the organization.")
17
17
  created_at: Optional[datetime] = Field(default_factory=get_utc_time, description="The creation date of the organization.")
18
18
 
19
19
 
letta/schemas/tool.py CHANGED
@@ -33,21 +33,21 @@ class Tool(BaseTool):
33
33
 
34
34
  """
35
35
 
36
- id: str = Field(..., description="The id of the tool.")
36
+ id: str = BaseTool.generate_id_field()
37
37
  description: Optional[str] = Field(None, description="The description of the tool.")
38
38
  source_type: Optional[str] = Field(None, description="The type of the source code.")
39
39
  module: Optional[str] = Field(None, description="The module of the function.")
40
- organization_id: str = Field(..., description="The unique identifier of the organization associated with the tool.")
41
- name: str = Field(..., description="The name of the function.")
42
- tags: List[str] = Field(..., description="Metadata tags.")
40
+ organization_id: Optional[str] = Field(None, description="The unique identifier of the organization associated with the tool.")
41
+ name: Optional[str] = Field(None, description="The name of the function.")
42
+ tags: List[str] = Field([], description="Metadata tags.")
43
43
 
44
44
  # code
45
45
  source_code: str = Field(..., description="The source code of the function.")
46
- json_schema: Dict = Field(default_factory=dict, description="The JSON schema of the function.")
46
+ json_schema: Optional[Dict] = Field(None, description="The JSON schema of the function.")
47
47
 
48
48
  # metadata fields
49
- created_by_id: str = Field(..., description="The id of the user that made this Tool.")
50
- last_updated_by_id: str = Field(..., description="The id of the user that made this Tool.")
49
+ created_by_id: Optional[str] = Field(None, description="The id of the user that made this Tool.")
50
+ last_updated_by_id: Optional[str] = Field(None, description="The id of the user that made this Tool.")
51
51
 
52
52
  def to_dict(self):
53
53
  """
letta/schemas/user.py CHANGED
@@ -21,7 +21,7 @@ class User(UserBase):
21
21
  created_at (datetime): The creation date of the user.
22
22
  """
23
23
 
24
- id: str = Field(..., description="The id of the user.")
24
+ id: str = UserBase.generate_id_field()
25
25
  organization_id: Optional[str] = Field(OrganizationManager.DEFAULT_ORG_ID, description="The organization id of the user")
26
26
  name: str = Field(..., description="The name of the user.")
27
27
  created_at: Optional[datetime] = Field(default_factory=datetime.utcnow, description="The creation date of the user.")
@@ -40,6 +40,7 @@ router = APIRouter(prefix="/agents", tags=["agents"])
40
40
 
41
41
  @router.get("/", response_model=List[AgentState], operation_id="list_agents")
42
42
  def list_agents(
43
+ name: Optional[str] = Query(None, description="Name of the agent"),
43
44
  server: "SyncServer" = Depends(get_letta_server),
44
45
  user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
45
46
  ):
@@ -49,7 +50,11 @@ def list_agents(
49
50
  """
50
51
  actor = server.get_user_or_default(user_id=user_id)
51
52
 
52
- return server.list_agents(user_id=actor.id)
53
+ agents = server.list_agents(user_id=actor.id)
54
+ # TODO: move this logic to the ORM
55
+ if name:
56
+ agents = [a for a in agents if a.name == name]
57
+ return agents
53
58
 
54
59
 
55
60
  @router.get("/{agent_id}/context", response_model=ContextWindowOverview, operation_id="get_agent_context_window")
@@ -38,7 +38,8 @@ def create_org(
38
38
  """
39
39
  Create a new org in the database
40
40
  """
41
- org = server.organization_manager.create_organization(name=request.name)
41
+ org = Organization(**request.model_dump())
42
+ org = server.organization_manager.create_organization(pydantic_org=org)
42
43
  return org
43
44
 
44
45
 
@@ -89,7 +89,8 @@ def create_tool(
89
89
  actor = server.get_user_or_default(user_id=user_id)
90
90
 
91
91
  # Send request to create the tool
92
- return server.tool_manager.create_or_update_tool(tool_create=request, actor=actor)
92
+ tool = Tool(**request.model_dump())
93
+ return server.tool_manager.create_or_update_tool(pydantic_tool=tool, actor=actor)
93
94
 
94
95
 
95
96
  @router.patch("/{tool_id}", response_model=Tool, operation_id="update_tool")
@@ -51,8 +51,8 @@ def create_user(
51
51
  """
52
52
  Create a new user in the database
53
53
  """
54
-
55
- user = server.user_manager.create_user(request)
54
+ user = User(**request.model_dump())
55
+ user = server.user_manager.create_user(user)
56
56
  return user
57
57
 
58
58
 
letta/server/server.py CHANGED
@@ -824,7 +824,7 @@ class SyncServer(Server):
824
824
  source_type = "python"
825
825
  tags = ["memory", "memgpt-base"]
826
826
  tool = self.tool_manager.create_or_update_tool(
827
- ToolCreate(
827
+ Tool(
828
828
  source_code=source_code,
829
829
  source_type=source_type,
830
830
  tags=tags,
@@ -1766,7 +1766,7 @@ class SyncServer(Server):
1766
1766
  tool_creates += ToolCreate.load_default_composio_tools()
1767
1767
  for tool_create in tool_creates:
1768
1768
  try:
1769
- self.tool_manager.create_or_update_tool(tool_create, actor=actor)
1769
+ self.tool_manager.create_or_update_tool(Tool(**tool_create.model_dump()), actor=actor)
1770
1770
  except Exception as e:
1771
1771
  warnings.warn(f"An error occurred while creating tool {tool_create}: {e}")
1772
1772
  warnings.warn(traceback.format_exc())
@@ -3,13 +3,13 @@ from typing import List, Optional
3
3
  from letta.orm.errors import NoResultFound
4
4
  from letta.orm.organization import Organization as OrganizationModel
5
5
  from letta.schemas.organization import Organization as PydanticOrganization
6
- from letta.utils import create_random_username, enforce_types
6
+ from letta.utils import enforce_types
7
7
 
8
8
 
9
9
  class OrganizationManager:
10
10
  """Manager class to handle business logic related to Organizations."""
11
11
 
12
- DEFAULT_ORG_ID = "organization-00000000-0000-4000-8000-000000000000"
12
+ DEFAULT_ORG_ID = "org-00000000-0000-4000-8000-000000000000"
13
13
  DEFAULT_ORG_NAME = "default_org"
14
14
 
15
15
  def __init__(self):
@@ -37,10 +37,10 @@ class OrganizationManager:
37
37
  raise ValueError(f"Organization with id {org_id} not found.")
38
38
 
39
39
  @enforce_types
40
- def create_organization(self, name: Optional[str] = None) -> PydanticOrganization:
40
+ def create_organization(self, pydantic_org: PydanticOrganization) -> PydanticOrganization:
41
41
  """Create a new organization. If a name is provided, it is used, otherwise, a random one is generated."""
42
42
  with self.session_maker() as session:
43
- org = OrganizationModel(name=name if name else create_random_username())
43
+ org = OrganizationModel(**pydantic_org.model_dump())
44
44
  org.create(session)
45
45
  return org.to_pydantic()
46
46
 
@@ -7,10 +7,9 @@ from letta.functions.functions import derive_openai_json_schema, load_function_s
7
7
 
8
8
  # TODO: Remove this once we translate all of these to the ORM
9
9
  from letta.orm.errors import NoResultFound
10
- from letta.orm.organization import Organization as OrganizationModel
11
10
  from letta.orm.tool import Tool as ToolModel
12
11
  from letta.schemas.tool import Tool as PydanticTool
13
- from letta.schemas.tool import ToolCreate, ToolUpdate
12
+ from letta.schemas.tool import ToolUpdate
14
13
  from letta.schemas.user import User as PydanticUser
15
14
  from letta.utils import enforce_types, printd
16
15
 
@@ -33,20 +32,20 @@ class ToolManager:
33
32
  self.session_maker = db_context
34
33
 
35
34
  @enforce_types
36
- def create_or_update_tool(self, tool_create: ToolCreate, actor: PydanticUser) -> PydanticTool:
35
+ def create_or_update_tool(self, pydantic_tool: PydanticTool, actor: PydanticUser) -> PydanticTool:
37
36
  """Create a new tool based on the ToolCreate schema."""
38
37
  # Derive json_schema
39
- derived_json_schema = tool_create.json_schema or derive_openai_json_schema(
40
- source_code=tool_create.source_code, name=tool_create.name
38
+ derived_json_schema = pydantic_tool.json_schema or derive_openai_json_schema(
39
+ source_code=pydantic_tool.source_code, name=pydantic_tool.name
41
40
  )
42
- derived_name = tool_create.name or derived_json_schema["name"]
41
+ derived_name = pydantic_tool.name or derived_json_schema["name"]
43
42
 
44
43
  try:
45
44
  # NOTE: We use the organization id here
46
45
  # This is important, because even if it's a different user, adding the same tool to the org should not happen
47
46
  tool = self.get_tool_by_name(tool_name=derived_name, actor=actor)
48
47
  # Put to dict and remove fields that should not be reset
49
- update_data = tool_create.model_dump(exclude={"module"}, exclude_unset=True)
48
+ update_data = pydantic_tool.model_dump(exclude={"module"}, exclude_unset=True, exclude_none=True)
50
49
  # Remove redundant update fields
51
50
  update_data = {key: value for key, value in update_data.items() if getattr(tool, key) != value}
52
51
 
@@ -55,22 +54,24 @@ class ToolManager:
55
54
  self.update_tool_by_id(tool.id, ToolUpdate(**update_data), actor)
56
55
  else:
57
56
  printd(
58
- f"`create_or_update_tool` was called with user_id={actor.id}, organization_id={actor.organization_id}, name={tool_create.name}, but found existing tool with nothing to update."
57
+ f"`create_or_update_tool` was called with user_id={actor.id}, organization_id={actor.organization_id}, name={pydantic_tool.name}, but found existing tool with nothing to update."
59
58
  )
60
59
  except NoResultFound:
61
- tool_create.json_schema = derived_json_schema
62
- tool_create.name = derived_name
63
- tool = self.create_tool(tool_create, actor=actor)
60
+ pydantic_tool.json_schema = derived_json_schema
61
+ pydantic_tool.name = derived_name
62
+ tool = self.create_tool(pydantic_tool, actor=actor)
64
63
 
65
64
  return tool
66
65
 
67
66
  @enforce_types
68
- def create_tool(self, tool_create: ToolCreate, actor: PydanticUser) -> PydanticTool:
67
+ def create_tool(self, pydantic_tool: PydanticTool, actor: PydanticUser) -> PydanticTool:
69
68
  """Create a new tool based on the ToolCreate schema."""
70
69
  # Create the tool
71
70
  with self.session_maker() as session:
72
- create_data = tool_create.model_dump()
73
- tool = ToolModel(**create_data, organization_id=actor.organization_id) # Unpack everything directly into ToolModel
71
+ # Set the organization id at the ORM layer
72
+ pydantic_tool.organization_id = actor.organization_id
73
+ tool_data = pydantic_tool.model_dump()
74
+ tool = ToolModel(**tool_data)
74
75
  tool.create(session, actor=actor)
75
76
 
76
77
  return tool.to_pydantic()
@@ -99,7 +100,7 @@ class ToolManager:
99
100
  db_session=session,
100
101
  cursor=cursor,
101
102
  limit=limit,
102
- _organization_id=OrganizationModel.get_uid_from_identifier(actor.organization_id),
103
+ organization_id=actor.organization_id,
103
104
  )
104
105
  return [tool.to_pydantic() for tool in tools]
105
106
 
@@ -176,7 +177,7 @@ class ToolManager:
176
177
  # create to tool
177
178
  tools.append(
178
179
  self.create_or_update_tool(
179
- ToolCreate(
180
+ PydanticTool(
180
181
  name=name,
181
182
  tags=tags,
182
183
  source_type="python",
@@ -4,7 +4,7 @@ from letta.orm.errors import NoResultFound
4
4
  from letta.orm.organization import Organization as OrganizationModel
5
5
  from letta.orm.user import User as UserModel
6
6
  from letta.schemas.user import User as PydanticUser
7
- from letta.schemas.user import UserCreate, UserUpdate
7
+ from letta.schemas.user import UserUpdate
8
8
  from letta.services.organization_manager import OrganizationManager
9
9
  from letta.utils import enforce_types
10
10
 
@@ -42,10 +42,10 @@ class UserManager:
42
42
  return user.to_pydantic()
43
43
 
44
44
  @enforce_types
45
- def create_user(self, user_create: UserCreate) -> PydanticUser:
45
+ def create_user(self, pydantic_user: PydanticUser) -> PydanticUser:
46
46
  """Create a new user if it doesn't already exist."""
47
47
  with self.session_maker() as session:
48
- new_user = UserModel(**user_create.model_dump())
48
+ new_user = UserModel(**pydantic_user.model_dump())
49
49
  new_user.create(session)
50
50
  return new_user.to_pydantic()
51
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.5.1.dev20241105104128
3
+ Version: 0.5.1.dev20241106104104
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -99,7 +99,7 @@ Description-Content-Type: text/markdown
99
99
  </h3>
100
100
 
101
101
  **👾 Letta** is an open source framework for building stateful LLM applications. You can use Letta to build **stateful agents** with advanced reasoning capabilities and transparent long-term memory. The Letta framework is white box and model-agnostic.
102
-
102
+
103
103
  [![Discord](https://img.shields.io/discord/1161736243340640419?label=Discord&logo=discord&logoColor=5865F2&style=flat-square&color=5865F2)](https://discord.gg/letta)
104
104
  [![Twitter Follow](https://img.shields.io/badge/Follow-%40Letta__AI-1DA1F2?style=flat-square&logo=x&logoColor=white)](https://twitter.com/Letta_AI)
105
105
  [![arxiv 2310.08560](https://img.shields.io/badge/Research-2310.08560-B31B1B?logo=arxiv&style=flat-square)](https://arxiv.org/abs/2310.08560)
@@ -1,8 +1,8 @@
1
1
  letta/__init__.py,sha256=zlZXKr0iQIsC7FTkN9fjTvAgQGPXyr0nnkMJ7_OE1D0,1014
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=sXRJHwgrsbOQrZwdrej0i8qAFj64xFu7fPXkpz4zj5c,76191
3
+ letta/agent.py,sha256=D4d0ASMYn4Wfq-AKoho7KUM1i9rWKIk5zEYod6zq418,76319
4
4
  letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
5
- letta/agent_store/db.py,sha256=mTY3YWUs5n17479GMeTmxN8DAyzbBweVcypyNsK_slg,23435
5
+ letta/agent_store/db.py,sha256=bOchnK54xpXjiAlvUqFDyGS8Q6T-nVearxoSU0i8Q_Q,23346
6
6
  letta/agent_store/lancedb.py,sha256=i63d4VZwj9UIOTNs5f0JZ_r5yZD-jKWz4FAH4RMpXOE,5104
7
7
  letta/agent_store/milvus.py,sha256=xUu-D9a6N10MuGJ-R-QWR2IHX77ueqAp88tV4gg9B4M,8470
8
8
  letta/agent_store/qdrant.py,sha256=6_33V-FEDpT9LG5zmr6-3y9slw1YFLswxpahiyMkvHA,7880
@@ -13,7 +13,7 @@ letta/cli/cli.py,sha256=JP5-nw38f2BjCOU4pU4LsDBQmTJNNhJcJuviw-9a2Yw,16142
13
13
  letta/cli/cli_config.py,sha256=D-CpSZI1cDvdSQr3-zhGuDrJnZo1Ko7bi_wuxcBxxqo,8555
14
14
  letta/cli/cli_load.py,sha256=x4L8s15GwIW13xrhKYFWHo_y-IVGtoPDHWWKcHDRP10,4587
15
15
  letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- letta/client/client.py,sha256=LQA4wm8BW5yoTrIcrgEN6dFKmNYnlao5MD6XgUoRmsQ,96632
16
+ letta/client/client.py,sha256=hboYStQk5VC4rC7mIbylZdlb36XhkG7xwK-2IOSE9zQ,97082
17
17
  letta/client/streaming.py,sha256=Hh5pjlyrdCuO2V75ZCxSSOCPd3BmHdKFGaIUJC6fBp0,4775
18
18
  letta/client/utils.py,sha256=OJlAKWrldc4I6M1WpcTWNtPJ4wfxlzlZqWLfCozkFtI,2872
19
19
  letta/config.py,sha256=eK-ip06ELHNYriInkgfidDvJxQ2tD1u49I-VLXB87nE,18929
@@ -91,14 +91,14 @@ letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
91
91
  letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
92
92
  letta/orm/__all__.py,sha256=2gh2MZTkA3Hw67VWVKK3JIStJOqTeLdpCvYSVYNeEDA,692
93
93
  letta/orm/__init__.py,sha256=J2GZpfXQunxU0ChavjkhoaSruluRFrLYknXD2m0BP_g,144
94
- letta/orm/base.py,sha256=uK56wXg4Hv5jGuBsThSgmlFdl0e2u4VGiTX4RiRqTK4,2671
94
+ letta/orm/base.py,sha256=K_LpNUURbsj44ycHbzvNXG_n8pBOjf1YvDaikIPDpQA,2716
95
95
  letta/orm/enums.py,sha256=KfHcFt_fR6GUmSlmfsa-TetvmuRxGESNve8MStRYW64,145
96
96
  letta/orm/errors.py,sha256=somsGtotFlb3SDM6tKdZ5TDGwEEP3ppx47ICAvNMnkg,225
97
- letta/orm/mixins.py,sha256=hZJFYnmSGklryuAOypvVhMgXyU7pgatGEun-p-2f_Fc,2457
98
- letta/orm/organization.py,sha256=ccE0ShFWqWPdtXiCRkZCSeIERbtwU-a7AF99N3S6IRM,1468
99
- letta/orm/sqlalchemy_base.py,sha256=BPnegSQxW0SVnvje4H2441COBHfO30Tkwi2vG-9gwjY,8288
100
- letta/orm/tool.py,sha256=4cYKSoogfpH4O0wruIqU8vzEGgiCOL3gQfG5doSqpDw,2089
101
- letta/orm/user.py,sha256=69FU1rSXhlRJMaAUPYebxgFPn8UCQRU8TYUkjXLrALQ,1136
97
+ letta/orm/mixins.py,sha256=fW4oa1cUFbgVE46KSQlW_hwzsZSqEBSSV-U3xJC6fyw,749
98
+ letta/orm/organization.py,sha256=KpoSStoxThbBHfG4nowkpyACwcW6R_kZFuPsp6aHGPA,1561
99
+ letta/orm/sqlalchemy_base.py,sha256=jxOQP3eAherFVD-0SbTanANEwbBfATXWqp9ix2pqhC0,6556
100
+ letta/orm/tool.py,sha256=7FIeldPJTEOLA5ygasTOVXqUcm2aouYYARUOJXdaC4Y,2151
101
+ letta/orm/user.py,sha256=-KNdpfnKRVJtSw0NrGRrVoSBBv7ASDchVempKtUuk-A,1229
102
102
  letta/persistence_manager.py,sha256=LlLgEDpSafCPAiyKmuq0NvVAnfBkZo6TWbGIKYQjQBs,5200
103
103
  letta/personas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
104
104
  letta/personas/examples/anna_pa.txt,sha256=zgiNdSNhy1HQy58cF_6RFPzcg2i37F9v38YuL1CW40A,1849
@@ -137,20 +137,20 @@ letta/schemas/letta_message.py,sha256=RuVVtwFbi85yP3dXQxowofQ6cI2cO_CdGtgpHGQzgH
137
137
  letta/schemas/letta_request.py,sha256=_oiDshc_AoFWIfXRk2VX5-AxO5vDlyN-9r-gnyLj_30,1890
138
138
  letta/schemas/letta_response.py,sha256=ClI0LKyhMVI0N5CSnTAbcHaw7simkyrUTKYX0fr0qzw,1610
139
139
  letta/schemas/llm_config.py,sha256=eFA48vKBTO70qaob8pak2CWOH7TCQeqWuClkMBc2vbY,4172
140
- letta/schemas/memory.py,sha256=rNUxCs11nZf_gbKgkJ-c4FRHZVhqIfTyfD2nS5WP6oI,11490
140
+ letta/schemas/memory.py,sha256=fHkJZr8CrGcHhbJlckWgfRYMhLkRliKCU-hRxqr19ks,11725
141
141
  letta/schemas/message.py,sha256=DQxnRYrYgHXpTKfMzfS-bpCAe-BO_Rmcfc1Wf-4GHjw,33703
142
142
  letta/schemas/openai/chat_completion_request.py,sha256=AOIwgbN3CZKVqkuXeMHeSa53u4h0wVq69t3T_LJ0vIE,3389
143
143
  letta/schemas/openai/chat_completion_response.py,sha256=05FRfm1EsVivyeWo2aoJk34h3W4pAb4lBCPn1eujjcw,3916
144
144
  letta/schemas/openai/chat_completions.py,sha256=V0ZPIIk-ds3O6MAkNHMz8zh1hqMFSPrTcYr88WDYzWE,3588
145
145
  letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNosmLVSuhXYa_xU,357
146
146
  letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
147
- letta/schemas/organization.py,sha256=DxWkApcw6ES1k_TKtF8Vw3pcPDHx1sWwPSmvhgw2fJo,679
147
+ letta/schemas/organization.py,sha256=d2oN3IK2HeruEHKXwIzCbJ3Fxdi_BEe9JZ8J9aDbHwQ,698
148
148
  letta/schemas/passage.py,sha256=eYQMxD_XjHAi72jmqcGBU4wM4VZtSU0XK8uhQxxN3Ug,3563
149
149
  letta/schemas/source.py,sha256=hB4Ai6Nj8dFdbxv5_Qaf4uN_cmdGmnzgc-4QnHXcV3o,2562
150
- letta/schemas/tool.py,sha256=f_sluD9aZ-9tgJWLFllvJ1Jeo5sFvHOv1qzU714YHEk,9395
150
+ letta/schemas/tool.py,sha256=GuZUMGCKERtBlLIBu0f6ZjOfmI4LXSjebyLBZUeaIPk,9415
151
151
  letta/schemas/tool_rule.py,sha256=dHEwVOZ40lMEVCrry7wlZM0IJo5SJrZqXKYpXe40bjY,778
152
152
  letta/schemas/usage.py,sha256=lvn1ooHwLEdv6gwQpw5PBUbcwn_gwdT6HA-fCiix6sY,817
153
- letta/schemas/user.py,sha256=nRuH0yVVmAFDq6ubqaKkupUmohMpikla0bQUaKVGooo,1543
153
+ letta/schemas/user.py,sha256=V32Tgl6oqB3KznkxUz12y7agkQicjzW7VocSpj78i6Q,1526
154
154
  letta/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
155
155
  letta/server/constants.py,sha256=yAdGbLkzlOU_dLTx0lKDmAnj0ZgRXCEaIcPJWO69eaE,92
156
156
  letta/server/rest_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -168,18 +168,18 @@ letta/server/rest_api/routers/openai/assistants/threads.py,sha256=WXVGBaBvSNPB7Z
168
168
  letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
169
  letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=-uye6cm4SnoQGwxhr1N1FrSXOlnO2Hvbfj6k8JSc45k,4918
170
170
  letta/server/rest_api/routers/v1/__init__.py,sha256=sqlVZa-u9DJwdRsp0_8YUGrac9DHguIB4wETlEDRylA,666
171
- letta/server/rest_api/routers/v1/agents.py,sha256=Dg_ZpQRVDP5vCOEiND7SWhnpGcraE5Qb4VZEdZhXprk,25509
171
+ letta/server/rest_api/routers/v1/agents.py,sha256=1qB56MaoE_cU_tOKAFkCLeBb06_TzxN0lHcWs4sOggM,25708
172
172
  letta/server/rest_api/routers/v1/blocks.py,sha256=0WekE_yBD2U3jYgPxI0DCFjACWavCAlvm_Ybw5SZBnw,2583
173
173
  letta/server/rest_api/routers/v1/health.py,sha256=pKCuVESlVOhGIb4VC4K-H82eZqfghmT6kvj2iOkkKuc,401
174
174
  letta/server/rest_api/routers/v1/jobs.py,sha256=a-j0v-5A0un0pVCOHpfeWnzpOWkVDQO6ti42k_qAlZY,2272
175
175
  letta/server/rest_api/routers/v1/llms.py,sha256=TcyvSx6MEM3je5F4DysL7ligmssL_pFlJaaO4uL95VY,877
176
- letta/server/rest_api/routers/v1/organizations.py,sha256=hCuni52aM1Gfmuu_BeodXAvTkfrvexEo_wZf8UtyaAM,2034
176
+ letta/server/rest_api/routers/v1/organizations.py,sha256=tyqVzXTpMtk3sKxI3Iz4aS6RhbGEbXDzFBB_CpW18v4,2080
177
177
  letta/server/rest_api/routers/v1/sources.py,sha256=eY_pk9jRL2Y9yIZdsTjH6EuKsfH1neaTU15MKNL0dvw,8749
178
- letta/server/rest_api/routers/v1/tools.py,sha256=AYJP1dGqFLVZkWaypPi7TP7r53Vfz2Ejf_kWxmLl6IQ,4234
179
- letta/server/rest_api/routers/v1/users.py,sha256=bxQ-YdevjDqmgNDfbSPAG_4KEVvPNBHD_-Lp1MdeMec,3374
178
+ letta/server/rest_api/routers/v1/tools.py,sha256=3QiHmEh3auDX8xi-QzUrh_r242amcitXF232pF6QfoY,4273
179
+ letta/server/rest_api/routers/v1/users.py,sha256=EfqXZk1X3YNajVKMud6dts8WojSUUkD18N4Wibi0ndU,3410
180
180
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
181
181
  letta/server/rest_api/utils.py,sha256=GdHYCzXtbM5VCAYDPR0z5gnNZpRhwPld2BGZV7xT6cU,2924
182
- letta/server/server.py,sha256=Mr6ERBRzG__LVZTNPab3HZlZs2oKJPmBnreKLJKgOsY,78916
182
+ letta/server/server.py,sha256=dz2pYza0ImLucFF7BLm7Vvtry3Dl3JbhJDd6SRnx8jQ,78931
183
183
  letta/server/startup.sh,sha256=Atyf4hL5XLpC4aA3esEyOeNWfemlgXlgpeKB59fDFvw,334
184
184
  letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
185
185
  letta/server/static_files/assets/index-9fa459a2.js,sha256=j2oMcDJO9dWJaH5e-tsflbVpWK20gLWpZKJk4-Kuy6A,1815592
@@ -193,16 +193,16 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
193
193
  letta/server/ws_api/protocol.py,sha256=M_-gM5iuDBwa1cuN2IGNCG5GxMJwU2d3XW93XALv9s8,1821
194
194
  letta/server/ws_api/server.py,sha256=C2Kv48PCwl46DQFb0ZP30s86KJLQ6dZk2AhWQEZn9pY,6004
195
195
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
196
- letta/services/organization_manager.py,sha256=wW4wOmKaqhHC7oTGWU38OO-dFtmRI0JVmq_k8gI1b0A,3658
197
- letta/services/tool_manager.py,sha256=vrRwY6XNmc-Bo1gG1M_RRHgqT7Sio31jYnKvtYjBXTc,8440
198
- letta/services/user_manager.py,sha256=5wjmnhLoxsc9FCKD0IPotfV24i_iCqmifOkhIBvshtc,4404
196
+ letta/services/organization_manager.py,sha256=2RMmA8TRE9OFkomnT2NptEuFL4Y1lU3H2w1YjacI-o8,3613
197
+ letta/services/tool_manager.py,sha256=IFfOX2fNGH7LYv5qI30CUTnUceW6zffs4O7qAQz5CTA,8397
198
+ letta/services/user_manager.py,sha256=UJa0hqCjz0yXtvrCR8OVBqlSR5lC_Ejn-uG__58zLds,4398
199
199
  letta/settings.py,sha256=yiYNmnYKj_BdTm0cBEIvQKYGU-lCmFntqsyVfRUy3_k,3411
200
200
  letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,15736
201
201
  letta/streaming_utils.py,sha256=329fsvj1ZN0r0LpQtmMPZ2vSxkDBIUUwvGHZFkjm2I8,11745
202
202
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
203
203
  letta/utils.py,sha256=SXLEYhyp3gHyIjrxNIKNZZ5ittKo3KOj6zxgC_Trex0,31012
204
- letta_nightly-0.5.1.dev20241105104128.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
205
- letta_nightly-0.5.1.dev20241105104128.dist-info/METADATA,sha256=uExxjv_fkEhqkbYDYrYJqcEOCCaI6MuFTzhr1x0J0z0,10795
206
- letta_nightly-0.5.1.dev20241105104128.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
207
- letta_nightly-0.5.1.dev20241105104128.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
208
- letta_nightly-0.5.1.dev20241105104128.dist-info/RECORD,,
204
+ letta_nightly-0.5.1.dev20241106104104.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
205
+ letta_nightly-0.5.1.dev20241106104104.dist-info/METADATA,sha256=LkoZ6XlBdXxzQ9lizd3w-BTqshhQgwEHGlAr2cPfIbo,10793
206
+ letta_nightly-0.5.1.dev20241106104104.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
207
+ letta_nightly-0.5.1.dev20241106104104.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
208
+ letta_nightly-0.5.1.dev20241106104104.dist-info/RECORD,,