letta-nightly 0.5.2.dev20241113104112__py3-none-any.whl → 0.5.2.dev20241114104133__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 +2 -1
- letta/client/client.py +3 -3
- letta/data_sources/connectors.py +3 -8
- letta/metadata.py +0 -73
- letta/orm/__init__.py +1 -0
- letta/orm/file.py +29 -0
- letta/orm/mixins.py +8 -0
- letta/orm/organization.py +2 -2
- letta/orm/source.py +2 -1
- letta/orm/tool.py +0 -1
- letta/orm/user.py +0 -2
- letta/schemas/file.py +5 -5
- letta/server/rest_api/routers/v1/sources.py +4 -2
- letta/server/server.py +8 -14
- letta/services/source_manager.py +45 -0
- {letta_nightly-0.5.2.dev20241113104112.dist-info → letta_nightly-0.5.2.dev20241114104133.dist-info}/METADATA +1 -1
- {letta_nightly-0.5.2.dev20241113104112.dist-info → letta_nightly-0.5.2.dev20241114104133.dist-info}/RECORD +20 -19
- {letta_nightly-0.5.2.dev20241113104112.dist-info → letta_nightly-0.5.2.dev20241114104133.dist-info}/LICENSE +0 -0
- {letta_nightly-0.5.2.dev20241113104112.dist-info → letta_nightly-0.5.2.dev20241114104133.dist-info}/WHEEL +0 -0
- {letta_nightly-0.5.2.dev20241113104112.dist-info → letta_nightly-0.5.2.dev20241114104133.dist-info}/entry_points.txt +0 -0
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,
|
|
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,
|
|
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.
|
|
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.
|
|
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
|
"""
|
letta/data_sources/connectors.py
CHANGED
|
@@ -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
|
-
|
|
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
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
1521
|
-
|
|
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,
|
|
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 []
|
letta/services/source_manager.py
CHANGED
|
@@ -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()
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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/
|
|
99
|
-
letta/orm/
|
|
100
|
-
letta/orm/
|
|
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=
|
|
103
|
-
letta/orm/user.py,sha256
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
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.
|
|
211
|
-
letta_nightly-0.5.2.
|
|
212
|
-
letta_nightly-0.5.2.
|
|
213
|
-
letta_nightly-0.5.2.
|
|
214
|
-
letta_nightly-0.5.2.
|
|
211
|
+
letta_nightly-0.5.2.dev20241114104133.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
|
|
212
|
+
letta_nightly-0.5.2.dev20241114104133.dist-info/METADATA,sha256=Tc9AzeU1-JqTpOlFoXMFNmbWmPcpmityzgOB_hPZwFY,11024
|
|
213
|
+
letta_nightly-0.5.2.dev20241114104133.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
214
|
+
letta_nightly-0.5.2.dev20241114104133.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
|
|
215
|
+
letta_nightly-0.5.2.dev20241114104133.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|