letta-nightly 0.6.35.dev20250304104154__py3-none-any.whl → 0.6.36.dev20250305002337__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/__init__.py +1 -1
- letta/client/client.py +11 -0
- letta/constants.py +4 -1
- letta/log.py +1 -1
- letta/schemas/letta_base.py +3 -3
- letta/schemas/providers.py +1 -1
- letta/schemas/sandbox_config.py +2 -1
- letta/serialize_schemas/agent.py +45 -12
- letta/serialize_schemas/base.py +41 -0
- letta/serialize_schemas/block.py +15 -0
- letta/serialize_schemas/message.py +15 -1
- letta/serialize_schemas/tool.py +15 -0
- letta/server/rest_api/routers/v1/identities.py +17 -4
- letta/server/startup.sh +9 -0
- letta/services/agent_manager.py +41 -9
- letta/services/message_manager.py +1 -1
- letta/services/sandbox_config_manager.py +2 -2
- {letta_nightly-0.6.35.dev20250304104154.dist-info → letta_nightly-0.6.36.dev20250305002337.dist-info}/METADATA +1 -1
- {letta_nightly-0.6.35.dev20250304104154.dist-info → letta_nightly-0.6.36.dev20250305002337.dist-info}/RECORD +22 -20
- {letta_nightly-0.6.35.dev20250304104154.dist-info → letta_nightly-0.6.36.dev20250305002337.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.35.dev20250304104154.dist-info → letta_nightly-0.6.36.dev20250305002337.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.35.dev20250304104154.dist-info → letta_nightly-0.6.36.dev20250305002337.dist-info}/entry_points.txt +0 -0
letta/__init__.py
CHANGED
letta/client/client.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import sys
|
|
2
3
|
import time
|
|
3
4
|
from typing import Callable, Dict, Generator, List, Optional, Union
|
|
4
5
|
|
|
@@ -40,6 +41,16 @@ from letta.schemas.tool_rule import BaseToolRule
|
|
|
40
41
|
from letta.server.rest_api.interface import QueuingInterface
|
|
41
42
|
from letta.utils import get_human_text, get_persona_text
|
|
42
43
|
|
|
44
|
+
# Print deprecation notice in yellow when module is imported
|
|
45
|
+
print(
|
|
46
|
+
"\n\n\033[93m"
|
|
47
|
+
+ "DEPRECATION WARNING: This legacy Python client has been deprecated and will be removed in a future release.\n"
|
|
48
|
+
+ "Please migrate to the new official python SDK by running: pip install letta-client\n"
|
|
49
|
+
+ "For further documentation, visit: https://docs.letta.com/api-reference/overview#python-sdk"
|
|
50
|
+
+ "\033[0m\n\n",
|
|
51
|
+
file=sys.stderr,
|
|
52
|
+
)
|
|
53
|
+
|
|
43
54
|
|
|
44
55
|
def create_client(base_url: Optional[str] = None, token: Optional[str] = None):
|
|
45
56
|
if base_url is None:
|
letta/constants.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
from logging import CRITICAL, DEBUG, ERROR, INFO, NOTSET, WARN, WARNING
|
|
3
3
|
|
|
4
4
|
LETTA_DIR = os.path.join(os.path.expanduser("~"), ".letta")
|
|
5
|
-
|
|
5
|
+
LETTA_TOOL_EXECUTION_DIR = os.path.join(LETTA_DIR, "tool_execution_dir")
|
|
6
6
|
|
|
7
7
|
ADMIN_PREFIX = "/v1/admin"
|
|
8
8
|
API_PREFIX = "/v1"
|
|
@@ -146,6 +146,9 @@ MESSAGE_SUMMARY_WARNING_STR = " ".join(
|
|
|
146
146
|
# "Remember to pass request_heartbeat = true if you would like to send a message immediately after.",
|
|
147
147
|
]
|
|
148
148
|
)
|
|
149
|
+
DATA_SOURCE_ATTACH_ALERT = (
|
|
150
|
+
"[ALERT] New data was just uploaded to archival memory. You can view this data by calling the archival_memory_search tool."
|
|
151
|
+
)
|
|
149
152
|
|
|
150
153
|
# The ackknowledgement message used in the summarize sequence
|
|
151
154
|
MESSAGE_SUMMARY_REQUEST_ACK = "Understood, I will respond with a summary of the message (and only the summary, nothing else) once I receive the conversation history. I'm ready."
|
letta/log.py
CHANGED
letta/schemas/letta_base.py
CHANGED
|
@@ -38,15 +38,15 @@ class LettaBase(BaseModel):
|
|
|
38
38
|
description=cls._id_description(prefix),
|
|
39
39
|
pattern=cls._id_regex_pattern(prefix),
|
|
40
40
|
examples=[cls._id_example(prefix)],
|
|
41
|
-
default_factory=cls.
|
|
41
|
+
default_factory=cls.generate_id,
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
45
|
-
def
|
|
45
|
+
def generate_id(cls, prefix: Optional[str] = None) -> str:
|
|
46
46
|
prefix = prefix or cls.__id_prefix__
|
|
47
47
|
return f"{prefix}-{uuid.uuid4()}"
|
|
48
48
|
|
|
49
|
-
# def
|
|
49
|
+
# def generate_id(self) -> str:
|
|
50
50
|
# return f"{self.__id_prefix__}-{uuid.uuid4()}"
|
|
51
51
|
|
|
52
52
|
@classmethod
|
letta/schemas/providers.py
CHANGED
|
@@ -27,7 +27,7 @@ class Provider(ProviderBase):
|
|
|
27
27
|
|
|
28
28
|
def resolve_identifier(self):
|
|
29
29
|
if not self.id:
|
|
30
|
-
self.id = ProviderBase.
|
|
30
|
+
self.id = ProviderBase.generate_id(prefix=ProviderBase.__id_prefix__)
|
|
31
31
|
|
|
32
32
|
def list_llm_models(self) -> List[LLMConfig]:
|
|
33
33
|
return []
|
letta/schemas/sandbox_config.py
CHANGED
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Literal, Optional, Union
|
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel, Field, model_validator
|
|
8
8
|
|
|
9
|
+
from letta.constants import LETTA_TOOL_EXECUTION_DIR
|
|
9
10
|
from letta.schemas.agent import AgentState
|
|
10
11
|
from letta.schemas.letta_base import LettaBase, OrmMetadataBase
|
|
11
12
|
from letta.settings import tool_settings
|
|
@@ -71,7 +72,7 @@ class LocalSandboxConfig(BaseModel):
|
|
|
71
72
|
if tool_settings.local_sandbox_dir:
|
|
72
73
|
data["sandbox_dir"] = tool_settings.local_sandbox_dir
|
|
73
74
|
else:
|
|
74
|
-
data["sandbox_dir"] =
|
|
75
|
+
data["sandbox_dir"] = LETTA_TOOL_EXECUTION_DIR
|
|
75
76
|
|
|
76
77
|
return data
|
|
77
78
|
|
letta/serialize_schemas/agent.py
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
from
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from marshmallow import fields, post_dump
|
|
2
4
|
|
|
3
5
|
from letta.orm import Agent
|
|
6
|
+
from letta.schemas.agent import AgentState as PydanticAgentState
|
|
7
|
+
from letta.schemas.user import User
|
|
4
8
|
from letta.serialize_schemas.base import BaseSchema
|
|
9
|
+
from letta.serialize_schemas.block import SerializedBlockSchema
|
|
5
10
|
from letta.serialize_schemas.custom_fields import EmbeddingConfigField, LLMConfigField, ToolRulesField
|
|
6
11
|
from letta.serialize_schemas.message import SerializedMessageSchema
|
|
12
|
+
from letta.serialize_schemas.tool import SerializedToolSchema
|
|
13
|
+
from letta.server.db import SessionLocal
|
|
7
14
|
|
|
8
15
|
|
|
9
16
|
class SerializedAgentSchema(BaseSchema):
|
|
@@ -12,25 +19,51 @@ class SerializedAgentSchema(BaseSchema):
|
|
|
12
19
|
Excludes relational fields.
|
|
13
20
|
"""
|
|
14
21
|
|
|
22
|
+
__pydantic_model__ = PydanticAgentState
|
|
23
|
+
|
|
15
24
|
llm_config = LLMConfigField()
|
|
16
25
|
embedding_config = EmbeddingConfigField()
|
|
17
26
|
tool_rules = ToolRulesField()
|
|
18
27
|
|
|
19
28
|
messages = fields.List(fields.Nested(SerializedMessageSchema))
|
|
29
|
+
core_memory = fields.List(fields.Nested(SerializedBlockSchema))
|
|
30
|
+
tools = fields.List(fields.Nested(SerializedToolSchema))
|
|
31
|
+
|
|
32
|
+
def __init__(self, *args, session: SessionLocal, actor: User, **kwargs):
|
|
33
|
+
super().__init__(*args, actor=actor, **kwargs)
|
|
34
|
+
self.session = session
|
|
35
|
+
|
|
36
|
+
# Propagate session and actor to nested schemas automatically
|
|
37
|
+
for field in self.fields.values():
|
|
38
|
+
if isinstance(field, fields.List) and isinstance(field.inner, fields.Nested):
|
|
39
|
+
field.inner.schema.session = session
|
|
40
|
+
field.inner.schema.actor = actor
|
|
41
|
+
elif isinstance(field, fields.Nested):
|
|
42
|
+
field.schema.session = session
|
|
43
|
+
field.schema.actor = actor
|
|
44
|
+
|
|
45
|
+
@post_dump
|
|
46
|
+
def sanitize_ids(self, data: Dict, **kwargs):
|
|
47
|
+
data = super().sanitize_ids(data, **kwargs)
|
|
48
|
+
|
|
49
|
+
# Remap IDs of messages
|
|
50
|
+
# Need to do this in post, so we can correctly map the in-context message IDs
|
|
51
|
+
# TODO: Remap message_ids to reference objects, not just be a list
|
|
52
|
+
id_remapping = dict()
|
|
53
|
+
for message in data.get("messages"):
|
|
54
|
+
message_id = message.get("id")
|
|
55
|
+
if message_id not in id_remapping:
|
|
56
|
+
id_remapping[message_id] = SerializedMessageSchema.__pydantic_model__.generate_id()
|
|
57
|
+
message["id"] = id_remapping[message_id]
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError(f"Duplicate message IDs in agent.messages: {message_id}")
|
|
20
60
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if session:
|
|
24
|
-
self.session = session
|
|
61
|
+
# Remap in context message ids
|
|
62
|
+
data["message_ids"] = [id_remapping[message_id] for message_id in data.get("message_ids")]
|
|
25
63
|
|
|
26
|
-
|
|
27
|
-
for field_name, field_obj in self.fields.items():
|
|
28
|
-
if isinstance(field_obj, fields.List) and hasattr(field_obj.inner, "schema"):
|
|
29
|
-
field_obj.inner.schema.session = session
|
|
30
|
-
elif hasattr(field_obj, "schema"):
|
|
31
|
-
field_obj.schema.session = session
|
|
64
|
+
return data
|
|
32
65
|
|
|
33
66
|
class Meta(BaseSchema.Meta):
|
|
34
67
|
model = Agent
|
|
35
68
|
# TODO: Serialize these as well...
|
|
36
|
-
exclude = ("
|
|
69
|
+
exclude = BaseSchema.Meta.exclude + ("sources", "tags", "source_passages", "agent_passages")
|
letta/serialize_schemas/base.py
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
from typing import Dict, Optional
|
|
2
|
+
|
|
3
|
+
from marshmallow import post_dump, pre_load
|
|
1
4
|
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
|
|
5
|
+
from sqlalchemy.inspection import inspect
|
|
6
|
+
|
|
7
|
+
from letta.schemas.user import User
|
|
2
8
|
|
|
3
9
|
|
|
4
10
|
class BaseSchema(SQLAlchemyAutoSchema):
|
|
@@ -7,6 +13,41 @@ class BaseSchema(SQLAlchemyAutoSchema):
|
|
|
7
13
|
This ensures all schemas share the same session.
|
|
8
14
|
"""
|
|
9
15
|
|
|
16
|
+
__pydantic_model__ = None
|
|
17
|
+
sensitive_ids = {"_created_by_id", "_last_updated_by_id"}
|
|
18
|
+
sensitive_relationships = {"organization"}
|
|
19
|
+
id_scramble_placeholder = "xxx"
|
|
20
|
+
|
|
21
|
+
def __init__(self, *args, actor: Optional[User] = None, **kwargs):
|
|
22
|
+
super().__init__(*args, **kwargs)
|
|
23
|
+
self.actor = actor
|
|
24
|
+
|
|
25
|
+
@post_dump
|
|
26
|
+
def sanitize_ids(self, data: Dict, **kwargs):
|
|
27
|
+
data["id"] = self.__pydantic_model__.generate_id()
|
|
28
|
+
|
|
29
|
+
for sensitive_id in BaseSchema.sensitive_ids.union(BaseSchema.sensitive_relationships):
|
|
30
|
+
if sensitive_id in data:
|
|
31
|
+
data[sensitive_id] = BaseSchema.id_scramble_placeholder
|
|
32
|
+
|
|
33
|
+
return data
|
|
34
|
+
|
|
35
|
+
@pre_load
|
|
36
|
+
def regenerate_ids(self, data: Dict, **kwargs):
|
|
37
|
+
if self.Meta.model:
|
|
38
|
+
mapper = inspect(self.Meta.model)
|
|
39
|
+
for sensitive_id in BaseSchema.sensitive_ids:
|
|
40
|
+
if sensitive_id in mapper.columns:
|
|
41
|
+
data[sensitive_id] = self.actor.id
|
|
42
|
+
|
|
43
|
+
for relationship in BaseSchema.sensitive_relationships:
|
|
44
|
+
if relationship in mapper.relationships:
|
|
45
|
+
data[relationship] = self.actor.organization_id
|
|
46
|
+
|
|
47
|
+
return data
|
|
48
|
+
|
|
10
49
|
class Meta:
|
|
50
|
+
model = None
|
|
11
51
|
include_relationships = True
|
|
12
52
|
load_instance = True
|
|
53
|
+
exclude = ()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from letta.orm.block import Block
|
|
2
|
+
from letta.schemas.block import Block as PydanticBlock
|
|
3
|
+
from letta.serialize_schemas.base import BaseSchema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SerializedBlockSchema(BaseSchema):
|
|
7
|
+
"""
|
|
8
|
+
Marshmallow schema for serializing/deserializing Block objects.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__pydantic_model__ = PydanticBlock
|
|
12
|
+
|
|
13
|
+
class Meta(BaseSchema.Meta):
|
|
14
|
+
model = Block
|
|
15
|
+
exclude = BaseSchema.Meta.exclude + ("agents",)
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from marshmallow import post_dump
|
|
4
|
+
|
|
1
5
|
from letta.orm.message import Message
|
|
6
|
+
from letta.schemas.message import Message as PydanticMessage
|
|
2
7
|
from letta.serialize_schemas.base import BaseSchema
|
|
3
8
|
from letta.serialize_schemas.custom_fields import ToolCallField
|
|
4
9
|
|
|
@@ -8,8 +13,17 @@ class SerializedMessageSchema(BaseSchema):
|
|
|
8
13
|
Marshmallow schema for serializing/deserializing Message objects.
|
|
9
14
|
"""
|
|
10
15
|
|
|
16
|
+
__pydantic_model__ = PydanticMessage
|
|
17
|
+
|
|
11
18
|
tool_calls = ToolCallField()
|
|
12
19
|
|
|
20
|
+
@post_dump
|
|
21
|
+
def sanitize_ids(self, data: Dict, **kwargs):
|
|
22
|
+
# We don't want to remap here
|
|
23
|
+
# Because of the way that message_ids is just a JSON field on agents
|
|
24
|
+
# We need to wait for the agent dumps, and then keep track of all the message IDs we remapped
|
|
25
|
+
return data
|
|
26
|
+
|
|
13
27
|
class Meta(BaseSchema.Meta):
|
|
14
28
|
model = Message
|
|
15
|
-
exclude = ("step", "job_message")
|
|
29
|
+
exclude = BaseSchema.Meta.exclude + ("step", "job_message", "agent")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from letta.orm import Tool
|
|
2
|
+
from letta.schemas.tool import Tool as PydanticTool
|
|
3
|
+
from letta.serialize_schemas.base import BaseSchema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SerializedToolSchema(BaseSchema):
|
|
7
|
+
"""
|
|
8
|
+
Marshmallow schema for serializing/deserializing Tool objects.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__pydantic_model__ = PydanticTool
|
|
12
|
+
|
|
13
|
+
class Meta(BaseSchema.Meta):
|
|
14
|
+
model = Tool
|
|
15
|
+
exclude = BaseSchema.Meta.exclude
|
|
@@ -42,6 +42,8 @@ def list_identities(
|
|
|
42
42
|
)
|
|
43
43
|
except HTTPException:
|
|
44
44
|
raise
|
|
45
|
+
except NoResultFound as e:
|
|
46
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
45
47
|
except Exception as e:
|
|
46
48
|
raise HTTPException(status_code=500, detail=f"{e}")
|
|
47
49
|
return identities
|
|
@@ -75,11 +77,11 @@ def create_identity(
|
|
|
75
77
|
except UniqueConstraintViolationError:
|
|
76
78
|
if identity.project_id:
|
|
77
79
|
raise HTTPException(
|
|
78
|
-
status_code=
|
|
80
|
+
status_code=409,
|
|
79
81
|
detail=f"An identity with identifier key {identity.identifier_key} already exists for project {identity.project_id}",
|
|
80
82
|
)
|
|
81
83
|
else:
|
|
82
|
-
raise HTTPException(status_code=
|
|
84
|
+
raise HTTPException(status_code=409, detail=f"An identity with identifier key {identity.identifier_key} already exists")
|
|
83
85
|
except Exception as e:
|
|
84
86
|
raise HTTPException(status_code=500, detail=f"{e}")
|
|
85
87
|
|
|
@@ -96,6 +98,8 @@ def upsert_identity(
|
|
|
96
98
|
return server.identity_manager.upsert_identity(identity=identity, actor=actor)
|
|
97
99
|
except HTTPException:
|
|
98
100
|
raise
|
|
101
|
+
except NoResultFound as e:
|
|
102
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
99
103
|
except Exception as e:
|
|
100
104
|
raise HTTPException(status_code=500, detail=f"{e}")
|
|
101
105
|
|
|
@@ -112,6 +116,8 @@ def modify_identity(
|
|
|
112
116
|
return server.identity_manager.update_identity(identity_id=identity_id, identity=identity, actor=actor)
|
|
113
117
|
except HTTPException:
|
|
114
118
|
raise
|
|
119
|
+
except NoResultFound as e:
|
|
120
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
115
121
|
except Exception as e:
|
|
116
122
|
raise HTTPException(status_code=500, detail=f"{e}")
|
|
117
123
|
|
|
@@ -125,5 +131,12 @@ def delete_identity(
|
|
|
125
131
|
"""
|
|
126
132
|
Delete an identity by its identifier key
|
|
127
133
|
"""
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
try:
|
|
135
|
+
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
|
136
|
+
server.identity_manager.delete_identity(identity_id=identity_id, actor=actor)
|
|
137
|
+
except HTTPException:
|
|
138
|
+
raise
|
|
139
|
+
except NoResultFound as e:
|
|
140
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
141
|
+
except Exception as e:
|
|
142
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
letta/server/startup.sh
CHANGED
|
@@ -38,6 +38,15 @@ if ! alembic upgrade head; then
|
|
|
38
38
|
fi
|
|
39
39
|
echo "Database migration completed successfully."
|
|
40
40
|
|
|
41
|
+
# Set permissions for tool execution directory if configured
|
|
42
|
+
if [ -n "$LETTA_SANDBOX_MOUNT_PATH" ]; then
|
|
43
|
+
if ! chmod 777 "$LETTA_SANDBOX_MOUNT_PATH"; then
|
|
44
|
+
echo "ERROR: Failed to set permissions for tool execution directory at: $LETTA_SANDBOX_MOUNT_PATH"
|
|
45
|
+
echo "Please check that the directory exists and is accessible"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
fi
|
|
49
|
+
|
|
41
50
|
# If ADE is enabled, add the --ade flag to the command
|
|
42
51
|
CMD="letta server --host $HOST --port $PORT"
|
|
43
52
|
if [ "${SECURE:-false}" = "true" ]; then
|
letta/services/agent_manager.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Dict, List, Optional
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
from sqlalchemy import Select, and_, func, literal, or_, select, union_all
|
|
6
6
|
|
|
7
|
-
from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, MAX_EMBEDDING_DIM, MULTI_AGENT_TOOLS
|
|
7
|
+
from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, DATA_SOURCE_ATTACH_ALERT, MAX_EMBEDDING_DIM, MULTI_AGENT_TOOLS
|
|
8
8
|
from letta.embeddings import embedding_model
|
|
9
9
|
from letta.helpers.datetime_helpers import get_utc_time
|
|
10
10
|
from letta.log import get_logger
|
|
@@ -35,6 +35,7 @@ from letta.schemas.tool_rule import TerminalToolRule as PydanticTerminalToolRule
|
|
|
35
35
|
from letta.schemas.tool_rule import ToolRule as PydanticToolRule
|
|
36
36
|
from letta.schemas.user import User as PydanticUser
|
|
37
37
|
from letta.serialize_schemas import SerializedAgentSchema
|
|
38
|
+
from letta.serialize_schemas.tool import SerializedToolSchema
|
|
38
39
|
from letta.services.block_manager import BlockManager
|
|
39
40
|
from letta.services.helpers.agent_manager_helper import (
|
|
40
41
|
_process_relationship,
|
|
@@ -394,18 +395,28 @@ class AgentManager:
|
|
|
394
395
|
with self.session_maker() as session:
|
|
395
396
|
# Retrieve the agent
|
|
396
397
|
agent = AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
|
|
397
|
-
schema = SerializedAgentSchema(session=session)
|
|
398
|
+
schema = SerializedAgentSchema(session=session, actor=actor)
|
|
398
399
|
return schema.dump(agent)
|
|
399
400
|
|
|
400
401
|
@enforce_types
|
|
401
|
-
def deserialize(self, serialized_agent: dict, actor: PydanticUser) -> PydanticAgentState:
|
|
402
|
-
|
|
402
|
+
def deserialize(self, serialized_agent: dict, actor: PydanticUser, mark_as_copy: bool = True) -> PydanticAgentState:
|
|
403
|
+
tool_data_list = serialized_agent.pop("tools", [])
|
|
404
|
+
|
|
403
405
|
with self.session_maker() as session:
|
|
404
|
-
schema = SerializedAgentSchema(session=session)
|
|
406
|
+
schema = SerializedAgentSchema(session=session, actor=actor)
|
|
405
407
|
agent = schema.load(serialized_agent, session=session)
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
408
|
+
if mark_as_copy:
|
|
409
|
+
agent.name += "_copy"
|
|
410
|
+
agent.create(session, actor=actor)
|
|
411
|
+
pydantic_agent = agent.to_pydantic()
|
|
412
|
+
|
|
413
|
+
# Need to do this separately as there's some fancy upsert logic that SqlAlchemy cannot handle
|
|
414
|
+
for tool_data in tool_data_list:
|
|
415
|
+
pydantic_tool = SerializedToolSchema(actor=actor).load(tool_data, transient=True).to_pydantic()
|
|
416
|
+
pydantic_tool = self.tool_manager.create_or_update_tool(pydantic_tool, actor=actor)
|
|
417
|
+
pydantic_agent = self.attach_tool(agent_id=pydantic_agent.id, tool_id=pydantic_tool.id, actor=actor)
|
|
418
|
+
|
|
419
|
+
return pydantic_agent
|
|
409
420
|
|
|
410
421
|
# ======================================================================================================================
|
|
411
422
|
# Per Agent Environment Variable Management
|
|
@@ -670,6 +681,7 @@ class AgentManager:
|
|
|
670
681
|
ValueError: If either agent or source doesn't exist
|
|
671
682
|
IntegrityError: If the source is already attached to the agent
|
|
672
683
|
"""
|
|
684
|
+
|
|
673
685
|
with self.session_maker() as session:
|
|
674
686
|
# Verify both agent and source exist and user has permission to access them
|
|
675
687
|
agent = AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
|
|
@@ -687,7 +699,27 @@ class AgentManager:
|
|
|
687
699
|
|
|
688
700
|
# Commit the changes
|
|
689
701
|
agent.update(session, actor=actor)
|
|
690
|
-
|
|
702
|
+
|
|
703
|
+
# Add system messsage alert to agent
|
|
704
|
+
self.append_system_message(
|
|
705
|
+
agent_id=agent_id,
|
|
706
|
+
content=DATA_SOURCE_ATTACH_ALERT,
|
|
707
|
+
actor=actor,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
return agent.to_pydantic()
|
|
711
|
+
|
|
712
|
+
@enforce_types
|
|
713
|
+
def append_system_message(self, agent_id: str, content: str, actor: PydanticUser):
|
|
714
|
+
|
|
715
|
+
# get the agent
|
|
716
|
+
agent = self.get_agent_by_id(agent_id=agent_id, actor=actor)
|
|
717
|
+
message = PydanticMessage.dict_to_message(
|
|
718
|
+
agent_id=agent.id, user_id=actor.id, model=agent.llm_config.model, openai_message_dict={"role": "system", "content": content}
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# update agent in-context message IDs
|
|
722
|
+
self.append_to_in_context_messages(messages=[message], agent_id=agent_id, actor=actor)
|
|
691
723
|
|
|
692
724
|
@enforce_types
|
|
693
725
|
def list_attached_sources(self, agent_id: str, actor: PydanticUser) -> List[PydanticSource]:
|
|
@@ -46,7 +46,7 @@ class MessageManager:
|
|
|
46
46
|
|
|
47
47
|
# Sort results directly based on message_ids
|
|
48
48
|
result_dict = {msg.id: msg.to_pydantic() for msg in results}
|
|
49
|
-
return [result_dict
|
|
49
|
+
return list(filter(lambda x: x is not None, [result_dict.get(msg_id, None) for msg_id in message_ids]))
|
|
50
50
|
|
|
51
51
|
@enforce_types
|
|
52
52
|
def create_message(self, pydantic_msg: PydanticMessage, actor: PydanticUser) -> PydanticMessage:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
-
from letta.constants import
|
|
3
|
+
from letta.constants import LETTA_TOOL_EXECUTION_DIR
|
|
4
4
|
from letta.log import get_logger
|
|
5
5
|
from letta.orm.errors import NoResultFound
|
|
6
6
|
from letta.orm.sandbox_config import SandboxConfig as SandboxConfigModel
|
|
@@ -35,7 +35,7 @@ class SandboxConfigManager:
|
|
|
35
35
|
default_config = {} # Empty
|
|
36
36
|
else:
|
|
37
37
|
# TODO: May want to move this to environment variables v.s. persisting in database
|
|
38
|
-
default_local_sandbox_path =
|
|
38
|
+
default_local_sandbox_path = LETTA_TOOL_EXECUTION_DIR
|
|
39
39
|
default_config = LocalSandboxConfig(sandbox_dir=default_local_sandbox_path).model_dump(exclude_none=True)
|
|
40
40
|
|
|
41
41
|
sandbox_config = self.create_or_update_sandbox_config(SandboxConfigCreate(config=default_config), actor=actor)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
letta/__init__.py,sha256=
|
|
1
|
+
letta/__init__.py,sha256=3laFXdFab_elN6Et2nVUFuVgXonpAuR0CntCKlRqyPI,918
|
|
2
2
|
letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
|
3
3
|
letta/agent.py,sha256=VljWPlG9bh22cF0o0s9lGw0Qi5Ua2O2JLSOiFtH1SfE,62823
|
|
4
4
|
letta/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -11,11 +11,11 @@ letta/cli/cli.py,sha256=zJz78-qDUz-depb7VQWkg87RBKiETQU4h9DI6ukQBa8,16477
|
|
|
11
11
|
letta/cli/cli_config.py,sha256=MNMhIAAjXiAy2gX_gAtqiY0Ya6VNbzXJWjIcRVEZa-k,8597
|
|
12
12
|
letta/cli/cli_load.py,sha256=xFw-CuzjChcIptaqQ1XpDROENt0JSjyPeiQ0nmEeO1k,2706
|
|
13
13
|
letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
letta/client/client.py,sha256=
|
|
14
|
+
letta/client/client.py,sha256=G2X1QBXOQORzVwViRNax2EPSYwzdxw_L2RiTFS-SS9Q,139150
|
|
15
15
|
letta/client/streaming.py,sha256=lN9vamc07sfQlRbFif327GvURLUPhx-4AC_oUOPvs6w,4543
|
|
16
16
|
letta/client/utils.py,sha256=VCGV-op5ZSmurd4yw7Vhf93XDQ0BkyBT8qsuV7EqfiU,2859
|
|
17
17
|
letta/config.py,sha256=JFGY4TWW0Wm5fTbZamOwWqk5G8Nn-TXyhgByGoAqy2c,12375
|
|
18
|
-
letta/constants.py,sha256=
|
|
18
|
+
letta/constants.py,sha256=MvctT-wAIq4CKlYn3XerWAYsqiJj7ydJqqCCuwJLQa0,7675
|
|
19
19
|
letta/data_sources/connectors.py,sha256=R2AssXpqS7wN6VI8AfxvqaZs5S1ZACc4E_FewmR9iZI,7022
|
|
20
20
|
letta/data_sources/connectors_helper.py,sha256=2TQjCt74fCgT5sw1AP8PalDEk06jPBbhrPG4HVr-WLs,3371
|
|
21
21
|
letta/embeddings.py,sha256=zqlfbN3aCgSOlNd9M2NW9zrwx4WwQzketb8oa5BzzE8,10831
|
|
@@ -94,7 +94,7 @@ letta/local_llm/webui/api.py,sha256=kkxncdCFq1vjgvaHOoQ__j7rcDPgC1F64KcEm94Y6Rs,
|
|
|
94
94
|
letta/local_llm/webui/legacy_api.py,sha256=k3H3y4qp2Fs-XmP24iSIEyvq6wjWFWBzklY3-wRAJNI,2335
|
|
95
95
|
letta/local_llm/webui/legacy_settings.py,sha256=BLmd3TSx5StnY3ibjwaxYATPt_Lvq-o1rlcc_-Q1JcU,538
|
|
96
96
|
letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ftVPh8,552
|
|
97
|
-
letta/log.py,sha256=
|
|
97
|
+
letta/log.py,sha256=p8tJmoPNBg6CJ5PBWjzad1kkQLBjftHvoBUMYXlyL8k,2213
|
|
98
98
|
letta/main.py,sha256=_agyaYPJq70OL0goNwO34zENL2KupnTgqlg-HVcNaTY,15379
|
|
99
99
|
letta/memory.py,sha256=Jqh6Uz4mMAC1aAO2sH8zw4TuMWZr1OI-ZgkSUDh2TQI,3353
|
|
100
100
|
letta/offline_memory_agent.py,sha256=P_rm6GmKAH6lg7-njuv7dK29f7v5-tAQy-rMOwcPfwk,7499
|
|
@@ -167,7 +167,7 @@ letta/schemas/file.py,sha256=ChN2CWzLI2TT9WLItcfElEH0E8b7kzPylF2OQBr6Beg,1550
|
|
|
167
167
|
letta/schemas/health.py,sha256=zT6mYovvD17iJRuu2rcaQQzbEEYrkwvAE9TB7iU824c,139
|
|
168
168
|
letta/schemas/identity.py,sha256=028DseEyCErZ_fifQdFp4ON5QKlDWp-6hrrlekqko_g,2883
|
|
169
169
|
letta/schemas/job.py,sha256=MX9EiLDDIeHm3q52ImOjp7UzXEdYTXAWWobRCAxwV0w,2225
|
|
170
|
-
letta/schemas/letta_base.py,sha256=
|
|
170
|
+
letta/schemas/letta_base.py,sha256=DuMqiNFmYwTAsXUHYdX--EWgtFUVzph5ptLZvu7aguI,3988
|
|
171
171
|
letta/schemas/letta_message.py,sha256=QHzIEwnEJEkE02biCwyQo5IvL2fVq_whBRQD3vPYO48,9837
|
|
172
172
|
letta/schemas/letta_request.py,sha256=dzy3kwb5j2QLaSV0sDlwISEMt2xxH3IiK-vR9xJV65k,1123
|
|
173
173
|
letta/schemas/letta_response.py,sha256=pq-SxXQy5yZo1-DiAwV2mMURlUvz1Uu7HHR_tB1hMho,7139
|
|
@@ -182,9 +182,9 @@ letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNo
|
|
|
182
182
|
letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
|
|
183
183
|
letta/schemas/organization.py,sha256=_RR8jlOOdJyG31q53IDdIvBVvIfAZrQWAGuvc5HmW24,788
|
|
184
184
|
letta/schemas/passage.py,sha256=RG0vkaewEu4a_NAZM-FVyMammHjqpPP0RDYAdu27g6A,3723
|
|
185
|
-
letta/schemas/providers.py,sha256=
|
|
185
|
+
letta/schemas/providers.py,sha256=2Ijzjj1gPETiFyl8yb4ZbwaTljw5WSCdGayAgCsBeYE,43665
|
|
186
186
|
letta/schemas/run.py,sha256=SRqPRziINIiPunjOhE_NlbnQYgxTvqmbauni_yfBQRA,2085
|
|
187
|
-
letta/schemas/sandbox_config.py,sha256=
|
|
187
|
+
letta/schemas/sandbox_config.py,sha256=SZCo3FSMz-DIBMKGu0atT4tsVFXGsqMFPaJnjrxpkX4,5993
|
|
188
188
|
letta/schemas/source.py,sha256=-BQVolcXA2ziCu2ztR6cbTdGUc8G7vGJy7rvpdf1hpg,2880
|
|
189
189
|
letta/schemas/step.py,sha256=8rfOW4gbEIB7yp05pqmZn4P3U3XrvXeUd9lPg9yh6x8,2122
|
|
190
190
|
letta/schemas/tool.py,sha256=3K3csZrk0m9e1lg05tNrO5WCTBiCT9lS-WCVL8jM7mk,11201
|
|
@@ -192,10 +192,12 @@ letta/schemas/tool_rule.py,sha256=2YQZba4fXS3u4j8pIk7BDujfq8rnxSVMwJSyaVgApH4,21
|
|
|
192
192
|
letta/schemas/usage.py,sha256=8oYRH-JX0PfjIu2zkT5Uu3UWQ7Unnz_uHiO8hRGI4m0,912
|
|
193
193
|
letta/schemas/user.py,sha256=V32Tgl6oqB3KznkxUz12y7agkQicjzW7VocSpj78i6Q,1526
|
|
194
194
|
letta/serialize_schemas/__init__.py,sha256=mflGEZvM3NuMG9Q6dccEdVk73BUHoX-v7hmfk025Gmc,64
|
|
195
|
-
letta/serialize_schemas/agent.py,sha256=
|
|
196
|
-
letta/serialize_schemas/base.py,sha256=
|
|
195
|
+
letta/serialize_schemas/agent.py,sha256=XQiLmve761rqktUNNjMYNyuxn1xHq0wtJeZVD9UV11c,2817
|
|
196
|
+
letta/serialize_schemas/base.py,sha256=gH3LyWn7EbiDSJ5T6vUhTf6dilTSfM_TJMIsfQ1Wu_E,1682
|
|
197
|
+
letta/serialize_schemas/block.py,sha256=BeZ2FZO59IWUHiPMSNKlZMi9dH9Gbys0rJpsoCFBjdg,420
|
|
197
198
|
letta/serialize_schemas/custom_fields.py,sha256=HPonsK5DIU9EezXmQG0vAq2CWZVucld6Fqm3-VcJBg8,2101
|
|
198
|
-
letta/serialize_schemas/message.py,sha256=
|
|
199
|
+
letta/serialize_schemas/message.py,sha256=Tdj_ixjjqM2j29gqxLgrdWpTgh5mR3YF5wTbPTnSsHY,920
|
|
200
|
+
letta/serialize_schemas/tool.py,sha256=x13LlwOvbAwnS7Jf5M1nOQCUnDhm4oTFY_mYJu3aJhM,392
|
|
199
201
|
letta/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
200
202
|
letta/server/constants.py,sha256=yAdGbLkzlOU_dLTx0lKDmAnj0ZgRXCEaIcPJWO69eaE,92
|
|
201
203
|
letta/server/db.py,sha256=cA1MHpMCTTC1MX7VWppJ-cKq1XW93Vws_vTV0-bKmTE,3642
|
|
@@ -215,7 +217,7 @@ letta/server/rest_api/routers/v1/__init__.py,sha256=Zi2th-okqT_RWAjB8MYGHX8CpHt1
|
|
|
215
217
|
letta/server/rest_api/routers/v1/agents.py,sha256=I5V06th-lpUZ2fX2wtlYLD8SC2W1AkEIHRR4lr098YM,26658
|
|
216
218
|
letta/server/rest_api/routers/v1/blocks.py,sha256=0j7JX2BQzk31RyhvPZeEb-zh9ImXsVU4_8y5XMiR_WA,3900
|
|
217
219
|
letta/server/rest_api/routers/v1/health.py,sha256=MoOjkydhGcJXTiuJrKIB0etVXiRMdTa51S8RQ8-50DQ,399
|
|
218
|
-
letta/server/rest_api/routers/v1/identities.py,sha256=
|
|
220
|
+
letta/server/rest_api/routers/v1/identities.py,sha256=gRTcOXxbpiTzQzzVA_I1Sc9aUSKhW6YcQcJb__HxOX4,5957
|
|
219
221
|
letta/server/rest_api/routers/v1/jobs.py,sha256=4oeJfI2odNGubU_g7WSORJhn_usFsbRaD-qm86rve1E,2746
|
|
220
222
|
letta/server/rest_api/routers/v1/llms.py,sha256=lYp5URXtZk1yu_Pe-p1Wq1uQ0qeb6aWtx78rXSB7N_E,881
|
|
221
223
|
letta/server/rest_api/routers/v1/organizations.py,sha256=8n-kA9LHtKImdY2xL-v7m6nYAbFWqH1vjBCJhQbv7Is,2077
|
|
@@ -231,7 +233,7 @@ letta/server/rest_api/routers/v1/voice.py,sha256=0Yk-2c4Ec_viqMLMAn_9kZPAYeUgLQo
|
|
|
231
233
|
letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
|
|
232
234
|
letta/server/rest_api/utils.py,sha256=TlVzgtsg0jmaXzvGubtzOE9WSyvRe6_DFoIBK9EDYt8,13820
|
|
233
235
|
letta/server/server.py,sha256=LrIVbKJeEhSTbbiUQdg0gGvpXveY6ITJINWZ9gqehEA,58118
|
|
234
|
-
letta/server/startup.sh,sha256=
|
|
236
|
+
letta/server/startup.sh,sha256=eo7zz4HGu5ryOshfbOSGbXpUDDyoaP7fTq4z8269uaw,1939
|
|
235
237
|
letta/server/static_files/assets/index-048c9598.js,sha256=mR16XppvselwKCcNgONs4L7kZEVa4OEERm4lNZYtLSk,146819
|
|
236
238
|
letta/server/static_files/assets/index-0e31b727.css,sha256=SBbja96uiQVLDhDOroHgM6NSl7tS4lpJRCREgSS_hA8,7672
|
|
237
239
|
letta/server/static_files/favicon.ico,sha256=DezhLdFSbM8o81wCOZcV3riq7tFUOGQD4h6-vr-HuU0,342
|
|
@@ -244,18 +246,18 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
|
|
|
244
246
|
letta/server/ws_api/protocol.py,sha256=5mDgpfNZn_kNwHnpt5Dsuw8gdNH298sgxTGed3etzYg,1836
|
|
245
247
|
letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
|
|
246
248
|
letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
247
|
-
letta/services/agent_manager.py,sha256=
|
|
249
|
+
letta/services/agent_manager.py,sha256=VksHRO4kTYbymJhds9im5w2g9kfpIcCjG9X8ON9eWG0,57077
|
|
248
250
|
letta/services/block_manager.py,sha256=PkbiO59VimLyK6PolWR5O29uHxksCH6pP6K4Vkov3NA,5573
|
|
249
251
|
letta/services/helpers/agent_manager_helper.py,sha256=uvQsmk2ZCAvG4AuI9r3Cs3IDe3Ra8X-qP3B3RbBVMtg,11179
|
|
250
252
|
letta/services/helpers/tool_execution_helper.py,sha256=lLoebs1kZKjw62y1PxHbIDkHq_heJN2ZT0gKje-R8oo,6941
|
|
251
253
|
letta/services/identity_manager.py,sha256=o1XMd5NG2gZ_YWrPOmisfFN0i-2ohoq8n1GM8F49Lf4,7144
|
|
252
254
|
letta/services/job_manager.py,sha256=ejcv_nMljByimiWJjvj7aqUFQktL1kK-vx_cra2L2cs,16317
|
|
253
|
-
letta/services/message_manager.py,sha256=
|
|
255
|
+
letta/services/message_manager.py,sha256=rRd3eMpW_SWbAU_dHAzsX6om9E2yfXno4FwmUEOnZCk,10751
|
|
254
256
|
letta/services/organization_manager.py,sha256=dhQ3cFPXWNYLfMjdahr2HsOAMJ1JtCEWj1G8Nei5MQc,3388
|
|
255
257
|
letta/services/passage_manager.py,sha256=mwShFO_xRaTi82fvANb_ngO0TmGaZPA9FPu8KuZ6Gd8,8643
|
|
256
258
|
letta/services/per_agent_lock_manager.py,sha256=porM0cKKANQ1FvcGXOO_qM7ARk5Fgi1HVEAhXsAg9-4,546
|
|
257
259
|
letta/services/provider_manager.py,sha256=QOKMSZOM6eAWa2-nANWQc1frKBh8N3gqDq0V87fnSuc,3748
|
|
258
|
-
letta/services/sandbox_config_manager.py,sha256=
|
|
260
|
+
letta/services/sandbox_config_manager.py,sha256=ATgZNWNpkdIQDUPy4ABsguHQba2PZf51-c4Ji60MzLE,13361
|
|
259
261
|
letta/services/source_manager.py,sha256=SE24AiPPhpvZMGDD047H3_ZDD7OM4zHbTW1JXjPEv7U,7672
|
|
260
262
|
letta/services/step_manager.py,sha256=5wAxv3OzoYwW9aDcg26hC3foQqOl5qA0tno3vg_Ai5U,5107
|
|
261
263
|
letta/services/summarizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -270,8 +272,8 @@ letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,1194
|
|
|
270
272
|
letta/system.py,sha256=dnOrS2FlRMwijQnOvfrky0Lg8wEw-FUq2zzfAJOUSKA,8477
|
|
271
273
|
letta/tracing.py,sha256=h_-c2lIKHmA7yCLOvgaHijMabmRC__FAl2rZtVKufUM,8017
|
|
272
274
|
letta/utils.py,sha256=AdHrQ2OQ3V4XhJ1LtYwbLUO71j2IJY37cIUxXPgaaRY,32125
|
|
273
|
-
letta_nightly-0.6.
|
|
274
|
-
letta_nightly-0.6.
|
|
275
|
-
letta_nightly-0.6.
|
|
276
|
-
letta_nightly-0.6.
|
|
277
|
-
letta_nightly-0.6.
|
|
275
|
+
letta_nightly-0.6.36.dev20250305002337.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
|
|
276
|
+
letta_nightly-0.6.36.dev20250305002337.dist-info/METADATA,sha256=Tz_fOWZtclmqoKIa_WOGDAxKZBbcYl8CPNAmC0G6ZWE,22627
|
|
277
|
+
letta_nightly-0.6.36.dev20250305002337.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
278
|
+
letta_nightly-0.6.36.dev20250305002337.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
|
|
279
|
+
letta_nightly-0.6.36.dev20250305002337.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|