letta-nightly 0.7.13.dev20250511104036__py3-none-any.whl → 0.7.14.dev20250513020711__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.
- letta/__init__.py +1 -1
- letta/agent.py +14 -17
- letta/agents/base_agent.py +112 -1
- letta/agents/letta_agent.py +35 -55
- letta/agents/letta_agent_batch.py +22 -45
- letta/agents/voice_agent.py +10 -42
- letta/functions/schema_generator.py +7 -3
- letta/llm_api/anthropic.py +4 -2
- letta/llm_api/openai.py +4 -2
- letta/orm/agents_tags.py +5 -2
- letta/orm/blocks_agents.py +3 -1
- letta/orm/sqlalchemy_base.py +91 -1
- letta/schemas/message.py +1 -1
- letta/serialize_schemas/marshmallow_agent.py +4 -4
- letta/server/db.py +180 -88
- letta/server/rest_api/app.py +6 -3
- letta/server/rest_api/chat_completions_interface.py +1 -0
- letta/server/rest_api/interface.py +54 -16
- letta/server/rest_api/routers/v1/sources.py +1 -0
- letta/server/server.py +1 -2
- letta/services/agent_manager.py +40 -31
- letta/services/block_manager.py +61 -34
- letta/services/group_manager.py +11 -15
- letta/services/identity_manager.py +9 -13
- letta/services/job_manager.py +12 -17
- letta/services/llm_batch_manager.py +17 -21
- letta/services/message_manager.py +53 -31
- letta/services/organization_manager.py +7 -14
- letta/services/passage_manager.py +6 -10
- letta/services/provider_manager.py +5 -9
- letta/services/sandbox_config_manager.py +13 -17
- letta/services/source_manager.py +13 -17
- letta/services/step_manager.py +5 -9
- letta/services/tool_manager.py +9 -14
- letta/services/user_manager.py +7 -12
- letta/settings.py +2 -0
- letta/streaming_interface.py +2 -0
- letta/utils.py +1 -1
- {letta_nightly-0.7.13.dev20250511104036.dist-info → letta_nightly-0.7.14.dev20250513020711.dist-info}/METADATA +2 -1
- {letta_nightly-0.7.13.dev20250511104036.dist-info → letta_nightly-0.7.14.dev20250513020711.dist-info}/RECORD +43 -43
- {letta_nightly-0.7.13.dev20250511104036.dist-info → letta_nightly-0.7.14.dev20250513020711.dist-info}/LICENSE +0 -0
- {letta_nightly-0.7.13.dev20250511104036.dist-info → letta_nightly-0.7.14.dev20250513020711.dist-info}/WHEEL +0 -0
- {letta_nightly-0.7.13.dev20250511104036.dist-info → letta_nightly-0.7.14.dev20250513020711.dist-info}/entry_points.txt +0 -0
@@ -12,6 +12,7 @@ from letta.schemas.letta_message import LettaMessageUpdateUnion
|
|
12
12
|
from letta.schemas.message import Message as PydanticMessage
|
13
13
|
from letta.schemas.message import MessageUpdate
|
14
14
|
from letta.schemas.user import User as PydanticUser
|
15
|
+
from letta.server.db import db_registry
|
15
16
|
from letta.utils import enforce_types
|
16
17
|
|
17
18
|
logger = get_logger(__name__)
|
@@ -20,15 +21,10 @@ logger = get_logger(__name__)
|
|
20
21
|
class MessageManager:
|
21
22
|
"""Manager class to handle business logic related to Messages."""
|
22
23
|
|
23
|
-
def __init__(self):
|
24
|
-
from letta.server.db import db_context
|
25
|
-
|
26
|
-
self.session_maker = db_context
|
27
|
-
|
28
24
|
@enforce_types
|
29
25
|
def get_message_by_id(self, message_id: str, actor: PydanticUser) -> Optional[PydanticMessage]:
|
30
26
|
"""Fetch a message by ID."""
|
31
|
-
with
|
27
|
+
with db_registry.session() as session:
|
32
28
|
try:
|
33
29
|
message = MessageModel.read(db_session=session, identifier=message_id, actor=actor)
|
34
30
|
return message.to_pydantic()
|
@@ -38,7 +34,7 @@ class MessageManager:
|
|
38
34
|
@enforce_types
|
39
35
|
def get_messages_by_ids(self, message_ids: List[str], actor: PydanticUser) -> List[PydanticMessage]:
|
40
36
|
"""Fetch messages by ID and return them in the requested order."""
|
41
|
-
with
|
37
|
+
with db_registry.session() as session:
|
42
38
|
results = MessageModel.list(db_session=session, id=message_ids, organization_id=actor.organization_id, limit=len(message_ids))
|
43
39
|
|
44
40
|
if len(results) != len(message_ids):
|
@@ -53,7 +49,7 @@ class MessageManager:
|
|
53
49
|
@enforce_types
|
54
50
|
def create_message(self, pydantic_msg: PydanticMessage, actor: PydanticUser) -> PydanticMessage:
|
55
51
|
"""Create a new message."""
|
56
|
-
with
|
52
|
+
with db_registry.session() as session:
|
57
53
|
# Set the organization id of the Pydantic message
|
58
54
|
pydantic_msg.organization_id = actor.organization_id
|
59
55
|
msg_data = pydantic_msg.model_dump(to_orm=True)
|
@@ -86,7 +82,7 @@ class MessageManager:
|
|
86
82
|
orm_messages.append(MessageModel(**msg_data))
|
87
83
|
|
88
84
|
# Use the batch_create method for efficient creation
|
89
|
-
with
|
85
|
+
with db_registry.session() as session:
|
90
86
|
created_messages = MessageModel.batch_create(orm_messages, session, actor=actor)
|
91
87
|
|
92
88
|
# Convert back to Pydantic models
|
@@ -173,7 +169,7 @@ class MessageManager:
|
|
173
169
|
"""
|
174
170
|
Updates an existing record in the database with values from the provided record object.
|
175
171
|
"""
|
176
|
-
with
|
172
|
+
with db_registry.session() as session:
|
177
173
|
# Fetch existing message from database
|
178
174
|
message = MessageModel.read(
|
179
175
|
db_session=session,
|
@@ -181,31 +177,57 @@ class MessageManager:
|
|
181
177
|
actor=actor,
|
182
178
|
)
|
183
179
|
|
184
|
-
|
185
|
-
if message_update.tool_calls and message.role != MessageRole.assistant:
|
186
|
-
raise ValueError(
|
187
|
-
f"Tool calls {message_update.tool_calls} can only be added to assistant messages. Message {message_id} has role {message.role}."
|
188
|
-
)
|
189
|
-
if message_update.tool_call_id and message.role != MessageRole.tool:
|
190
|
-
raise ValueError(
|
191
|
-
f"Tool call IDs {message_update.tool_call_id} can only be added to tool messages. Message {message_id} has role {message.role}."
|
192
|
-
)
|
193
|
-
|
194
|
-
# get update dictionary
|
195
|
-
update_data = message_update.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
196
|
-
# Remove redundant update fields
|
197
|
-
update_data = {key: value for key, value in update_data.items() if getattr(message, key) != value}
|
198
|
-
|
199
|
-
for key, value in update_data.items():
|
200
|
-
setattr(message, key, value)
|
180
|
+
message = self._update_message_by_id_impl(message_id, message_update, actor, message)
|
201
181
|
message.update(db_session=session, actor=actor)
|
182
|
+
return message.to_pydantic()
|
183
|
+
|
184
|
+
@enforce_types
|
185
|
+
async def update_message_by_id_async(self, message_id: str, message_update: MessageUpdate, actor: PydanticUser) -> PydanticMessage:
|
186
|
+
"""
|
187
|
+
Updates an existing record in the database with values from the provided record object.
|
188
|
+
Async version of the function above.
|
189
|
+
"""
|
190
|
+
async with db_registry.async_session() as session:
|
191
|
+
# Fetch existing message from database
|
192
|
+
message = await MessageModel.read_async(
|
193
|
+
db_session=session,
|
194
|
+
identifier=message_id,
|
195
|
+
actor=actor,
|
196
|
+
)
|
202
197
|
|
198
|
+
message = self._update_message_by_id_impl(message_id, message_update, actor, message)
|
199
|
+
await message.update_async(db_session=session, actor=actor)
|
203
200
|
return message.to_pydantic()
|
204
201
|
|
202
|
+
def _update_message_by_id_impl(
|
203
|
+
self, message_id: str, message_update: MessageUpdate, actor: PydanticUser, message: MessageModel
|
204
|
+
) -> MessageModel:
|
205
|
+
"""
|
206
|
+
Modifies the existing message object to update the database in the sync/async functions.
|
207
|
+
"""
|
208
|
+
# Some safety checks specific to messages
|
209
|
+
if message_update.tool_calls and message.role != MessageRole.assistant:
|
210
|
+
raise ValueError(
|
211
|
+
f"Tool calls {message_update.tool_calls} can only be added to assistant messages. Message {message_id} has role {message.role}."
|
212
|
+
)
|
213
|
+
if message_update.tool_call_id and message.role != MessageRole.tool:
|
214
|
+
raise ValueError(
|
215
|
+
f"Tool call IDs {message_update.tool_call_id} can only be added to tool messages. Message {message_id} has role {message.role}."
|
216
|
+
)
|
217
|
+
|
218
|
+
# get update dictionary
|
219
|
+
update_data = message_update.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
220
|
+
# Remove redundant update fields
|
221
|
+
update_data = {key: value for key, value in update_data.items() if getattr(message, key) != value}
|
222
|
+
|
223
|
+
for key, value in update_data.items():
|
224
|
+
setattr(message, key, value)
|
225
|
+
return message
|
226
|
+
|
205
227
|
@enforce_types
|
206
228
|
def delete_message_by_id(self, message_id: str, actor: PydanticUser) -> bool:
|
207
229
|
"""Delete a message."""
|
208
|
-
with
|
230
|
+
with db_registry.session() as session:
|
209
231
|
try:
|
210
232
|
msg = MessageModel.read(
|
211
233
|
db_session=session,
|
@@ -229,7 +251,7 @@ class MessageManager:
|
|
229
251
|
actor: The user requesting the count
|
230
252
|
role: The role of the message
|
231
253
|
"""
|
232
|
-
with
|
254
|
+
with db_registry.session() as session:
|
233
255
|
return MessageModel.size(db_session=session, actor=actor, role=role, agent_id=agent_id)
|
234
256
|
|
235
257
|
@enforce_types
|
@@ -293,7 +315,7 @@ class MessageManager:
|
|
293
315
|
NoResultFound: If the provided after/before message IDs do not exist.
|
294
316
|
"""
|
295
317
|
|
296
|
-
with
|
318
|
+
with db_registry.session() as session:
|
297
319
|
# Permission check: raise if the agent doesn't exist or actor is not allowed.
|
298
320
|
AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
|
299
321
|
|
@@ -363,7 +385,7 @@ class MessageManager:
|
|
363
385
|
Efficiently deletes all messages associated with a given agent_id,
|
364
386
|
while enforcing permission checks and avoiding any ORM‑level loads.
|
365
387
|
"""
|
366
|
-
with
|
388
|
+
with db_registry.session() as session:
|
367
389
|
# 1) verify the agent exists and the actor has access
|
368
390
|
AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
|
369
391
|
|
@@ -4,6 +4,7 @@ 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
6
|
from letta.schemas.organization import OrganizationUpdate
|
7
|
+
from letta.server.db import db_registry
|
7
8
|
from letta.utils import enforce_types
|
8
9
|
|
9
10
|
|
@@ -13,14 +14,6 @@ class OrganizationManager:
|
|
13
14
|
DEFAULT_ORG_ID = "org-00000000-0000-4000-8000-000000000000"
|
14
15
|
DEFAULT_ORG_NAME = "default_org"
|
15
16
|
|
16
|
-
def __init__(self):
|
17
|
-
# TODO: Please refactor this out
|
18
|
-
# I am currently working on a ORM refactor and would like to make a more minimal set of changes
|
19
|
-
# - Matt
|
20
|
-
from letta.server.db import db_context
|
21
|
-
|
22
|
-
self.session_maker = db_context
|
23
|
-
|
24
17
|
@enforce_types
|
25
18
|
def get_default_organization(self) -> PydanticOrganization:
|
26
19
|
"""Fetch the default organization."""
|
@@ -29,7 +22,7 @@ class OrganizationManager:
|
|
29
22
|
@enforce_types
|
30
23
|
def get_organization_by_id(self, org_id: str) -> Optional[PydanticOrganization]:
|
31
24
|
"""Fetch an organization by ID."""
|
32
|
-
with
|
25
|
+
with db_registry.session() as session:
|
33
26
|
organization = OrganizationModel.read(db_session=session, identifier=org_id)
|
34
27
|
return organization.to_pydantic()
|
35
28
|
|
@@ -44,7 +37,7 @@ class OrganizationManager:
|
|
44
37
|
|
45
38
|
@enforce_types
|
46
39
|
def _create_organization(self, pydantic_org: PydanticOrganization) -> PydanticOrganization:
|
47
|
-
with
|
40
|
+
with db_registry.session() as session:
|
48
41
|
org = OrganizationModel(**pydantic_org.model_dump(to_orm=True))
|
49
42
|
org.create(session)
|
50
43
|
return org.to_pydantic()
|
@@ -57,7 +50,7 @@ class OrganizationManager:
|
|
57
50
|
@enforce_types
|
58
51
|
def update_organization_name_using_id(self, org_id: str, name: Optional[str] = None) -> PydanticOrganization:
|
59
52
|
"""Update an organization."""
|
60
|
-
with
|
53
|
+
with db_registry.session() as session:
|
61
54
|
org = OrganizationModel.read(db_session=session, identifier=org_id)
|
62
55
|
if name:
|
63
56
|
org.name = name
|
@@ -67,7 +60,7 @@ class OrganizationManager:
|
|
67
60
|
@enforce_types
|
68
61
|
def update_organization(self, org_id: str, org_update: OrganizationUpdate) -> PydanticOrganization:
|
69
62
|
"""Update an organization."""
|
70
|
-
with
|
63
|
+
with db_registry.session() as session:
|
71
64
|
org = OrganizationModel.read(db_session=session, identifier=org_id)
|
72
65
|
if org_update.name:
|
73
66
|
org.name = org_update.name
|
@@ -79,14 +72,14 @@ class OrganizationManager:
|
|
79
72
|
@enforce_types
|
80
73
|
def delete_organization_by_id(self, org_id: str):
|
81
74
|
"""Delete an organization by marking it as deleted."""
|
82
|
-
with
|
75
|
+
with db_registry.session() as session:
|
83
76
|
organization = OrganizationModel.read(db_session=session, identifier=org_id)
|
84
77
|
organization.hard_delete(session)
|
85
78
|
|
86
79
|
@enforce_types
|
87
80
|
def list_organizations(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticOrganization]:
|
88
81
|
"""List all organizations with optional pagination."""
|
89
|
-
with
|
82
|
+
with db_registry.session() as session:
|
90
83
|
organizations = OrganizationModel.list(
|
91
84
|
db_session=session,
|
92
85
|
after=after,
|
@@ -10,21 +10,17 @@ from letta.orm.passage import AgentPassage, SourcePassage
|
|
10
10
|
from letta.schemas.agent import AgentState
|
11
11
|
from letta.schemas.passage import Passage as PydanticPassage
|
12
12
|
from letta.schemas.user import User as PydanticUser
|
13
|
+
from letta.server.db import db_registry
|
13
14
|
from letta.utils import enforce_types
|
14
15
|
|
15
16
|
|
16
17
|
class PassageManager:
|
17
18
|
"""Manager class to handle business logic related to Passages."""
|
18
19
|
|
19
|
-
def __init__(self):
|
20
|
-
from letta.server.db import db_context
|
21
|
-
|
22
|
-
self.session_maker = db_context
|
23
|
-
|
24
20
|
@enforce_types
|
25
21
|
def get_passage_by_id(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
26
22
|
"""Fetch a passage by ID."""
|
27
|
-
with
|
23
|
+
with db_registry.session() as session:
|
28
24
|
# Try source passages first
|
29
25
|
try:
|
30
26
|
passage = SourcePassage.read(db_session=session, identifier=passage_id, actor=actor)
|
@@ -69,7 +65,7 @@ class PassageManager:
|
|
69
65
|
else:
|
70
66
|
raise ValueError("Passage must have either agent_id or source_id")
|
71
67
|
|
72
|
-
with
|
68
|
+
with db_registry.session() as session:
|
73
69
|
passage.create(session, actor=actor)
|
74
70
|
return passage.to_pydantic()
|
75
71
|
|
@@ -145,7 +141,7 @@ class PassageManager:
|
|
145
141
|
if not passage_id:
|
146
142
|
raise ValueError("Passage ID must be provided.")
|
147
143
|
|
148
|
-
with
|
144
|
+
with db_registry.session() as session:
|
149
145
|
# Try source passages first
|
150
146
|
try:
|
151
147
|
curr_passage = SourcePassage.read(
|
@@ -179,7 +175,7 @@ class PassageManager:
|
|
179
175
|
if not passage_id:
|
180
176
|
raise ValueError("Passage ID must be provided.")
|
181
177
|
|
182
|
-
with
|
178
|
+
with db_registry.session() as session:
|
183
179
|
# Try source passages first
|
184
180
|
try:
|
185
181
|
passage = SourcePassage.read(db_session=session, identifier=passage_id, actor=actor)
|
@@ -217,7 +213,7 @@ class PassageManager:
|
|
217
213
|
actor: The user requesting the count
|
218
214
|
agent_id: The agent ID of the messages
|
219
215
|
"""
|
220
|
-
with
|
216
|
+
with db_registry.session() as session:
|
221
217
|
return AgentPassage.size(db_session=session, actor=actor, agent_id=agent_id)
|
222
218
|
|
223
219
|
def estimate_embeddings_size(
|
@@ -5,20 +5,16 @@ from letta.schemas.enums import ProviderCategory, ProviderType
|
|
5
5
|
from letta.schemas.providers import Provider as PydanticProvider
|
6
6
|
from letta.schemas.providers import ProviderCheck, ProviderCreate, ProviderUpdate
|
7
7
|
from letta.schemas.user import User as PydanticUser
|
8
|
+
from letta.server.db import db_registry
|
8
9
|
from letta.utils import enforce_types
|
9
10
|
|
10
11
|
|
11
12
|
class ProviderManager:
|
12
13
|
|
13
|
-
def __init__(self):
|
14
|
-
from letta.server.db import db_context
|
15
|
-
|
16
|
-
self.session_maker = db_context
|
17
|
-
|
18
14
|
@enforce_types
|
19
15
|
def create_provider(self, request: ProviderCreate, actor: PydanticUser) -> PydanticProvider:
|
20
16
|
"""Create a new provider if it doesn't already exist."""
|
21
|
-
with
|
17
|
+
with db_registry.session() as session:
|
22
18
|
provider_create_args = {**request.model_dump(), "provider_category": ProviderCategory.byok}
|
23
19
|
provider = PydanticProvider(**provider_create_args)
|
24
20
|
|
@@ -38,7 +34,7 @@ class ProviderManager:
|
|
38
34
|
@enforce_types
|
39
35
|
def update_provider(self, provider_id: str, provider_update: ProviderUpdate, actor: PydanticUser) -> PydanticProvider:
|
40
36
|
"""Update provider details."""
|
41
|
-
with
|
37
|
+
with db_registry.session() as session:
|
42
38
|
# Retrieve the existing provider by ID
|
43
39
|
existing_provider = ProviderModel.read(db_session=session, identifier=provider_id, actor=actor)
|
44
40
|
|
@@ -54,7 +50,7 @@ class ProviderManager:
|
|
54
50
|
@enforce_types
|
55
51
|
def delete_provider_by_id(self, provider_id: str, actor: PydanticUser):
|
56
52
|
"""Delete a provider."""
|
57
|
-
with
|
53
|
+
with db_registry.session() as session:
|
58
54
|
# Clear api key field
|
59
55
|
existing_provider = ProviderModel.read(db_session=session, identifier=provider_id, actor=actor)
|
60
56
|
existing_provider.api_key = None
|
@@ -80,7 +76,7 @@ class ProviderManager:
|
|
80
76
|
filter_kwargs["name"] = name
|
81
77
|
if provider_type:
|
82
78
|
filter_kwargs["provider_type"] = provider_type
|
83
|
-
with
|
79
|
+
with db_registry.session() as session:
|
84
80
|
providers = ProviderModel.list(
|
85
81
|
db_session=session,
|
86
82
|
after=after,
|
@@ -11,6 +11,7 @@ from letta.schemas.sandbox_config import LocalSandboxConfig
|
|
11
11
|
from letta.schemas.sandbox_config import SandboxConfig as PydanticSandboxConfig
|
12
12
|
from letta.schemas.sandbox_config import SandboxConfigCreate, SandboxConfigUpdate, SandboxType
|
13
13
|
from letta.schemas.user import User as PydanticUser
|
14
|
+
from letta.server.db import db_registry
|
14
15
|
from letta.utils import enforce_types, printd
|
15
16
|
|
16
17
|
logger = get_logger(__name__)
|
@@ -19,11 +20,6 @@ logger = get_logger(__name__)
|
|
19
20
|
class SandboxConfigManager:
|
20
21
|
"""Manager class to handle business logic related to SandboxConfig and SandboxEnvironmentVariable."""
|
21
22
|
|
22
|
-
def __init__(self):
|
23
|
-
from letta.server.db import db_context
|
24
|
-
|
25
|
-
self.session_maker = db_context
|
26
|
-
|
27
23
|
@enforce_types
|
28
24
|
def get_or_create_default_sandbox_config(self, sandbox_type: SandboxType, actor: PydanticUser) -> PydanticSandboxConfig:
|
29
25
|
sandbox_config = self.get_sandbox_config_by_type(sandbox_type, actor=actor)
|
@@ -69,7 +65,7 @@ class SandboxConfigManager:
|
|
69
65
|
return db_sandbox
|
70
66
|
else:
|
71
67
|
# If the sandbox configuration doesn't exist, create a new one
|
72
|
-
with
|
68
|
+
with db_registry.session() as session:
|
73
69
|
db_sandbox = SandboxConfigModel(**sandbox_config.model_dump(exclude_none=True))
|
74
70
|
db_sandbox.create(session, actor=actor)
|
75
71
|
return db_sandbox.to_pydantic()
|
@@ -79,7 +75,7 @@ class SandboxConfigManager:
|
|
79
75
|
self, sandbox_config_id: str, sandbox_update: SandboxConfigUpdate, actor: PydanticUser
|
80
76
|
) -> PydanticSandboxConfig:
|
81
77
|
"""Update an existing sandbox configuration."""
|
82
|
-
with
|
78
|
+
with db_registry.session() as session:
|
83
79
|
sandbox = SandboxConfigModel.read(db_session=session, identifier=sandbox_config_id, actor=actor)
|
84
80
|
# We need to check that the sandbox_update provided is the same type as the original sandbox
|
85
81
|
if sandbox.type != sandbox_update.config.type:
|
@@ -104,7 +100,7 @@ class SandboxConfigManager:
|
|
104
100
|
@enforce_types
|
105
101
|
def delete_sandbox_config(self, sandbox_config_id: str, actor: PydanticUser) -> PydanticSandboxConfig:
|
106
102
|
"""Delete a sandbox configuration by its ID."""
|
107
|
-
with
|
103
|
+
with db_registry.session() as session:
|
108
104
|
sandbox = SandboxConfigModel.read(db_session=session, identifier=sandbox_config_id, actor=actor)
|
109
105
|
sandbox.hard_delete(db_session=session, actor=actor)
|
110
106
|
return sandbox.to_pydantic()
|
@@ -122,14 +118,14 @@ class SandboxConfigManager:
|
|
122
118
|
if sandbox_type:
|
123
119
|
kwargs.update({"type": sandbox_type})
|
124
120
|
|
125
|
-
with
|
121
|
+
with db_registry.session() as session:
|
126
122
|
sandboxes = SandboxConfigModel.list(db_session=session, after=after, limit=limit, **kwargs)
|
127
123
|
return [sandbox.to_pydantic() for sandbox in sandboxes]
|
128
124
|
|
129
125
|
@enforce_types
|
130
126
|
def get_sandbox_config_by_id(self, sandbox_config_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticSandboxConfig]:
|
131
127
|
"""Retrieve a sandbox configuration by its ID."""
|
132
|
-
with
|
128
|
+
with db_registry.session() as session:
|
133
129
|
try:
|
134
130
|
sandbox = SandboxConfigModel.read(db_session=session, identifier=sandbox_config_id, actor=actor)
|
135
131
|
return sandbox.to_pydantic()
|
@@ -139,7 +135,7 @@ class SandboxConfigManager:
|
|
139
135
|
@enforce_types
|
140
136
|
def get_sandbox_config_by_type(self, type: SandboxType, actor: Optional[PydanticUser] = None) -> Optional[PydanticSandboxConfig]:
|
141
137
|
"""Retrieve a sandbox config by its type."""
|
142
|
-
with
|
138
|
+
with db_registry.session() as session:
|
143
139
|
try:
|
144
140
|
sandboxes = SandboxConfigModel.list(
|
145
141
|
db_session=session,
|
@@ -175,7 +171,7 @@ class SandboxConfigManager:
|
|
175
171
|
|
176
172
|
return db_env_var
|
177
173
|
else:
|
178
|
-
with
|
174
|
+
with db_registry.session() as session:
|
179
175
|
env_var = SandboxEnvVarModel(**env_var.model_dump(to_orm=True, exclude_none=True))
|
180
176
|
env_var.create(session, actor=actor)
|
181
177
|
return env_var.to_pydantic()
|
@@ -185,7 +181,7 @@ class SandboxConfigManager:
|
|
185
181
|
self, env_var_id: str, env_var_update: SandboxEnvironmentVariableUpdate, actor: PydanticUser
|
186
182
|
) -> PydanticEnvVar:
|
187
183
|
"""Update an existing sandbox environment variable."""
|
188
|
-
with
|
184
|
+
with db_registry.session() as session:
|
189
185
|
env_var = SandboxEnvVarModel.read(db_session=session, identifier=env_var_id, actor=actor)
|
190
186
|
update_data = env_var_update.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
191
187
|
update_data = {key: value for key, value in update_data.items() if getattr(env_var, key) != value}
|
@@ -204,7 +200,7 @@ class SandboxConfigManager:
|
|
204
200
|
@enforce_types
|
205
201
|
def delete_sandbox_env_var(self, env_var_id: str, actor: PydanticUser) -> PydanticEnvVar:
|
206
202
|
"""Delete a sandbox environment variable by its ID."""
|
207
|
-
with
|
203
|
+
with db_registry.session() as session:
|
208
204
|
env_var = SandboxEnvVarModel.read(db_session=session, identifier=env_var_id, actor=actor)
|
209
205
|
env_var.hard_delete(db_session=session, actor=actor)
|
210
206
|
return env_var.to_pydantic()
|
@@ -218,7 +214,7 @@ class SandboxConfigManager:
|
|
218
214
|
limit: Optional[int] = 50,
|
219
215
|
) -> List[PydanticEnvVar]:
|
220
216
|
"""List all sandbox environment variables with optional pagination."""
|
221
|
-
with
|
217
|
+
with db_registry.session() as session:
|
222
218
|
env_vars = SandboxEnvVarModel.list(
|
223
219
|
db_session=session,
|
224
220
|
after=after,
|
@@ -233,7 +229,7 @@ class SandboxConfigManager:
|
|
233
229
|
self, key: str, actor: PydanticUser, after: Optional[str] = None, limit: Optional[int] = 50
|
234
230
|
) -> List[PydanticEnvVar]:
|
235
231
|
"""List all sandbox environment variables with optional pagination."""
|
236
|
-
with
|
232
|
+
with db_registry.session() as session:
|
237
233
|
env_vars = SandboxEnvVarModel.list(
|
238
234
|
db_session=session,
|
239
235
|
after=after,
|
@@ -258,7 +254,7 @@ class SandboxConfigManager:
|
|
258
254
|
self, key: str, sandbox_config_id: str, actor: Optional[PydanticUser] = None
|
259
255
|
) -> Optional[PydanticEnvVar]:
|
260
256
|
"""Retrieve a sandbox environment variable by its key and sandbox_config_id."""
|
261
|
-
with
|
257
|
+
with db_registry.session() as session:
|
262
258
|
try:
|
263
259
|
env_var = SandboxEnvVarModel.list(
|
264
260
|
db_session=session,
|
letta/services/source_manager.py
CHANGED
@@ -8,17 +8,13 @@ from letta.schemas.file import FileMetadata as PydanticFileMetadata
|
|
8
8
|
from letta.schemas.source import Source as PydanticSource
|
9
9
|
from letta.schemas.source import SourceUpdate
|
10
10
|
from letta.schemas.user import User as PydanticUser
|
11
|
+
from letta.server.db import db_registry
|
11
12
|
from letta.utils import enforce_types, printd
|
12
13
|
|
13
14
|
|
14
15
|
class SourceManager:
|
15
16
|
"""Manager class to handle business logic related to Sources."""
|
16
17
|
|
17
|
-
def __init__(self):
|
18
|
-
from letta.server.db import db_context
|
19
|
-
|
20
|
-
self.session_maker = db_context
|
21
|
-
|
22
18
|
@enforce_types
|
23
19
|
def create_source(self, source: PydanticSource, actor: PydanticUser) -> PydanticSource:
|
24
20
|
"""Create a new source based on the PydanticSource schema."""
|
@@ -27,7 +23,7 @@ class SourceManager:
|
|
27
23
|
if db_source:
|
28
24
|
return db_source
|
29
25
|
else:
|
30
|
-
with
|
26
|
+
with db_registry.session() as session:
|
31
27
|
# Provide default embedding config if not given
|
32
28
|
source.organization_id = actor.organization_id
|
33
29
|
source = SourceModel(**source.model_dump(to_orm=True, exclude_none=True))
|
@@ -37,7 +33,7 @@ class SourceManager:
|
|
37
33
|
@enforce_types
|
38
34
|
def update_source(self, source_id: str, source_update: SourceUpdate, actor: PydanticUser) -> PydanticSource:
|
39
35
|
"""Update a source by its ID with the given SourceUpdate object."""
|
40
|
-
with
|
36
|
+
with db_registry.session() as session:
|
41
37
|
source = SourceModel.read(db_session=session, identifier=source_id, actor=actor)
|
42
38
|
|
43
39
|
# get update dictionary
|
@@ -59,7 +55,7 @@ class SourceManager:
|
|
59
55
|
@enforce_types
|
60
56
|
def delete_source(self, source_id: str, actor: PydanticUser) -> PydanticSource:
|
61
57
|
"""Delete a source by its ID."""
|
62
|
-
with
|
58
|
+
with db_registry.session() as session:
|
63
59
|
source = SourceModel.read(db_session=session, identifier=source_id)
|
64
60
|
source.hard_delete(db_session=session, actor=actor)
|
65
61
|
return source.to_pydantic()
|
@@ -67,7 +63,7 @@ class SourceManager:
|
|
67
63
|
@enforce_types
|
68
64
|
def list_sources(self, actor: PydanticUser, after: Optional[str] = None, limit: Optional[int] = 50, **kwargs) -> List[PydanticSource]:
|
69
65
|
"""List all sources with optional pagination."""
|
70
|
-
with
|
66
|
+
with db_registry.session() as session:
|
71
67
|
sources = SourceModel.list(
|
72
68
|
db_session=session,
|
73
69
|
after=after,
|
@@ -85,7 +81,7 @@ class SourceManager:
|
|
85
81
|
"""
|
86
82
|
Get the total count of sources for the given user.
|
87
83
|
"""
|
88
|
-
with
|
84
|
+
with db_registry.session() as session:
|
89
85
|
return SourceModel.size(db_session=session, actor=actor)
|
90
86
|
|
91
87
|
@enforce_types
|
@@ -100,7 +96,7 @@ class SourceManager:
|
|
100
96
|
Returns:
|
101
97
|
List[PydanticAgentState]: List of agents that have this source attached
|
102
98
|
"""
|
103
|
-
with
|
99
|
+
with db_registry.session() as session:
|
104
100
|
# Verify source exists and user has permission to access it
|
105
101
|
source = SourceModel.read(db_session=session, identifier=source_id, actor=actor)
|
106
102
|
|
@@ -112,7 +108,7 @@ class SourceManager:
|
|
112
108
|
@enforce_types
|
113
109
|
def get_source_by_id(self, source_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticSource]:
|
114
110
|
"""Retrieve a source by its ID."""
|
115
|
-
with
|
111
|
+
with db_registry.session() as session:
|
116
112
|
try:
|
117
113
|
source = SourceModel.read(db_session=session, identifier=source_id, actor=actor)
|
118
114
|
return source.to_pydantic()
|
@@ -122,7 +118,7 @@ class SourceManager:
|
|
122
118
|
@enforce_types
|
123
119
|
def get_source_by_name(self, source_name: str, actor: PydanticUser) -> Optional[PydanticSource]:
|
124
120
|
"""Retrieve a source by its name."""
|
125
|
-
with
|
121
|
+
with db_registry.session() as session:
|
126
122
|
sources = SourceModel.list(
|
127
123
|
db_session=session,
|
128
124
|
name=source_name,
|
@@ -141,7 +137,7 @@ class SourceManager:
|
|
141
137
|
if db_file:
|
142
138
|
return db_file
|
143
139
|
else:
|
144
|
-
with
|
140
|
+
with db_registry.session() as session:
|
145
141
|
file_metadata.organization_id = actor.organization_id
|
146
142
|
file_metadata = FileMetadataModel(**file_metadata.model_dump(to_orm=True, exclude_none=True))
|
147
143
|
file_metadata.create(session, actor=actor)
|
@@ -151,7 +147,7 @@ class SourceManager:
|
|
151
147
|
@enforce_types
|
152
148
|
def get_file_by_id(self, file_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticFileMetadata]:
|
153
149
|
"""Retrieve a file by its ID."""
|
154
|
-
with
|
150
|
+
with db_registry.session() as session:
|
155
151
|
try:
|
156
152
|
file = FileMetadataModel.read(db_session=session, identifier=file_id, actor=actor)
|
157
153
|
return file.to_pydantic()
|
@@ -163,7 +159,7 @@ class SourceManager:
|
|
163
159
|
self, source_id: str, actor: PydanticUser, after: Optional[str] = None, limit: Optional[int] = 50
|
164
160
|
) -> List[PydanticFileMetadata]:
|
165
161
|
"""List all files with optional pagination."""
|
166
|
-
with
|
162
|
+
with db_registry.session() as session:
|
167
163
|
files = FileMetadataModel.list(
|
168
164
|
db_session=session, after=after, limit=limit, organization_id=actor.organization_id, source_id=source_id
|
169
165
|
)
|
@@ -172,7 +168,7 @@ class SourceManager:
|
|
172
168
|
@enforce_types
|
173
169
|
def delete_file(self, file_id: str, actor: PydanticUser) -> PydanticFileMetadata:
|
174
170
|
"""Delete a file by its ID."""
|
175
|
-
with
|
171
|
+
with db_registry.session() as session:
|
176
172
|
file = FileMetadataModel.read(db_session=session, identifier=file_id)
|
177
173
|
file.hard_delete(db_session=session, actor=actor)
|
178
174
|
return file.to_pydantic()
|
letta/services/step_manager.py
CHANGED
@@ -11,17 +11,13 @@ from letta.orm.step import Step as StepModel
|
|
11
11
|
from letta.schemas.openai.chat_completion_response import UsageStatistics
|
12
12
|
from letta.schemas.step import Step as PydanticStep
|
13
13
|
from letta.schemas.user import User as PydanticUser
|
14
|
+
from letta.server.db import db_registry
|
14
15
|
from letta.tracing import get_trace_id
|
15
16
|
from letta.utils import enforce_types
|
16
17
|
|
17
18
|
|
18
19
|
class StepManager:
|
19
20
|
|
20
|
-
def __init__(self):
|
21
|
-
from letta.server.db import db_context
|
22
|
-
|
23
|
-
self.session_maker = db_context
|
24
|
-
|
25
21
|
@enforce_types
|
26
22
|
def list_steps(
|
27
23
|
self,
|
@@ -36,7 +32,7 @@ class StepManager:
|
|
36
32
|
agent_id: Optional[str] = None,
|
37
33
|
) -> List[PydanticStep]:
|
38
34
|
"""List all jobs with optional pagination and status filter."""
|
39
|
-
with
|
35
|
+
with db_registry.session() as session:
|
40
36
|
filter_kwargs = {"organization_id": actor.organization_id}
|
41
37
|
if model:
|
42
38
|
filter_kwargs["model"] = model
|
@@ -85,7 +81,7 @@ class StepManager:
|
|
85
81
|
"tid": None,
|
86
82
|
"trace_id": get_trace_id(), # Get the current trace ID
|
87
83
|
}
|
88
|
-
with
|
84
|
+
with db_registry.session() as session:
|
89
85
|
if job_id:
|
90
86
|
self._verify_job_access(session, job_id, actor, access=["write"])
|
91
87
|
new_step = StepModel(**step_data)
|
@@ -94,7 +90,7 @@ class StepManager:
|
|
94
90
|
|
95
91
|
@enforce_types
|
96
92
|
def get_step(self, step_id: str, actor: PydanticUser) -> PydanticStep:
|
97
|
-
with
|
93
|
+
with db_registry.session() as session:
|
98
94
|
step = StepModel.read(db_session=session, identifier=step_id, actor=actor)
|
99
95
|
return step.to_pydantic()
|
100
96
|
|
@@ -113,7 +109,7 @@ class StepManager:
|
|
113
109
|
Raises:
|
114
110
|
NoResultFound: If the step does not exist
|
115
111
|
"""
|
116
|
-
with
|
112
|
+
with db_registry.session() as session:
|
117
113
|
step = session.get(StepModel, step_id)
|
118
114
|
if not step:
|
119
115
|
raise NoResultFound(f"Step with id {step_id} does not exist")
|