letta-nightly 0.5.2.dev20241113104112__py3-none-any.whl → 0.5.2.dev20241113234401__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_store/db.py CHANGED
@@ -27,8 +27,9 @@ from tqdm import tqdm
27
27
  from letta.agent_store.storage import StorageConnector, TableType
28
28
  from letta.config import LettaConfig
29
29
  from letta.constants import MAX_EMBEDDING_DIM
30
- from letta.metadata import EmbeddingConfigColumn, FileMetadataModel, ToolCallColumn
30
+ from letta.metadata import EmbeddingConfigColumn, ToolCallColumn
31
31
  from letta.orm.base import Base
32
+ from letta.orm.file import FileMetadata as FileMetadataModel
32
33
 
33
34
  # from letta.schemas.message import Message, Passage, Record, RecordType, ToolCall
34
35
  from letta.schemas.message import Message
letta/client/client.py CHANGED
@@ -2395,7 +2395,7 @@ class LocalClient(AbstractClient):
2395
2395
  Args:
2396
2396
  id (str): ID of the tool
2397
2397
  """
2398
- return self.server.tool_manager.delete_tool_by_id(id, user_id=self.user_id)
2398
+ return self.server.tool_manager.delete_tool_by_id(id, actor=self.user)
2399
2399
 
2400
2400
  def get_tool_id(self, name: str) -> Optional[str]:
2401
2401
  """
@@ -2440,7 +2440,7 @@ class LocalClient(AbstractClient):
2440
2440
  return job
2441
2441
 
2442
2442
  def delete_file_from_source(self, source_id: str, file_id: str):
2443
- self.server.delete_file_from_source(source_id, file_id, user_id=self.user_id)
2443
+ self.server.source_manager.delete_file(file_id, actor=self.user)
2444
2444
 
2445
2445
  def get_job(self, job_id: str):
2446
2446
  return self.server.get_job(job_id=job_id)
@@ -2561,7 +2561,7 @@ class LocalClient(AbstractClient):
2561
2561
  Returns:
2562
2562
  files (List[FileMetadata]): List of files
2563
2563
  """
2564
- return self.server.list_files_from_source(source_id=source_id, limit=limit, cursor=cursor)
2564
+ return self.server.source_manager.list_files(source_id=source_id, limit=limit, cursor=cursor, actor=self.user)
2565
2565
 
2566
2566
  def update_source(self, source_id: str, name: Optional[str] = None) -> Source:
2567
2567
  """
@@ -12,6 +12,7 @@ from letta.embeddings import embedding_model
12
12
  from letta.schemas.file import FileMetadata
13
13
  from letta.schemas.passage import Passage
14
14
  from letta.schemas.source import Source
15
+ from letta.services.source_manager import SourceManager
15
16
  from letta.utils import create_uuid_from_string
16
17
 
17
18
 
@@ -41,12 +42,7 @@ class DataConnector:
41
42
  """
42
43
 
43
44
 
44
- def load_data(
45
- connector: DataConnector,
46
- source: Source,
47
- passage_store: StorageConnector,
48
- file_metadata_store: StorageConnector,
49
- ):
45
+ def load_data(connector: DataConnector, source: Source, passage_store: StorageConnector, source_manager: SourceManager, actor: "User"):
50
46
  """Load data from a connector (generates file and passages) into a specified source_id, associated with a user_id."""
51
47
  embedding_config = source.embedding_config
52
48
 
@@ -60,7 +56,7 @@ def load_data(
60
56
  file_count = 0
61
57
  for file_metadata in connector.find_files(source):
62
58
  file_count += 1
63
- file_metadata_store.insert(file_metadata)
59
+ source_manager.create_file(file_metadata, actor)
64
60
 
65
61
  # generate passages
66
62
  for passage_text, passage_metadata in connector.generate_passages(file_metadata, chunk_size=embedding_config.embedding_chunk_size):
@@ -155,7 +151,6 @@ class DirectoryConnector(DataConnector):
155
151
 
156
152
  for metadata in extract_metadata_from_files(files):
157
153
  yield FileMetadata(
158
- user_id=source.created_by_id,
159
154
  source_id=source.id,
160
155
  file_name=metadata.get("file_name"),
161
156
  file_path=metadata.get("file_path"),
letta/metadata.py CHANGED
@@ -11,7 +11,6 @@ from sqlalchemy import (
11
11
  Column,
12
12
  DateTime,
13
13
  Index,
14
- Integer,
15
14
  String,
16
15
  TypeDecorator,
17
16
  )
@@ -24,7 +23,6 @@ from letta.schemas.api_key import APIKey
24
23
  from letta.schemas.block import Block, Human, Persona
25
24
  from letta.schemas.embedding_config import EmbeddingConfig
26
25
  from letta.schemas.enums import JobStatus
27
- from letta.schemas.file import FileMetadata
28
26
  from letta.schemas.job import Job
29
27
  from letta.schemas.llm_config import LLMConfig
30
28
  from letta.schemas.memory import Memory
@@ -40,41 +38,6 @@ from letta.settings import settings
40
38
  from letta.utils import enforce_types, get_utc_time, printd
41
39
 
42
40
 
43
- class FileMetadataModel(Base):
44
- __tablename__ = "files"
45
- __table_args__ = {"extend_existing": True}
46
-
47
- id = Column(String, primary_key=True, nullable=False)
48
- user_id = Column(String, nullable=False)
49
- # TODO: Investigate why this breaks during table creation due to FK
50
- # source_id = Column(String, ForeignKey("sources.id"), nullable=False)
51
- source_id = Column(String, nullable=False)
52
- file_name = Column(String, nullable=True)
53
- file_path = Column(String, nullable=True)
54
- file_type = Column(String, nullable=True)
55
- file_size = Column(Integer, nullable=True)
56
- file_creation_date = Column(String, nullable=True)
57
- file_last_modified_date = Column(String, nullable=True)
58
- created_at = Column(DateTime(timezone=True), server_default=func.now())
59
-
60
- def __repr__(self):
61
- return f"<FileMetadata(id='{self.id}', source_id='{self.source_id}', file_name='{self.file_name}')>"
62
-
63
- def to_record(self):
64
- return FileMetadata(
65
- id=self.id,
66
- user_id=self.user_id,
67
- source_id=self.source_id,
68
- file_name=self.file_name,
69
- file_path=self.file_path,
70
- file_type=self.file_type,
71
- file_size=self.file_size,
72
- file_creation_date=self.file_creation_date,
73
- file_last_modified_date=self.file_last_modified_date,
74
- created_at=self.created_at,
75
- )
76
-
77
-
78
41
  class LLMConfigColumn(TypeDecorator):
79
42
  """Custom type for storing LLMConfig as JSON"""
80
43
 
@@ -510,21 +473,6 @@ class MetadataStore:
510
473
  session.add(BlockModel(**vars(block)))
511
474
  session.commit()
512
475
 
513
- @enforce_types
514
- def delete_file_from_source(self, source_id: str, file_id: str, user_id: Optional[str]):
515
- with self.session_maker() as session:
516
- file_metadata = (
517
- session.query(FileMetadataModel)
518
- .filter(FileMetadataModel.source_id == source_id, FileMetadataModel.id == file_id, FileMetadataModel.user_id == user_id)
519
- .first()
520
- )
521
-
522
- if file_metadata:
523
- session.delete(file_metadata)
524
- session.commit()
525
-
526
- return file_metadata
527
-
528
476
  @enforce_types
529
477
  def delete_block(self, block_id: str):
530
478
  with self.session_maker() as session:
@@ -653,27 +601,6 @@ class MetadataStore:
653
601
  session.add(JobModel(**vars(job)))
654
602
  session.commit()
655
603
 
656
- @enforce_types
657
- def list_files_from_source(self, source_id: str, limit: int, cursor: Optional[str]):
658
- with self.session_maker() as session:
659
- # Start with the basic query filtered by source_id
660
- query = session.query(FileMetadataModel).filter(FileMetadataModel.source_id == source_id)
661
-
662
- if cursor:
663
- # Assuming cursor is the ID of the last file in the previous page
664
- query = query.filter(FileMetadataModel.id > cursor)
665
-
666
- # Order by ID or other ordering criteria to ensure correct pagination
667
- query = query.order_by(FileMetadataModel.id)
668
-
669
- # Limit the number of results returned
670
- results = query.limit(limit).all()
671
-
672
- # Convert the results to the required FileMetadata objects
673
- files = [r.to_record() for r in results]
674
-
675
- return files
676
-
677
604
  def delete_job(self, job_id: str):
678
605
  with self.session_maker() as session:
679
606
  session.query(JobModel).filter(JobModel.id == job_id).delete()
letta/orm/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from letta.orm.base import Base
2
+ from letta.orm.file import FileMetadata
2
3
  from letta.orm.organization import Organization
3
4
  from letta.orm.source import Source
4
5
  from letta.orm.tool import Tool
letta/orm/file.py ADDED
@@ -0,0 +1,29 @@
1
+ from typing import TYPE_CHECKING, Optional
2
+
3
+ from sqlalchemy import Integer, String
4
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
5
+
6
+ from letta.orm.mixins import OrganizationMixin, SourceMixin
7
+ from letta.orm.sqlalchemy_base import SqlalchemyBase
8
+ from letta.schemas.file import FileMetadata as PydanticFileMetadata
9
+
10
+ if TYPE_CHECKING:
11
+ from letta.orm.organization import Organization
12
+
13
+
14
+ class FileMetadata(SqlalchemyBase, OrganizationMixin, SourceMixin):
15
+ """Represents metadata for an uploaded file."""
16
+
17
+ __tablename__ = "files"
18
+ __pydantic_model__ = PydanticFileMetadata
19
+
20
+ file_name: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The name of the file.")
21
+ file_path: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The file path on the system.")
22
+ file_type: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The type of the file.")
23
+ file_size: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, doc="The size of the file in bytes.")
24
+ file_creation_date: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The creation date of the file.")
25
+ file_last_modified_date: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="The last modified date of the file.")
26
+
27
+ # relationships
28
+ organization: Mapped["Organization"] = relationship("Organization", back_populates="files", lazy="selectin")
29
+ source: Mapped["Source"] = relationship("Source", back_populates="files", lazy="selectin")
letta/orm/mixins.py CHANGED
@@ -29,3 +29,11 @@ class UserMixin(Base):
29
29
  __abstract__ = True
30
30
 
31
31
  user_id: Mapped[str] = mapped_column(String, ForeignKey("users.id"))
32
+
33
+
34
+ class SourceMixin(Base):
35
+ """Mixin for models (e.g. file) that belong to a source."""
36
+
37
+ __abstract__ = True
38
+
39
+ source_id: Mapped[str] = mapped_column(String, ForeignKey("sources.id"))
letta/orm/organization.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from typing import TYPE_CHECKING, List
2
2
 
3
- from sqlalchemy import String
4
3
  from sqlalchemy.orm import Mapped, mapped_column, relationship
5
4
 
5
+ from letta.orm.file import FileMetadata
6
6
  from letta.orm.sqlalchemy_base import SqlalchemyBase
7
7
  from letta.schemas.organization import Organization as PydanticOrganization
8
8
 
@@ -18,7 +18,6 @@ class Organization(SqlalchemyBase):
18
18
  __tablename__ = "organizations"
19
19
  __pydantic_model__ = PydanticOrganization
20
20
 
21
- id: Mapped[str] = mapped_column(String, primary_key=True)
22
21
  name: Mapped[str] = mapped_column(doc="The display name of the organization.")
23
22
 
24
23
  # relationships
@@ -26,6 +25,7 @@ class Organization(SqlalchemyBase):
26
25
  tools: Mapped[List["Tool"]] = relationship("Tool", back_populates="organization", cascade="all, delete-orphan")
27
26
  sources: Mapped[List["Source"]] = relationship("Source", back_populates="organization", cascade="all, delete-orphan")
28
27
  agents_tags: Mapped[List["AgentsTags"]] = relationship("AgentsTags", back_populates="organization", cascade="all, delete-orphan")
28
+ files: Mapped[List["FileMetadata"]] = relationship("FileMetadata", back_populates="organization", cascade="all, delete-orphan")
29
29
  # TODO: Map these relationships later when we actually make these models
30
30
  # below is just a suggestion
31
31
  # agents: Mapped[List["Agent"]] = relationship("Agent", back_populates="organization", cascade="all, delete-orphan")
letta/orm/source.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import TYPE_CHECKING, Optional
1
+ from typing import TYPE_CHECKING, List, Optional
2
2
 
3
3
  from sqlalchemy import JSON, TypeDecorator
4
4
  from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -47,4 +47,5 @@ class Source(SqlalchemyBase, OrganizationMixin):
47
47
 
48
48
  # relationships
49
49
  organization: Mapped["Organization"] = relationship("Organization", back_populates="sources")
50
+ files: Mapped[List["Source"]] = relationship("FileMetadata", back_populates="source", cascade="all, delete-orphan")
50
51
  # agents: Mapped[List["Agent"]] = relationship("Agent", secondary="sources_agents", back_populates="sources")
letta/orm/tool.py CHANGED
@@ -28,7 +28,6 @@ class Tool(SqlalchemyBase, OrganizationMixin):
28
28
  # An organization should not have multiple tools with the same name
29
29
  __table_args__ = (UniqueConstraint("name", "organization_id", name="uix_name_organization"),)
30
30
 
31
- id: Mapped[str] = mapped_column(String, primary_key=True)
32
31
  name: Mapped[str] = mapped_column(doc="The display name of the tool.")
33
32
  description: Mapped[Optional[str]] = mapped_column(nullable=True, doc="The description of the tool.")
34
33
  tags: Mapped[List] = mapped_column(JSON, doc="Metadata tags used to filter tools.")
letta/orm/user.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
- from sqlalchemy import String
4
3
  from sqlalchemy.orm import Mapped, mapped_column, relationship
5
4
 
6
5
  from letta.orm.mixins import OrganizationMixin
@@ -17,7 +16,6 @@ class User(SqlalchemyBase, OrganizationMixin):
17
16
  __tablename__ = "users"
18
17
  __pydantic_model__ = PydanticUser
19
18
 
20
- id: Mapped[str] = mapped_column(String, primary_key=True)
21
19
  name: Mapped[str] = mapped_column(nullable=False, doc="The display name of the user.")
22
20
 
23
21
  # relationships
letta/schemas/file.py CHANGED
@@ -4,7 +4,6 @@ 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
8
7
 
9
8
 
10
9
  class FileMetadataBase(LettaBase):
@@ -17,7 +16,7 @@ class FileMetadata(FileMetadataBase):
17
16
  """Representation of a single FileMetadata"""
18
17
 
19
18
  id: str = FileMetadataBase.generate_id_field()
20
- user_id: str = Field(description="The unique identifier of the user associated with the document.")
19
+ organization_id: Optional[str] = Field(None, description="The unique identifier of the organization associated with the document.")
21
20
  source_id: str = Field(..., description="The unique identifier of the source associated with the document.")
22
21
  file_name: Optional[str] = Field(None, description="The name of the file.")
23
22
  file_path: Optional[str] = Field(None, description="The path to the file.")
@@ -25,7 +24,8 @@ class FileMetadata(FileMetadataBase):
25
24
  file_size: Optional[int] = Field(None, description="The size of the file in bytes.")
26
25
  file_creation_date: Optional[str] = Field(None, description="The creation date of the file.")
27
26
  file_last_modified_date: Optional[str] = Field(None, description="The last modified date of the file.")
28
- created_at: datetime = Field(default_factory=get_utc_time, description="The creation date of this file metadata object.")
29
27
 
30
- class Config:
31
- extra = "allow"
28
+ # orm metadata, optional fields
29
+ created_at: Optional[datetime] = Field(default_factory=datetime.utcnow, description="The creation date of the file.")
30
+ updated_at: Optional[datetime] = Field(default_factory=datetime.utcnow, description="The update date of the file.")
31
+ is_deleted: bool = Field(False, description="Whether this file is deleted or not.")
@@ -198,11 +198,13 @@ def list_files_from_source(
198
198
  limit: int = Query(1000, description="Number of files to return"),
199
199
  cursor: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
200
200
  server: "SyncServer" = Depends(get_letta_server),
201
+ user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
201
202
  ):
202
203
  """
203
204
  List paginated files associated with a data source.
204
205
  """
205
- return server.list_files_from_source(source_id=source_id, limit=limit, cursor=cursor)
206
+ actor = server.get_user_or_default(user_id=user_id)
207
+ return server.source_manager.list_files(source_id=source_id, limit=limit, cursor=cursor, actor=actor)
206
208
 
207
209
 
208
210
  # it's redundant to include /delete in the URL path. The HTTP verb DELETE already implies that action.
@@ -219,7 +221,7 @@ def delete_file_from_source(
219
221
  """
220
222
  actor = server.get_user_or_default(user_id=user_id)
221
223
 
222
- deleted_file = server.delete_file_from_source(source_id=source_id, file_id=file_id, user_id=actor.id)
224
+ deleted_file = server.source_manager.delete_file(file_id=file_id, actor=actor)
223
225
  if deleted_file is None:
224
226
  raise HTTPException(status_code=404, detail=f"File with id={file_id} not found.")
225
227
 
letta/server/server.py CHANGED
@@ -65,7 +65,6 @@ from letta.schemas.embedding_config import EmbeddingConfig
65
65
 
66
66
  # openai schemas
67
67
  from letta.schemas.enums import JobStatus
68
- from letta.schemas.file import FileMetadata
69
68
  from letta.schemas.job import Job
70
69
  from letta.schemas.letta_message import LettaMessage
71
70
  from letta.schemas.llm_config import LLMConfig
@@ -1513,12 +1512,16 @@ class SyncServer(Server):
1513
1512
  if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
1514
1513
  raise ValueError(f"Agent agent_id={agent_id} does not exist")
1515
1514
 
1516
- # Verify that the agent exists and is owned by the user
1515
+ # Verify that the agent exists and belongs to the org of the user
1517
1516
  agent_state = self.ms.get_agent(agent_id=agent_id, user_id=user_id)
1518
1517
  if not agent_state:
1519
1518
  raise ValueError(f"Could not find agent_id={agent_id} under user_id={user_id}")
1520
- if agent_state.user_id != user_id:
1521
- raise ValueError(f"Could not authorize agent_id={agent_id} with user_id={user_id}")
1519
+
1520
+ agent_state_user = self.user_manager.get_user_by_id(user_id=agent_state.user_id)
1521
+ if agent_state_user.organization_id != actor.organization_id:
1522
+ raise ValueError(
1523
+ f"Could not authorize agent_id={agent_id} with user_id={user_id} because of differing organizations; agent_id was created in {agent_state_user.organization_id} while user belongs to {actor.organization_id}. How did they get the agent id?"
1524
+ )
1522
1525
 
1523
1526
  # First, if the agent is in the in-memory cache we should remove it
1524
1527
  # List of {'user_id': user_id, 'agent_id': agent_id, 'agent': agent_obj} dicts
@@ -1632,9 +1635,6 @@ class SyncServer(Server):
1632
1635
 
1633
1636
  return job
1634
1637
 
1635
- def delete_file_from_source(self, source_id: str, file_id: str, user_id: Optional[str]) -> Optional[FileMetadata]:
1636
- return self.ms.delete_file_from_source(source_id=source_id, file_id=file_id, user_id=user_id)
1637
-
1638
1638
  def load_data(
1639
1639
  self,
1640
1640
  user_id: str,
@@ -1652,10 +1652,9 @@ class SyncServer(Server):
1652
1652
 
1653
1653
  # get the data connectors
1654
1654
  passage_store = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
1655
- file_store = StorageConnector.get_storage_connector(TableType.FILES, self.config, user_id=user_id)
1656
1655
 
1657
1656
  # load data into the document store
1658
- passage_count, document_count = load_data(connector, source, passage_store, file_store)
1657
+ passage_count, document_count = load_data(connector, source, passage_store, self.source_manager, actor=user)
1659
1658
  return passage_count, document_count
1660
1659
 
1661
1660
  def attach_source_to_agent(
@@ -1674,7 +1673,6 @@ class SyncServer(Server):
1674
1673
  data_source = self.source_manager.get_source_by_name(source_name=source_name, actor=user)
1675
1674
  else:
1676
1675
  raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
1677
-
1678
1676
  # get connection to data source storage
1679
1677
  source_connector = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
1680
1678
 
@@ -1720,10 +1718,6 @@ class SyncServer(Server):
1720
1718
 
1721
1719
  return [self.source_manager.get_source_by_id(source_id=id) for id in source_ids]
1722
1720
 
1723
- def list_files_from_source(self, source_id: str, limit: int = 1000, cursor: Optional[str] = None) -> List[FileMetadata]:
1724
- # list all attached sources to an agent
1725
- return self.ms.list_files_from_source(source_id=source_id, limit=limit, cursor=cursor)
1726
-
1727
1721
  def list_data_source_passages(self, user_id: str, source_id: str) -> List[Passage]:
1728
1722
  warnings.warn("list_data_source_passages is not yet implemented, returning empty list.", category=UserWarning)
1729
1723
  return []
@@ -1,7 +1,9 @@
1
1
  from typing import List, Optional
2
2
 
3
3
  from letta.orm.errors import NoResultFound
4
+ from letta.orm.file import FileMetadata as FileMetadataModel
4
5
  from letta.orm.source import Source as SourceModel
6
+ from letta.schemas.file import FileMetadata as PydanticFileMetadata
5
7
  from letta.schemas.source import Source as PydanticSource
6
8
  from letta.schemas.source import SourceUpdate
7
9
  from letta.schemas.user import User as PydanticUser
@@ -98,3 +100,46 @@ class SourceManager:
98
100
  return None
99
101
  else:
100
102
  return sources[0].to_pydantic()
103
+
104
+ @enforce_types
105
+ def create_file(self, file_metadata: PydanticFileMetadata, actor: PydanticUser) -> PydanticFileMetadata:
106
+ """Create a new file based on the PydanticFileMetadata schema."""
107
+ db_file = self.get_file_by_id(file_metadata.id, actor=actor)
108
+ if db_file:
109
+ return db_file
110
+ else:
111
+ with self.session_maker() as session:
112
+ file_metadata.organization_id = actor.organization_id
113
+ file_metadata = FileMetadataModel(**file_metadata.model_dump(exclude_none=True))
114
+ file_metadata.create(session, actor=actor)
115
+ return file_metadata.to_pydantic()
116
+
117
+ # TODO: We make actor optional for now, but should most likely be enforced due to security reasons
118
+ @enforce_types
119
+ def get_file_by_id(self, file_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticFileMetadata]:
120
+ """Retrieve a file by its ID."""
121
+ with self.session_maker() as session:
122
+ try:
123
+ file = FileMetadataModel.read(db_session=session, identifier=file_id, actor=actor)
124
+ return file.to_pydantic()
125
+ except NoResultFound:
126
+ return None
127
+
128
+ @enforce_types
129
+ def list_files(
130
+ self, source_id: str, actor: PydanticUser, cursor: Optional[str] = None, limit: Optional[int] = 50
131
+ ) -> List[PydanticFileMetadata]:
132
+ """List all files with optional pagination."""
133
+ with self.session_maker() as session:
134
+ files = FileMetadataModel.list(
135
+ db_session=session, cursor=cursor, limit=limit, organization_id=actor.organization_id, source_id=source_id
136
+ )
137
+ return [file.to_pydantic() for file in files]
138
+
139
+ @enforce_types
140
+ def delete_file(self, file_id: str, actor: PydanticUser) -> PydanticFileMetadata:
141
+ """Delete a file by its ID."""
142
+ with self.session_maker() as session:
143
+ file = FileMetadataModel.read(db_session=session, identifier=file_id)
144
+ file.delete(db_session=session, actor=actor)
145
+ return file.to_pydantic()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.5.2.dev20241113104112
3
+ Version: 0.5.2.dev20241113234401
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -2,7 +2,7 @@ letta/__init__.py,sha256=IMLtpH5HlbVUa1mmPpSyBpTZqVz1rsS7lbuqT7viBQ0,1014
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
3
  letta/agent.py,sha256=mQxkYsG80P_KCzXN6EDmwi6tiXebabHpcYsgs7ugWP0,76952
4
4
  letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
5
- letta/agent_store/db.py,sha256=iBdP9IxvuenKRF7SVRhFlC8yvRwNn2xxOfBFvadBcNA,23383
5
+ letta/agent_store/db.py,sha256=9pIeakcRls0Fi3wO2b9Jg_Qw1IBJ-_GUSpE1z-upGS0,23425
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,13 +13,13 @@ letta/cli/cli.py,sha256=oREx2gmpFvw7CdmXjhzoj_4iypM27WAK5vePESIgcHo,16898
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=nnd0n4NWKDpBnt41LgcQYxfNuOGYQ2CuQ4SpHbKArZo,97883
16
+ letta/client/client.py,sha256=XR4VukYXnZ1LG5og_BP1_m_FWrjb6ESFFkT-gK6_-os,97885
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
20
20
  letta/constants.py,sha256=c8pEfIhtpqFGunyzGObnfEeRJNkunfmq9Pfiau8YYfA,6544
21
21
  letta/credentials.py,sha256=D9mlcPsdDWlIIXQQD8wSPE9M_QvsRrb0p3LB5i9OF5Q,5806
22
- letta/data_sources/connectors.py,sha256=gEZCpNwGPR5MCnAofmFmQMuLbiJ9I44HJ2rSNRGMqP4,9918
22
+ letta/data_sources/connectors.py,sha256=5VKxfeV-QyUlK1wexLlpgar99dGm6PHxFaEbSeByo_U,9923
23
23
  letta/data_sources/connectors_helper.py,sha256=2TQjCt74fCgT5sw1AP8PalDEk06jPBbhrPG4HVr-WLs,3371
24
24
  letta/embeddings.py,sha256=qPt8kB-wmuRIg1py7DHnQGJpw3DmQHJ505FJvc0K6Yk,8873
25
25
  letta/errors.py,sha256=cDOo4cSYL-LA0w0b0GdsxXd5k2I1LLOY8nhtXk9YqYs,2875
@@ -85,22 +85,23 @@ letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ft
85
85
  letta/log.py,sha256=Oy5b71AXfrnQShxI_4ULo5U3kmZJG01bXbP_64Nr4Fk,2105
86
86
  letta/main.py,sha256=h-qPQn_Ok5wf2cf54RFPfe8yp6sCmE-Kp9mBk_HZf7o,18797
87
87
  letta/memory.py,sha256=YupXOvzVJXH59RW4XWBrd7qMNEYaMbtWXCheKeWZwpU,17873
88
- letta/metadata.py,sha256=2bfM0cJk78Aw3j1aAbxld1mHwwhd8vrnFHQJj1Ts3LI,25750
88
+ letta/metadata.py,sha256=xgwOPX_KQUl8OGxql2VAqe3ksg8EBD5gbctEDnM4RPk,22860
89
89
  letta/o1_agent.py,sha256=LqATgTpkc02-nCH_F87EOvgxLjdjT9F07kdzj3zSdQg,3118
90
90
  letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
91
  letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
92
92
  letta/orm/__all__.py,sha256=2gh2MZTkA3Hw67VWVKK3JIStJOqTeLdpCvYSVYNeEDA,692
93
- letta/orm/__init__.py,sha256=mR6vCTKJk4QzmqIFQozBoGo95RPEy3og5a-jQNKOpzk,180
93
+ letta/orm/__init__.py,sha256=tp62sheGPGDmFHG1CBvYEnVenSj62cxWLaBMWK2NU80,220
94
94
  letta/orm/agents_tags.py,sha256=Qa7Yt9imL8xbGP57fflccAMy7Z32CQiU_7eZKSSPngc,1119
95
95
  letta/orm/base.py,sha256=K_LpNUURbsj44ycHbzvNXG_n8pBOjf1YvDaikIPDpQA,2716
96
96
  letta/orm/enums.py,sha256=KfHcFt_fR6GUmSlmfsa-TetvmuRxGESNve8MStRYW64,145
97
97
  letta/orm/errors.py,sha256=somsGtotFlb3SDM6tKdZ5TDGwEEP3ppx47ICAvNMnkg,225
98
- letta/orm/mixins.py,sha256=fW4oa1cUFbgVE46KSQlW_hwzsZSqEBSSV-U3xJC6fyw,749
99
- letta/orm/organization.py,sha256=_2HRrc1jCRNcZj-G5V70Xw_eEy21bBW7_f4c_6g_668,1712
100
- letta/orm/source.py,sha256=Vr6WkD26BbVeZMHmcJFY6h2upChDW5OFNZEnlGvl9Q4,1895
98
+ letta/orm/file.py,sha256=FtfZlJLXfac4ntaw3kC0N9VRoD255m8EK4p-pC2lcHk,1519
99
+ letta/orm/mixins.py,sha256=w8ALpGlytx2TDOOBBkqNgZTCimUOibijL7EKKKLJcM8,943
100
+ letta/orm/organization.py,sha256=wp58JWdv3OGjv4nYVEdoF5ps8BmP25Yxq5yfdFkf9_s,1792
101
+ letta/orm/source.py,sha256=Ib0XHCMt345RjBSC30A398rZ21W5mA4PXX00XNXyd24,2021
101
102
  letta/orm/sqlalchemy_base.py,sha256=QZ_b2jxNjXSvK-bJGxEHQiZqRn8tKNSuJRDorYCDCvE,7369
102
- letta/orm/tool.py,sha256=7FIeldPJTEOLA5ygasTOVXqUcm2aouYYARUOJXdaC4Y,2151
103
- letta/orm/user.py,sha256=-KNdpfnKRVJtSw0NrGRrVoSBBv7ASDchVempKtUuk-A,1229
103
+ letta/orm/tool.py,sha256=d0GclU_7qg8Z6ZE6kkH1kmrUAMCiV-ZM8BGaT1mnBU4,2089
104
+ letta/orm/user.py,sha256=bB4qGIT-ZoECZeeVqG-z3Z7WFXGqpC-GPcoYQoJZOuc,1137
104
105
  letta/persistence_manager.py,sha256=LlLgEDpSafCPAiyKmuq0NvVAnfBkZo6TWbGIKYQjQBs,5200
105
106
  letta/personas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
107
  letta/personas/examples/anna_pa.txt,sha256=zgiNdSNhy1HQy58cF_6RFPzcg2i37F9v38YuL1CW40A,1849
@@ -132,7 +133,7 @@ letta/schemas/api_key.py,sha256=u07yzzMn-hBAHZIIKbWY16KsgiFjSNR8lAghpMUo3_4,682
132
133
  letta/schemas/block.py,sha256=gc7NOZbv4ig_6ZgJ8-I875QoHxXcbcQIG94Cnwlqu8M,3999
133
134
  letta/schemas/embedding_config.py,sha256=1kD6NpiXeH4roVumxqDAKk7xt8SpXGWNhZs_XXUSlEU,2855
134
135
  letta/schemas/enums.py,sha256=WfRYpLh_pD-VyhEnp3Y6pPfx063zq2o4jky6PulqO8w,629
135
- letta/schemas/file.py,sha256=Ns0V2Jp6_lmiCmLCnXsOAh_UNqTW-fstcLKjYI35VOA,1357
136
+ letta/schemas/file.py,sha256=ChN2CWzLI2TT9WLItcfElEH0E8b7kzPylF2OQBr6Beg,1550
136
137
  letta/schemas/health.py,sha256=zT6mYovvD17iJRuu2rcaQQzbEEYrkwvAE9TB7iU824c,139
137
138
  letta/schemas/job.py,sha256=605TWjUdNy5Rgoc7en_DX3Giz-I7sVTXiSRZqxL__d8,1543
138
139
  letta/schemas/letta_base.py,sha256=VP6h6mV7u2AVSrcgzTIoptORv6BqmQXkkMzb7-NlcWs,3026
@@ -178,12 +179,12 @@ letta/server/rest_api/routers/v1/health.py,sha256=pKCuVESlVOhGIb4VC4K-H82eZqfghm
178
179
  letta/server/rest_api/routers/v1/jobs.py,sha256=a-j0v-5A0un0pVCOHpfeWnzpOWkVDQO6ti42k_qAlZY,2272
179
180
  letta/server/rest_api/routers/v1/llms.py,sha256=TcyvSx6MEM3je5F4DysL7ligmssL_pFlJaaO4uL95VY,877
180
181
  letta/server/rest_api/routers/v1/organizations.py,sha256=tyqVzXTpMtk3sKxI3Iz4aS6RhbGEbXDzFBB_CpW18v4,2080
181
- letta/server/rest_api/routers/v1/sources.py,sha256=u6THgcSbSx2r0Sszr0v_6NV1U9AeMUDzZj9n3_kIXr8,8809
182
+ letta/server/rest_api/routers/v1/sources.py,sha256=dSDgzt4LQPZxnZRFLHdvsFn_qoupbYvRhLIi8l1KVbs,8981
182
183
  letta/server/rest_api/routers/v1/tools.py,sha256=Bkb9oKswOycj5S3fBeim7LpDrZf37SybGwV6fyi3BFs,4296
183
184
  letta/server/rest_api/routers/v1/users.py,sha256=M1wEr2IyHzuRwINYxLXTkrbAH3osLe_cWjzrWrzR1aw,3729
184
185
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
185
186
  letta/server/rest_api/utils.py,sha256=GdHYCzXtbM5VCAYDPR0z5gnNZpRhwPld2BGZV7xT6cU,2924
186
- letta/server/server.py,sha256=Bk-FPS0aODYFckEAgr5aPWVVizbf2gfcESnRK0aWdfQ,80038
187
+ letta/server/server.py,sha256=RQHtJwhpCgnRSL38wF13vATDkS5thCAMbtRDwU2U-yM,79746
187
188
  letta/server/startup.sh,sha256=wTOQOJJZw_Iec57WIu0UW0AVflk0ZMWYZWg8D3T_gSQ,698
188
189
  letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
189
190
  letta/server/static_files/assets/index-9fa459a2.js,sha256=j2oMcDJO9dWJaH5e-tsflbVpWK20gLWpZKJk4-Kuy6A,1815592
@@ -199,7 +200,7 @@ letta/server/ws_api/server.py,sha256=C2Kv48PCwl46DQFb0ZP30s86KJLQ6dZk2AhWQEZn9pY
199
200
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
200
201
  letta/services/agents_tags_manager.py,sha256=zNqeXDpaf4dQ77jrRHiQfITdk4FawBzcND-9tWrj8gw,3127
201
202
  letta/services/organization_manager.py,sha256=OfE2_NMmhqXURX4sg7hCOiFQVQpV5ZiPu7J3sboCSYc,3555
202
- letta/services/source_manager.py,sha256=-02XYBsWa3Dt64Bq-anZ-US_b0XFlqrRyy3nNzlgoBs,4332
203
+ letta/services/source_manager.py,sha256=StX5Wfd7XSCKJet8qExIu3GMoI-eMIbEarAeTv2gq0s,6555
203
204
  letta/services/tool_manager.py,sha256=z3nnUDQWuqB5RYk_y78EvIH6SMx-KJy7qeHqclZHonw,7897
204
205
  letta/services/user_manager.py,sha256=UJa0hqCjz0yXtvrCR8OVBqlSR5lC_Ejn-uG__58zLds,4398
205
206
  letta/settings.py,sha256=yiYNmnYKj_BdTm0cBEIvQKYGU-lCmFntqsyVfRUy3_k,3411
@@ -207,8 +208,8 @@ letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,
207
208
  letta/streaming_utils.py,sha256=329fsvj1ZN0r0LpQtmMPZ2vSxkDBIUUwvGHZFkjm2I8,11745
208
209
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
209
210
  letta/utils.py,sha256=SXLEYhyp3gHyIjrxNIKNZZ5ittKo3KOj6zxgC_Trex0,31012
210
- letta_nightly-0.5.2.dev20241113104112.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
211
- letta_nightly-0.5.2.dev20241113104112.dist-info/METADATA,sha256=R-hEIbQCiq8wTmE3Qayy_unZscQ1_6jmhoIzwIGZx_g,11024
212
- letta_nightly-0.5.2.dev20241113104112.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
213
- letta_nightly-0.5.2.dev20241113104112.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
214
- letta_nightly-0.5.2.dev20241113104112.dist-info/RECORD,,
211
+ letta_nightly-0.5.2.dev20241113234401.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
212
+ letta_nightly-0.5.2.dev20241113234401.dist-info/METADATA,sha256=sydGYHKPa9zN-9NrBVfbVicoEGwk4EfIemafhLSxZxY,11024
213
+ letta_nightly-0.5.2.dev20241113234401.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
214
+ letta_nightly-0.5.2.dev20241113234401.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
215
+ letta_nightly-0.5.2.dev20241113234401.dist-info/RECORD,,