letta-nightly 0.7.4.dev20250424184820__py3-none-any.whl → 0.7.5.dev20250425104208__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 +3 -4
- letta/schemas/providers.py +4 -1
- letta/server/rest_api/routers/v1/agents.py +11 -0
- letta/server/rest_api/routers/v1/identities.py +18 -0
- letta/server/rest_api/routers/v1/sources.py +11 -0
- letta/server/rest_api/routers/v1/tools.py +15 -0
- letta/server/server.py +2 -4
- letta/services/agent_manager.py +20 -6
- letta/services/identity_manager.py +11 -0
- letta/services/mcp/__init__.py +0 -0
- letta/services/mcp/base_client.py +67 -0
- letta/services/mcp/sse_client.py +25 -0
- letta/services/mcp/stdio_client.py +19 -0
- letta/services/mcp/types.py +48 -0
- letta/services/source_manager.py +11 -0
- letta/services/tool_executor/tool_execution_sandbox.py +136 -63
- letta/services/tool_manager.py +11 -0
- {letta_nightly-0.7.4.dev20250424184820.dist-info → letta_nightly-0.7.5.dev20250425104208.dist-info}/METADATA +1 -1
- {letta_nightly-0.7.4.dev20250424184820.dist-info → letta_nightly-0.7.5.dev20250425104208.dist-info}/RECORD +23 -18
- {letta_nightly-0.7.4.dev20250424184820.dist-info → letta_nightly-0.7.5.dev20250425104208.dist-info}/LICENSE +0 -0
- {letta_nightly-0.7.4.dev20250424184820.dist-info → letta_nightly-0.7.5.dev20250425104208.dist-info}/WHEEL +0 -0
- {letta_nightly-0.7.4.dev20250424184820.dist-info → letta_nightly-0.7.5.dev20250425104208.dist-info}/entry_points.txt +0 -0
letta/__init__.py
CHANGED
letta/agent.py
CHANGED
@@ -190,16 +190,15 @@ class Agent(BaseAgent):
|
|
190
190
|
Returns:
|
191
191
|
modified (bool): whether the memory was updated
|
192
192
|
"""
|
193
|
-
|
193
|
+
system_message = self.message_manager.get_message_by_id(message_id=self.agent_state.message_ids[0], actor=self.user)
|
194
|
+
if new_memory.compile() not in system_message.content[0].text:
|
194
195
|
# update the blocks (LRW) in the DB
|
195
196
|
for label in self.agent_state.memory.list_block_labels():
|
196
197
|
updated_value = new_memory.get_block(label).value
|
197
198
|
if updated_value != self.agent_state.memory.get_block(label).value:
|
198
199
|
# update the block if it's changed
|
199
200
|
block_id = self.agent_state.memory.get_block(label).id
|
200
|
-
|
201
|
-
block_id=block_id, block_update=BlockUpdate(value=updated_value), actor=self.user
|
202
|
-
)
|
201
|
+
self.block_manager.update_block(block_id=block_id, block_update=BlockUpdate(value=updated_value), actor=self.user)
|
203
202
|
|
204
203
|
# refresh memory from DB (using block ids)
|
205
204
|
self.agent_state.memory = Memory(
|
letta/schemas/providers.py
CHANGED
@@ -1233,7 +1233,10 @@ class AzureProvider(Provider):
|
|
1233
1233
|
"""
|
1234
1234
|
This is hardcoded for now, since there is no API endpoints to retrieve metadata for a model.
|
1235
1235
|
"""
|
1236
|
-
|
1236
|
+
context_window = AZURE_MODEL_TO_CONTEXT_LENGTH.get(model_name, None)
|
1237
|
+
if context_window is None:
|
1238
|
+
context_window = LLM_MAX_TOKENS.get(model_name, 4096)
|
1239
|
+
return context_window
|
1237
1240
|
|
1238
1241
|
|
1239
1242
|
class VLLMChatCompletionsProvider(Provider):
|
@@ -104,6 +104,17 @@ def list_agents(
|
|
104
104
|
)
|
105
105
|
|
106
106
|
|
107
|
+
@router.get("/count", response_model=int, operation_id="count_agents")
|
108
|
+
def count_agents(
|
109
|
+
server: SyncServer = Depends(get_letta_server),
|
110
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
111
|
+
):
|
112
|
+
"""
|
113
|
+
Get the count of all agents associated with a given user.
|
114
|
+
"""
|
115
|
+
return server.agent_manager.size(actor=server.user_manager.get_user_or_default(user_id=actor_id))
|
116
|
+
|
117
|
+
|
107
118
|
class IndentedORJSONResponse(Response):
|
108
119
|
media_type = "application/json"
|
109
120
|
|
@@ -49,6 +49,24 @@ def list_identities(
|
|
49
49
|
return identities
|
50
50
|
|
51
51
|
|
52
|
+
@router.get("/count", tags=["identities"], response_model=int, operation_id="count_identities")
|
53
|
+
def count_identities(
|
54
|
+
server: "SyncServer" = Depends(get_letta_server),
|
55
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
56
|
+
):
|
57
|
+
"""
|
58
|
+
Get count of all identities for a user
|
59
|
+
"""
|
60
|
+
try:
|
61
|
+
return server.identity_manager.size(actor=server.user_manager.get_user_or_default(user_id=actor_id))
|
62
|
+
except NoResultFound:
|
63
|
+
return 0
|
64
|
+
except HTTPException:
|
65
|
+
raise
|
66
|
+
except Exception as e:
|
67
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
68
|
+
|
69
|
+
|
52
70
|
@router.get("/{identity_id}", tags=["identities"], response_model=Identity, operation_id="retrieve_identity")
|
53
71
|
def retrieve_identity(
|
54
72
|
identity_id: str,
|
@@ -67,6 +67,17 @@ def list_sources(
|
|
67
67
|
return server.list_all_sources(actor=actor)
|
68
68
|
|
69
69
|
|
70
|
+
@router.get("/count", response_model=int, operation_id="count_sources")
|
71
|
+
def count_sources(
|
72
|
+
server: "SyncServer" = Depends(get_letta_server),
|
73
|
+
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
74
|
+
):
|
75
|
+
"""
|
76
|
+
Count all data sources created by a user.
|
77
|
+
"""
|
78
|
+
return server.source_manager.size(actor=server.user_manager.get_user_or_default(user_id=actor_id))
|
79
|
+
|
80
|
+
|
70
81
|
@router.post("/", response_model=Source, operation_id="create_source")
|
71
82
|
def create_source(
|
72
83
|
source_create: SourceCreate,
|
@@ -80,6 +80,21 @@ def list_tools(
|
|
80
80
|
raise HTTPException(status_code=500, detail=str(e))
|
81
81
|
|
82
82
|
|
83
|
+
@router.get("/count", response_model=int, operation_id="count_tools")
|
84
|
+
def count_tools(
|
85
|
+
server: SyncServer = Depends(get_letta_server),
|
86
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
87
|
+
):
|
88
|
+
"""
|
89
|
+
Get a count of all tools available to agents belonging to the org of the user
|
90
|
+
"""
|
91
|
+
try:
|
92
|
+
return server.tool_manager.size(actor=server.user_manager.get_user_or_default(user_id=actor_id))
|
93
|
+
except Exception as e:
|
94
|
+
print(f"Error occurred: {e}")
|
95
|
+
raise HTTPException(status_code=500, detail=str(e))
|
96
|
+
|
97
|
+
|
83
98
|
@router.post("/", response_model=Tool, operation_id="create_tool")
|
84
99
|
def create_tool(
|
85
100
|
request: ToolCreate = Body(...),
|
letta/server/server.py
CHANGED
@@ -1308,14 +1308,12 @@ class SyncServer(Server):
|
|
1308
1308
|
tool_execution_result = ToolExecutionSandbox(tool.name, tool_args, actor, tool_object=tool).run(
|
1309
1309
|
agent_state=agent_state, additional_env_vars=tool_env_vars
|
1310
1310
|
)
|
1311
|
-
status = "error" if tool_execution_result.stderr else "success"
|
1312
|
-
tool_return = str(tool_execution_result.stderr) if tool_execution_result.stderr else str(tool_execution_result.func_return)
|
1313
1311
|
return ToolReturnMessage(
|
1314
1312
|
id="null",
|
1315
1313
|
tool_call_id="null",
|
1316
1314
|
date=get_utc_time(),
|
1317
|
-
status=status,
|
1318
|
-
tool_return=
|
1315
|
+
status=tool_execution_result.status,
|
1316
|
+
tool_return=str(tool_execution_result.func_return),
|
1319
1317
|
stdout=tool_execution_result.stdout,
|
1320
1318
|
stderr=tool_execution_result.stderr,
|
1321
1319
|
)
|
letta/services/agent_manager.py
CHANGED
@@ -556,6 +556,16 @@ class AgentManager:
|
|
556
556
|
|
557
557
|
return list(session.execute(query).scalars())
|
558
558
|
|
559
|
+
def size(
|
560
|
+
self,
|
561
|
+
actor: PydanticUser,
|
562
|
+
) -> int:
|
563
|
+
"""
|
564
|
+
Get the total count of agents for the given user.
|
565
|
+
"""
|
566
|
+
with self.session_maker() as session:
|
567
|
+
return AgentModel.size(db_session=session, actor=actor)
|
568
|
+
|
559
569
|
@enforce_types
|
560
570
|
def get_agent_by_id(self, agent_id: str, actor: PydanticUser) -> PydanticAgentState:
|
561
571
|
"""Fetch an agent by its ID."""
|
@@ -590,15 +600,18 @@ class AgentManager:
|
|
590
600
|
agents_to_delete = [agent]
|
591
601
|
sleeptime_group_to_delete = None
|
592
602
|
|
593
|
-
# Delete sleeptime agent and group
|
603
|
+
# Delete sleeptime agent and group (TODO this is flimsy pls fix)
|
594
604
|
if agent.multi_agent_group:
|
595
605
|
participant_agent_ids = agent.multi_agent_group.agent_ids
|
596
606
|
if agent.multi_agent_group.manager_type == ManagerType.sleeptime and len(participant_agent_ids) == 1:
|
597
|
-
|
598
|
-
|
599
|
-
sleeptime_agent_group = GroupModel.read(db_session=session, identifier=agent.multi_agent_group.id, actor=actor)
|
600
|
-
sleeptime_group_to_delete = sleeptime_agent_group
|
607
|
+
try:
|
608
|
+
sleeptime_agent = AgentModel.read(db_session=session, identifier=participant_agent_ids[0], actor=actor)
|
601
609
|
agents_to_delete.append(sleeptime_agent)
|
610
|
+
except NoResultFound:
|
611
|
+
pass # agent already deleted
|
612
|
+
sleeptime_agent_group = GroupModel.read(db_session=session, identifier=agent.multi_agent_group.id, actor=actor)
|
613
|
+
sleeptime_group_to_delete = sleeptime_agent_group
|
614
|
+
|
602
615
|
try:
|
603
616
|
if sleeptime_group_to_delete is not None:
|
604
617
|
session.delete(sleeptime_group_to_delete)
|
@@ -931,7 +944,8 @@ class AgentManager:
|
|
931
944
|
modified (bool): whether the memory was updated
|
932
945
|
"""
|
933
946
|
agent_state = self.get_agent_by_id(agent_id=agent_id, actor=actor)
|
934
|
-
|
947
|
+
system_message = self.message_manager.get_message_by_id(message_id=agent_state.message_ids[0], actor=actor)
|
948
|
+
if new_memory.compile() not in system_message.content[0].text:
|
935
949
|
# update the blocks (LRW) in the DB
|
936
950
|
for label in agent_state.memory.list_block_labels():
|
937
951
|
updated_value = new_memory.get_block(label).value
|
@@ -190,6 +190,17 @@ class IdentityManager:
|
|
190
190
|
session.delete(identity)
|
191
191
|
session.commit()
|
192
192
|
|
193
|
+
@enforce_types
|
194
|
+
def size(
|
195
|
+
self,
|
196
|
+
actor: PydanticUser,
|
197
|
+
) -> int:
|
198
|
+
"""
|
199
|
+
Get the total count of identities for the given user.
|
200
|
+
"""
|
201
|
+
with self.session_maker() as session:
|
202
|
+
return IdentityModel.size(db_session=session, actor=actor)
|
203
|
+
|
193
204
|
def _process_relationship(
|
194
205
|
self,
|
195
206
|
session: Session,
|
File without changes
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from contextlib import AsyncExitStack
|
2
|
+
from typing import Optional, Tuple
|
3
|
+
|
4
|
+
from mcp import ClientSession
|
5
|
+
from mcp import Tool as MCPTool
|
6
|
+
from mcp.types import TextContent
|
7
|
+
|
8
|
+
from letta.functions.mcp_client.types import BaseServerConfig
|
9
|
+
from letta.log import get_logger
|
10
|
+
|
11
|
+
logger = get_logger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
# TODO: Get rid of Async prefix on this class name once we deprecate old sync code
|
15
|
+
class AsyncBaseMCPClient:
|
16
|
+
def __init__(self, server_config: BaseServerConfig):
|
17
|
+
self.server_config = server_config
|
18
|
+
self.exit_stack = AsyncExitStack()
|
19
|
+
self.session: Optional[ClientSession] = None
|
20
|
+
self.initialized = False
|
21
|
+
|
22
|
+
async def connect_to_server(self):
|
23
|
+
try:
|
24
|
+
await self._initialize_connection(self.server_config)
|
25
|
+
await self.session.initialize()
|
26
|
+
self.initialized = True
|
27
|
+
except Exception as e:
|
28
|
+
logger.error(
|
29
|
+
f"Connecting to MCP server failed. Please review your server config: {self.server_config.model_dump_json(indent=4)}"
|
30
|
+
)
|
31
|
+
raise e
|
32
|
+
|
33
|
+
async def _initialize_connection(self, exit_stack: AsyncExitStack[bool | None], server_config: BaseServerConfig) -> None:
|
34
|
+
raise NotImplementedError("Subclasses must implement _initialize_connection")
|
35
|
+
|
36
|
+
async def list_tools(self) -> list[MCPTool]:
|
37
|
+
self._check_initialized()
|
38
|
+
response = await self.session.list_tools()
|
39
|
+
return response.tools
|
40
|
+
|
41
|
+
async def execute_tool(self, tool_name: str, tool_args: dict) -> Tuple[str, bool]:
|
42
|
+
self._check_initialized()
|
43
|
+
result = await self.session.call_tool(tool_name, tool_args)
|
44
|
+
parsed_content = []
|
45
|
+
for content_piece in result.content:
|
46
|
+
if isinstance(content_piece, TextContent):
|
47
|
+
parsed_content.append(content_piece.text)
|
48
|
+
print("parsed_content (text)", parsed_content)
|
49
|
+
else:
|
50
|
+
parsed_content.append(str(content_piece))
|
51
|
+
print("parsed_content (other)", parsed_content)
|
52
|
+
if len(parsed_content) > 0:
|
53
|
+
final_content = " ".join(parsed_content)
|
54
|
+
else:
|
55
|
+
# TODO move hardcoding to constants
|
56
|
+
final_content = "Empty response from tool"
|
57
|
+
|
58
|
+
return final_content, result.isError
|
59
|
+
|
60
|
+
def _check_initialized(self):
|
61
|
+
if not self.initialized:
|
62
|
+
logger.error("MCPClient has not been initialized")
|
63
|
+
raise RuntimeError("MCPClient has not been initialized")
|
64
|
+
|
65
|
+
async def cleanup(self):
|
66
|
+
"""Clean up resources"""
|
67
|
+
await self.exit_stack.aclose()
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from contextlib import AsyncExitStack
|
2
|
+
|
3
|
+
from mcp import ClientSession
|
4
|
+
from mcp.client.sse import sse_client
|
5
|
+
|
6
|
+
from letta.functions.mcp_client.types import SSEServerConfig
|
7
|
+
from letta.log import get_logger
|
8
|
+
from letta.services.mcp.base_client import AsyncBaseMCPClient
|
9
|
+
|
10
|
+
# see: https://modelcontextprotocol.io/quickstart/user
|
11
|
+
MCP_CONFIG_TOPLEVEL_KEY = "mcpServers"
|
12
|
+
|
13
|
+
logger = get_logger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
# TODO: Get rid of Async prefix on this class name once we deprecate old sync code
|
17
|
+
class AsyncSSEMCPClient(AsyncBaseMCPClient):
|
18
|
+
async def _initialize_connection(self, exit_stack: AsyncExitStack[bool | None], server_config: SSEServerConfig) -> None:
|
19
|
+
sse_cm = sse_client(url=server_config.server_url)
|
20
|
+
sse_transport = await exit_stack.enter_async_context(sse_cm)
|
21
|
+
self.stdio, self.write = sse_transport
|
22
|
+
|
23
|
+
# Create and enter the ClientSession context manager
|
24
|
+
session_cm = ClientSession(self.stdio, self.write)
|
25
|
+
self.session = await exit_stack.enter_async_context(session_cm)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from contextlib import AsyncExitStack
|
2
|
+
|
3
|
+
from mcp import ClientSession, StdioServerParameters
|
4
|
+
from mcp.client.stdio import stdio_client
|
5
|
+
|
6
|
+
from letta.functions.mcp_client.types import StdioServerConfig
|
7
|
+
from letta.log import get_logger
|
8
|
+
from letta.services.mcp.base_client import AsyncBaseMCPClient
|
9
|
+
|
10
|
+
logger = get_logger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
# TODO: Get rid of Async prefix on this class name once we deprecate old sync code
|
14
|
+
class AsyncStdioMCPClient(AsyncBaseMCPClient):
|
15
|
+
async def _initialize_connection(self, exit_stack: AsyncExitStack[bool | None], server_config: StdioServerConfig) -> None:
|
16
|
+
server_params = StdioServerParameters(command=server_config.command, args=server_config.args)
|
17
|
+
stdio_transport = await exit_stack.enter_async_context(stdio_client(server_params))
|
18
|
+
self.stdio, self.write = stdio_transport
|
19
|
+
self.session = await exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from typing import List, Optional
|
3
|
+
|
4
|
+
from mcp import Tool
|
5
|
+
from pydantic import BaseModel, Field
|
6
|
+
|
7
|
+
|
8
|
+
class MCPTool(Tool):
|
9
|
+
"""A simple wrapper around MCP's tool definition (to avoid conflict with our own)"""
|
10
|
+
|
11
|
+
|
12
|
+
class MCPServerType(str, Enum):
|
13
|
+
SSE = "sse"
|
14
|
+
STDIO = "stdio"
|
15
|
+
|
16
|
+
|
17
|
+
class BaseServerConfig(BaseModel):
|
18
|
+
server_name: str = Field(..., description="The name of the server")
|
19
|
+
type: MCPServerType
|
20
|
+
|
21
|
+
|
22
|
+
class SSEServerConfig(BaseServerConfig):
|
23
|
+
type: MCPServerType = MCPServerType.SSE
|
24
|
+
server_url: str = Field(..., description="The URL of the server (MCP SSE client will connect to this URL)")
|
25
|
+
|
26
|
+
def to_dict(self) -> dict:
|
27
|
+
values = {
|
28
|
+
"transport": "sse",
|
29
|
+
"url": self.server_url,
|
30
|
+
}
|
31
|
+
return values
|
32
|
+
|
33
|
+
|
34
|
+
class StdioServerConfig(BaseServerConfig):
|
35
|
+
type: MCPServerType = MCPServerType.STDIO
|
36
|
+
command: str = Field(..., description="The command to run (MCP 'local' client will run this command)")
|
37
|
+
args: List[str] = Field(..., description="The arguments to pass to the command")
|
38
|
+
env: Optional[dict[str, str]] = Field(None, description="Environment variables to set")
|
39
|
+
|
40
|
+
def to_dict(self) -> dict:
|
41
|
+
values = {
|
42
|
+
"transport": "stdio",
|
43
|
+
"command": self.command,
|
44
|
+
"args": self.args,
|
45
|
+
}
|
46
|
+
if self.env is not None:
|
47
|
+
values["env"] = self.env
|
48
|
+
return values
|
letta/services/source_manager.py
CHANGED
@@ -77,6 +77,17 @@ class SourceManager:
|
|
77
77
|
)
|
78
78
|
return [source.to_pydantic() for source in sources]
|
79
79
|
|
80
|
+
@enforce_types
|
81
|
+
def size(
|
82
|
+
self,
|
83
|
+
actor: PydanticUser,
|
84
|
+
) -> int:
|
85
|
+
"""
|
86
|
+
Get the total count of sources for the given user.
|
87
|
+
"""
|
88
|
+
with self.session_maker() as session:
|
89
|
+
return SourceModel.size(db_session=session, actor=actor)
|
90
|
+
|
80
91
|
@enforce_types
|
81
92
|
def list_attached_agents(self, source_id: str, actor: Optional[PydanticUser] = None) -> List[PydanticAgentState]:
|
82
93
|
"""
|
@@ -1,10 +1,12 @@
|
|
1
1
|
import ast
|
2
2
|
import base64
|
3
|
+
import io
|
3
4
|
import os
|
4
5
|
import pickle
|
5
6
|
import subprocess
|
6
7
|
import sys
|
7
8
|
import tempfile
|
9
|
+
import traceback
|
8
10
|
import uuid
|
9
11
|
from typing import Any, Dict, Optional
|
10
12
|
|
@@ -117,98 +119,108 @@ class ToolExecutionSandbox:
|
|
117
119
|
|
118
120
|
@trace_method
|
119
121
|
def run_local_dir_sandbox(
|
120
|
-
self,
|
121
|
-
agent_state: Optional[AgentState] = None,
|
122
|
-
additional_env_vars: Optional[Dict] = None,
|
122
|
+
self, agent_state: Optional[AgentState] = None, additional_env_vars: Optional[Dict] = None
|
123
123
|
) -> ToolExecutionResult:
|
124
|
-
sbx_config = self.sandbox_config_manager.get_or_create_default_sandbox_config(
|
125
|
-
sandbox_type=SandboxType.LOCAL,
|
126
|
-
actor=self.user,
|
127
|
-
)
|
124
|
+
sbx_config = self.sandbox_config_manager.get_or_create_default_sandbox_config(sandbox_type=SandboxType.LOCAL, actor=self.user)
|
128
125
|
local_configs = sbx_config.get_local_config()
|
129
|
-
sandbox_dir = os.path.expanduser(local_configs.sandbox_dir)
|
130
|
-
venv_path = os.path.join(sandbox_dir, local_configs.venv_name)
|
131
126
|
|
132
|
-
#
|
127
|
+
# Get environment variables for the sandbox
|
133
128
|
env = os.environ.copy()
|
134
|
-
|
129
|
+
env_vars = self.sandbox_config_manager.get_sandbox_env_vars_as_dict(sandbox_config_id=sbx_config.id, actor=self.user, limit=100)
|
130
|
+
env.update(env_vars)
|
131
|
+
|
132
|
+
# Get environment variables for this agent specifically
|
135
133
|
if agent_state:
|
136
134
|
env.update(agent_state.get_agent_env_vars_as_dict())
|
135
|
+
|
136
|
+
# Finally, get any that are passed explicitly into the `run` function call
|
137
137
|
if additional_env_vars:
|
138
138
|
env.update(additional_env_vars)
|
139
139
|
|
140
|
-
#
|
141
|
-
if not os.path.exists(sandbox_dir):
|
142
|
-
logger.warning(f"Sandbox directory does not exist, creating: {sandbox_dir}")
|
143
|
-
os.makedirs(sandbox_dir)
|
140
|
+
# Safety checks
|
141
|
+
if not os.path.exists(local_configs.sandbox_dir) or not os.path.isdir(local_configs.sandbox_dir):
|
142
|
+
logger.warning(f"Sandbox directory does not exist, creating: {local_configs.sandbox_dir}")
|
143
|
+
os.makedirs(local_configs.sandbox_dir)
|
144
|
+
|
145
|
+
# Write the code to a temp file in the sandbox_dir
|
146
|
+
with tempfile.NamedTemporaryFile(mode="w", dir=local_configs.sandbox_dir, suffix=".py", delete=False) as temp_file:
|
147
|
+
if local_configs.force_create_venv:
|
148
|
+
# If using venv, we need to wrap with special string markers to separate out the output and the stdout (since it is all in stdout)
|
149
|
+
code = self.generate_execution_script(agent_state=agent_state, wrap_print_with_markers=True)
|
150
|
+
else:
|
151
|
+
code = self.generate_execution_script(agent_state=agent_state)
|
144
152
|
|
145
|
-
# Write the code to a temp file
|
146
|
-
with tempfile.NamedTemporaryFile(mode="w", dir=sandbox_dir, suffix=".py", delete=False) as temp_file:
|
147
|
-
code = self.generate_execution_script(agent_state=agent_state, wrap_print_with_markers=True)
|
148
153
|
temp_file.write(code)
|
149
154
|
temp_file.flush()
|
150
155
|
temp_file_path = temp_file.name
|
151
|
-
|
152
156
|
try:
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if self.force_recreate_venv or (not use_venv and local_configs.force_create_venv):
|
157
|
-
logger.warning(f"Virtual environment not found at {venv_path}. Creating one...")
|
158
|
-
log_event(name="start create_venv_for_local_sandbox", attributes={"venv_path": venv_path})
|
159
|
-
create_venv_for_local_sandbox(
|
160
|
-
sandbox_dir_path=sandbox_dir,
|
161
|
-
venv_path=venv_path,
|
162
|
-
env=env,
|
163
|
-
force_recreate=self.force_recreate_venv,
|
164
|
-
)
|
165
|
-
log_event(name="finish create_venv_for_local_sandbox")
|
166
|
-
use_venv = True
|
167
|
-
|
168
|
-
if use_venv:
|
169
|
-
log_event(name="start install_pip_requirements_for_sandbox", attributes={"local_configs": local_configs.model_dump_json()})
|
170
|
-
install_pip_requirements_for_sandbox(local_configs, env=env)
|
171
|
-
log_event(name="finish install_pip_requirements_for_sandbox", attributes={"local_configs": local_configs.model_dump_json()})
|
172
|
-
|
173
|
-
python_executable = find_python_executable(local_configs)
|
174
|
-
if not os.path.isfile(python_executable):
|
175
|
-
logger.warning(
|
176
|
-
f"Python executable not found at expected venv path: {python_executable}. Falling back to system Python."
|
177
|
-
)
|
178
|
-
python_executable = sys.executable
|
179
|
-
else:
|
180
|
-
env = dict(env)
|
181
|
-
env["VIRTUAL_ENV"] = venv_path
|
182
|
-
env["PATH"] = os.path.join(venv_path, "bin") + ":" + env.get("PATH", "")
|
157
|
+
if local_configs.force_create_venv:
|
158
|
+
return self.run_local_dir_sandbox_venv(sbx_config, env, temp_file_path)
|
183
159
|
else:
|
184
|
-
|
160
|
+
return self.run_local_dir_sandbox_directly(sbx_config, env, temp_file_path)
|
161
|
+
except Exception as e:
|
162
|
+
logger.error(f"Executing tool {self.tool_name} has an unexpected error: {e}")
|
163
|
+
logger.error(f"Logging out tool {self.tool_name} auto-generated code for debugging: \n\n{code}")
|
164
|
+
raise e
|
165
|
+
finally:
|
166
|
+
# Clean up the temp file
|
167
|
+
os.remove(temp_file_path)
|
168
|
+
|
169
|
+
@trace_method
|
170
|
+
def run_local_dir_sandbox_venv(
|
171
|
+
self,
|
172
|
+
sbx_config: SandboxConfig,
|
173
|
+
env: Dict[str, str],
|
174
|
+
temp_file_path: str,
|
175
|
+
) -> ToolExecutionResult:
|
176
|
+
local_configs = sbx_config.get_local_config()
|
177
|
+
sandbox_dir = os.path.expanduser(local_configs.sandbox_dir) # Expand tilde
|
178
|
+
venv_path = os.path.join(sandbox_dir, local_configs.venv_name)
|
179
|
+
|
180
|
+
# Recreate venv if required
|
181
|
+
if self.force_recreate_venv or not os.path.isdir(venv_path):
|
182
|
+
logger.warning(f"Virtual environment directory does not exist at: {venv_path}, creating one now...")
|
183
|
+
log_event(name="start create_venv_for_local_sandbox", attributes={"venv_path": venv_path})
|
184
|
+
create_venv_for_local_sandbox(
|
185
|
+
sandbox_dir_path=sandbox_dir, venv_path=venv_path, env=env, force_recreate=self.force_recreate_venv
|
186
|
+
)
|
187
|
+
log_event(name="finish create_venv_for_local_sandbox")
|
188
|
+
|
189
|
+
log_event(name="start install_pip_requirements_for_sandbox", attributes={"local_configs": local_configs.model_dump_json()})
|
190
|
+
install_pip_requirements_for_sandbox(local_configs, env=env)
|
191
|
+
log_event(name="finish install_pip_requirements_for_sandbox", attributes={"local_configs": local_configs.model_dump_json()})
|
192
|
+
|
193
|
+
# Ensure Python executable exists
|
194
|
+
python_executable = find_python_executable(local_configs)
|
195
|
+
if not os.path.isfile(python_executable):
|
196
|
+
raise FileNotFoundError(f"Python executable not found in virtual environment: {python_executable}")
|
185
197
|
|
186
|
-
|
198
|
+
# Set up environment variables
|
199
|
+
env["VIRTUAL_ENV"] = venv_path
|
200
|
+
env["PATH"] = os.path.join(venv_path, "bin") + ":" + env["PATH"]
|
201
|
+
env["PYTHONWARNINGS"] = "ignore"
|
187
202
|
|
203
|
+
# Execute the code
|
204
|
+
try:
|
188
205
|
log_event(name="start subprocess")
|
189
206
|
result = subprocess.run(
|
190
|
-
[python_executable, temp_file_path],
|
191
|
-
env=env,
|
192
|
-
cwd=sandbox_dir,
|
193
|
-
timeout=60,
|
194
|
-
capture_output=True,
|
195
|
-
text=True,
|
207
|
+
[python_executable, temp_file_path], env=env, cwd=sandbox_dir, timeout=60, capture_output=True, text=True, check=True
|
196
208
|
)
|
197
209
|
log_event(name="finish subprocess")
|
198
210
|
func_result, stdout = self.parse_out_function_results_markers(result.stdout)
|
199
|
-
func_return,
|
211
|
+
func_return, agent_state = self.parse_best_effort(func_result)
|
200
212
|
|
201
213
|
return ToolExecutionResult(
|
202
214
|
status="success",
|
203
215
|
func_return=func_return,
|
204
|
-
agent_state=
|
216
|
+
agent_state=agent_state,
|
205
217
|
stdout=[stdout] if stdout else [],
|
206
218
|
stderr=[result.stderr] if result.stderr else [],
|
207
219
|
sandbox_config_fingerprint=sbx_config.fingerprint(),
|
208
220
|
)
|
209
221
|
|
210
222
|
except subprocess.CalledProcessError as e:
|
211
|
-
logger.error(f"
|
223
|
+
logger.error(f"Executing tool {self.tool_name} has process error: {e}")
|
212
224
|
func_return = get_friendly_error_msg(
|
213
225
|
function_name=self.tool_name,
|
214
226
|
exception_name=type(e).__name__,
|
@@ -228,11 +240,72 @@ class ToolExecutionSandbox:
|
|
228
240
|
|
229
241
|
except Exception as e:
|
230
242
|
logger.error(f"Executing tool {self.tool_name} has an unexpected error: {e}")
|
231
|
-
logger.error(f"Generated script:\n{code}")
|
232
243
|
raise e
|
233
244
|
|
234
|
-
|
235
|
-
|
245
|
+
@trace_method
|
246
|
+
def run_local_dir_sandbox_directly(
|
247
|
+
self,
|
248
|
+
sbx_config: SandboxConfig,
|
249
|
+
env: Dict[str, str],
|
250
|
+
temp_file_path: str,
|
251
|
+
) -> ToolExecutionResult:
|
252
|
+
status = "success"
|
253
|
+
func_return, agent_state, stderr = None, None, None
|
254
|
+
|
255
|
+
old_stdout = sys.stdout
|
256
|
+
old_stderr = sys.stderr
|
257
|
+
captured_stdout, captured_stderr = io.StringIO(), io.StringIO()
|
258
|
+
|
259
|
+
sys.stdout = captured_stdout
|
260
|
+
sys.stderr = captured_stderr
|
261
|
+
|
262
|
+
try:
|
263
|
+
with self.temporary_env_vars(env):
|
264
|
+
|
265
|
+
# Read and compile the Python script
|
266
|
+
with open(temp_file_path, "r", encoding="utf-8") as f:
|
267
|
+
source = f.read()
|
268
|
+
code_obj = compile(source, temp_file_path, "exec")
|
269
|
+
|
270
|
+
# Provide a dict for globals.
|
271
|
+
globals_dict = dict(env) # or {}
|
272
|
+
# If you need to mimic `__main__` behavior:
|
273
|
+
globals_dict["__name__"] = "__main__"
|
274
|
+
globals_dict["__file__"] = temp_file_path
|
275
|
+
|
276
|
+
# Execute the compiled code
|
277
|
+
log_event(name="start exec", attributes={"temp_file_path": temp_file_path})
|
278
|
+
exec(code_obj, globals_dict)
|
279
|
+
log_event(name="finish exec", attributes={"temp_file_path": temp_file_path})
|
280
|
+
|
281
|
+
# Get result from the global dict
|
282
|
+
func_result = globals_dict.get(self.LOCAL_SANDBOX_RESULT_VAR_NAME)
|
283
|
+
func_return, agent_state = self.parse_best_effort(func_result)
|
284
|
+
|
285
|
+
except Exception as e:
|
286
|
+
func_return = get_friendly_error_msg(
|
287
|
+
function_name=self.tool_name,
|
288
|
+
exception_name=type(e).__name__,
|
289
|
+
exception_message=str(e),
|
290
|
+
)
|
291
|
+
traceback.print_exc(file=sys.stderr)
|
292
|
+
status = "error"
|
293
|
+
|
294
|
+
# Restore stdout/stderr
|
295
|
+
sys.stdout = old_stdout
|
296
|
+
sys.stderr = old_stderr
|
297
|
+
|
298
|
+
stdout_output = [captured_stdout.getvalue()] if captured_stdout.getvalue() else []
|
299
|
+
stderr_output = [captured_stderr.getvalue()] if captured_stderr.getvalue() else []
|
300
|
+
|
301
|
+
return ToolExecutionResult(
|
302
|
+
status=status,
|
303
|
+
func_return=func_return,
|
304
|
+
agent_state=agent_state,
|
305
|
+
stdout=stdout_output,
|
306
|
+
stderr=stderr_output,
|
307
|
+
sandbox_config_fingerprint=sbx_config.fingerprint(),
|
308
|
+
)
|
236
309
|
|
237
310
|
def parse_out_function_results_markers(self, text: str):
|
238
311
|
if self.LOCAL_SANDBOX_RESULT_START_MARKER not in text:
|
letta/services/tool_manager.py
CHANGED
@@ -145,6 +145,17 @@ class ToolManager:
|
|
145
145
|
|
146
146
|
return results
|
147
147
|
|
148
|
+
@enforce_types
|
149
|
+
def size(
|
150
|
+
self,
|
151
|
+
actor: PydanticUser,
|
152
|
+
) -> int:
|
153
|
+
"""
|
154
|
+
Get the total count of tools for the given user.
|
155
|
+
"""
|
156
|
+
with self.session_maker() as session:
|
157
|
+
return ToolModel.size(db_session=session, actor=actor)
|
158
|
+
|
148
159
|
@enforce_types
|
149
160
|
def update_tool_by_id(self, tool_id: str, tool_update: ToolUpdate, actor: PydanticUser) -> PydanticTool:
|
150
161
|
"""Update a tool by its ID with the given ToolUpdate object."""
|
@@ -1,6 +1,6 @@
|
|
1
|
-
letta/__init__.py,sha256=
|
1
|
+
letta/__init__.py,sha256=bAqRCwvutm5LP1C2uuuUcODc0u_yqtCtac7CL9bi61k,917
|
2
2
|
letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
3
|
-
letta/agent.py,sha256=
|
3
|
+
letta/agent.py,sha256=VCTvaT0skG1uj7s76GVEMKge9Q0ranjajlQtpjST4iw,72242
|
4
4
|
letta/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
letta/agents/base_agent.py,sha256=yjB1Yz6L-9hTFinqkvyf6c-8dDX9O4u8VCrKrvA4G3s,2348
|
6
6
|
letta/agents/ephemeral_agent.py,sha256=el-SUF_16vv_7OouIR-6z0pAE9Yc0PLibygvfCKwqfo,2736
|
@@ -221,7 +221,7 @@ letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNo
|
|
221
221
|
letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
|
222
222
|
letta/schemas/organization.py,sha256=TXrHN4IBQnX-mWvRuCOH57XZSLYCVOY0wWm2_UzDQIA,1279
|
223
223
|
letta/schemas/passage.py,sha256=RG0vkaewEu4a_NAZM-FVyMammHjqpPP0RDYAdu27g6A,3723
|
224
|
-
letta/schemas/providers.py,sha256=
|
224
|
+
letta/schemas/providers.py,sha256=6TyGvmfAT-bgmYblfjheQ3Ki4gGxVCFfC5Lt8D3X4qc,51784
|
225
225
|
letta/schemas/response_format.py,sha256=pXNsjbtpA3Tf8HsDyIa40CSmoUbVR_7n2WOfQaX4aFs,2204
|
226
226
|
letta/schemas/run.py,sha256=SRqPRziINIiPunjOhE_NlbnQYgxTvqmbauni_yfBQRA,2085
|
227
227
|
letta/schemas/sandbox_config.py,sha256=Uu_GDofZasycarUVjXB1tL5-X1pLXSsrCB_7XycRIuM,6011
|
@@ -258,11 +258,11 @@ letta/server/rest_api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
|
|
258
258
|
letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
259
259
|
letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=Ksh_F6hJDOrafY2Gxz7NQdQZXBNeX-3-w4mVEQgMhrQ,5327
|
260
260
|
letta/server/rest_api/routers/v1/__init__.py,sha256=M-L-Ls4rQaVyQvMjFNEu3NhcRLCvsbOKI-l7F4JxifQ,1557
|
261
|
-
letta/server/rest_api/routers/v1/agents.py,sha256=
|
261
|
+
letta/server/rest_api/routers/v1/agents.py,sha256=3mbPzEKBCzJmpUBUmJrs1WbmkIGTjEJ32HNUb1Lo5_M,34676
|
262
262
|
letta/server/rest_api/routers/v1/blocks.py,sha256=Sefvon0jLvlNh0oAzntUcDZptnutuJOf-2Wcad_45Dg,4169
|
263
263
|
letta/server/rest_api/routers/v1/groups.py,sha256=sLXkw8kgf9fhaQwb-n0SVbyzH6-e1kdzNuqGbdvPPgo,10890
|
264
264
|
letta/server/rest_api/routers/v1/health.py,sha256=MoOjkydhGcJXTiuJrKIB0etVXiRMdTa51S8RQ8-50DQ,399
|
265
|
-
letta/server/rest_api/routers/v1/identities.py,sha256=
|
265
|
+
letta/server/rest_api/routers/v1/identities.py,sha256=fvp-0cwvb4iX1fUGPkL--9nq8YD3tIE47kYRxUgOlp4,7462
|
266
266
|
letta/server/rest_api/routers/v1/jobs.py,sha256=4oeJfI2odNGubU_g7WSORJhn_usFsbRaD-qm86rve1E,2746
|
267
267
|
letta/server/rest_api/routers/v1/llms.py,sha256=lYp5URXtZk1yu_Pe-p1Wq1uQ0qeb6aWtx78rXSB7N_E,881
|
268
268
|
letta/server/rest_api/routers/v1/messages.py,sha256=3hfaiiCBiWr-wub_uzr3Vyh4IBTZYwGqeDG-2h6Xlus,5753
|
@@ -270,15 +270,15 @@ letta/server/rest_api/routers/v1/organizations.py,sha256=r7rj-cA3shgAgM0b2JCMqjY
|
|
270
270
|
letta/server/rest_api/routers/v1/providers.py,sha256=MVfAUvXj_2jx8XFwSigM-8CuCfEATW60h8J5UEmAhp0,3146
|
271
271
|
letta/server/rest_api/routers/v1/runs.py,sha256=9nuJRjBtRgZPq3CiCEUA_3S2xPHFP5DsJxIenH5OO34,8847
|
272
272
|
letta/server/rest_api/routers/v1/sandbox_configs.py,sha256=9hqnnMwJ3wCwO-Bezu3Xl8i3TDSIuInw3gSeHaKUXfE,8526
|
273
|
-
letta/server/rest_api/routers/v1/sources.py,sha256=
|
273
|
+
letta/server/rest_api/routers/v1/sources.py,sha256=U9cf7DlqKAvXgNnmyOvr2XLBHiJbimFCQw__gV__HV8,10709
|
274
274
|
letta/server/rest_api/routers/v1/steps.py,sha256=DVVwaxLNbNAgWpr2oQkrNjdS-wi0bP8kVJZUO-hiaf8,3275
|
275
275
|
letta/server/rest_api/routers/v1/tags.py,sha256=coydgvL6-9cuG2Hy5Ea7QY3inhTHlsf69w0tcZenBus,880
|
276
|
-
letta/server/rest_api/routers/v1/tools.py,sha256=
|
276
|
+
letta/server/rest_api/routers/v1/tools.py,sha256=vDDhG25vQOz3y12XN_dayh4OweW1rjaLHxZJuJgZkQA,18821
|
277
277
|
letta/server/rest_api/routers/v1/users.py,sha256=G5DBHSkPfBgVHN2Wkm-rVYiLQAudwQczIq2Z3YLdbVo,2277
|
278
278
|
letta/server/rest_api/routers/v1/voice.py,sha256=0lerWjrKLkt4gXLhZl1cIcgstOz9Q2HZwc67L58BCXE,2451
|
279
279
|
letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
|
280
280
|
letta/server/rest_api/utils.py,sha256=OKUWg7u4vmROVweqRrs83bQvS958bZAoR_bUFDwwqsc,14861
|
281
|
-
letta/server/server.py,sha256=
|
281
|
+
letta/server/server.py,sha256=XcZ_vrQkG2CVr_mgCKVAV6EMVjuR4lQVtU3FTAUJIIc,79726
|
282
282
|
letta/server/startup.sh,sha256=MRXh1RKbS5lyA7XAsk7O6Q4LEKOqnv5B-dwe0SnTHeQ,2514
|
283
283
|
letta/server/static_files/assets/index-048c9598.js,sha256=mR16XppvselwKCcNgONs4L7kZEVa4OEERm4lNZYtLSk,146819
|
284
284
|
letta/server/static_files/assets/index-0e31b727.css,sha256=SBbja96uiQVLDhDOroHgM6NSl7tS4lpJRCREgSS_hA8,7672
|
@@ -292,30 +292,35 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
|
|
292
292
|
letta/server/ws_api/protocol.py,sha256=5mDgpfNZn_kNwHnpt5Dsuw8gdNH298sgxTGed3etzYg,1836
|
293
293
|
letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
|
294
294
|
letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
295
|
-
letta/services/agent_manager.py,sha256=
|
295
|
+
letta/services/agent_manager.py,sha256=9BRBbAf0LXftDzke8OBzVgzOmP1FLCnsRSQggbNxeOU,71581
|
296
296
|
letta/services/block_manager.py,sha256=rphbpGBIEDFvCJ5GJt6A1OfbURGFQZ3WardljELQyAc,15225
|
297
297
|
letta/services/group_manager.py,sha256=z1woWRRnjkWrGG_auSicXr2bJaJ653JV6PYl2N_AUfw,12224
|
298
298
|
letta/services/helpers/agent_manager_helper.py,sha256=57ARg5TcmE_JdrWmhz5uaNHAt1NGlJ3wQH1tP2XOiAs,17825
|
299
299
|
letta/services/helpers/tool_execution_helper.py,sha256=E8knHJUP_EKmAzwKx-z-rd5EKjU-oroyPzyElUmq83o,6968
|
300
|
-
letta/services/identity_manager.py,sha256=
|
300
|
+
letta/services/identity_manager.py,sha256=sjHTCPbLYRDyWCJ3qjcuKZqWqzDoEuslRsDVKQtBraE,9683
|
301
301
|
letta/services/job_manager.py,sha256=Z9cSD-IqpiOIWE_UOTBUEIxGITrJ10JT1G3ske-0yIY,17166
|
302
302
|
letta/services/llm_batch_manager.py,sha256=jm6MY91hrZdcPOsimSd7Ns3mf0J4SwKV2_ShWXhD02k,16819
|
303
|
+
letta/services/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
304
|
+
letta/services/mcp/base_client.py,sha256=YoRb9eKKTGaLxaMVtuH5UcC74iXyWlcyYbC5xOeGr4k,2571
|
305
|
+
letta/services/mcp/sse_client.py,sha256=Vj0AgaadgMnpFQOWkSoPfeOI00ZvURMf3TIU7fv_DN8,1012
|
306
|
+
letta/services/mcp/stdio_client.py,sha256=wdPzTqSRkibjt9pXhwi0Nul_z_cTAPim-OHjLc__yBE,925
|
307
|
+
letta/services/mcp/types.py,sha256=nmcnQn2EpxXzXg5_pWPsHZobfxO6OucaUgz1bVvam7o,1411
|
303
308
|
letta/services/message_manager.py,sha256=iRFFu7WP9GBtGKrQp5Igiqp_wonSfRKZ_Ran5X6SZZA,17946
|
304
309
|
letta/services/organization_manager.py,sha256=Ax0KmPSc_YYsYaxeld9gc7ST-J6DemHQ542DD7l7AWA,3989
|
305
310
|
letta/services/passage_manager.py,sha256=KY18gHTbx8ROBsOeR7ZAefTMGZwzbxYqOjbadqVFiyQ,9121
|
306
311
|
letta/services/per_agent_lock_manager.py,sha256=porM0cKKANQ1FvcGXOO_qM7ARk5Fgi1HVEAhXsAg9-4,546
|
307
312
|
letta/services/provider_manager.py,sha256=_gEBW0tYIf2vJEGGYxk-nvogrFI9sjFl_97MSL5WC2s,3759
|
308
313
|
letta/services/sandbox_config_manager.py,sha256=ATgZNWNpkdIQDUPy4ABsguHQba2PZf51-c4Ji60MzLE,13361
|
309
|
-
letta/services/source_manager.py,sha256=
|
314
|
+
letta/services/source_manager.py,sha256=yW88wAJoeAWtbg0FxifE352jhgOTKNiG7K-IPKXKnvg,7961
|
310
315
|
letta/services/step_manager.py,sha256=B64iYn6Dt9yRKsSJ5vLxWQR2t-apvPLfUZyzrUsJTpI,5335
|
311
316
|
letta/services/summarizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
312
317
|
letta/services/summarizer/enums.py,sha256=szzPX2OBRRJEZsBTGYQThrNz02ELFqhuLwvOR7ozi7A,208
|
313
318
|
letta/services/summarizer/summarizer.py,sha256=4rbbzcB_lY4-3ybT8HMxM8OskLC38YCs9n5h0NhWRcY,4988
|
314
319
|
letta/services/tool_executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
315
320
|
letta/services/tool_executor/tool_execution_manager.py,sha256=RcmnwPougb8AxIwKdC4N9ZxTvOQqJyjI6CaVHF2HBi4,4505
|
316
|
-
letta/services/tool_executor/tool_execution_sandbox.py,sha256=
|
321
|
+
letta/services/tool_executor/tool_execution_sandbox.py,sha256=hu-SVqfRalJGcXRKTpbYkqgX-DZH3Uky_eD_vh0kx6s,24704
|
317
322
|
letta/services/tool_executor/tool_executor.py,sha256=keWIzQuwqSzcC6kWcbTY_SfclhKtOT5CZNE-r3OBWNk,27372
|
318
|
-
letta/services/tool_manager.py,sha256=
|
323
|
+
letta/services/tool_manager.py,sha256=lFnQkgBYXAXl9q_8_TqJX_UZtLTATXmPliCdm8_7K8M,10561
|
319
324
|
letta/services/tool_sandbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
320
325
|
letta/services/tool_sandbox/base.py,sha256=pUnPFkEg9I5ktMuT4AOOxbTnTmZTGcTA2phLe1H1EdY,8306
|
321
326
|
letta/services/tool_sandbox/e2b_sandbox.py,sha256=umsXfolzM_j67izswECDdVfnlcm03wLpMoZtS6SZ0sc,6147
|
@@ -327,8 +332,8 @@ letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,1194
|
|
327
332
|
letta/system.py,sha256=dnOrS2FlRMwijQnOvfrky0Lg8wEw-FUq2zzfAJOUSKA,8477
|
328
333
|
letta/tracing.py,sha256=RstWXpfWVF77nmb_ISORVWd9IQw2Ky3de40k_S70yKI,8258
|
329
334
|
letta/utils.py,sha256=IZFvtj9WYcrxUbkoUUYGDxMYQYdn5SgfqsvnARGsAzc,32245
|
330
|
-
letta_nightly-0.7.
|
331
|
-
letta_nightly-0.7.
|
332
|
-
letta_nightly-0.7.
|
333
|
-
letta_nightly-0.7.
|
334
|
-
letta_nightly-0.7.
|
335
|
+
letta_nightly-0.7.5.dev20250425104208.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
|
336
|
+
letta_nightly-0.7.5.dev20250425104208.dist-info/METADATA,sha256=WeqMXkmCYUka4wxlVOUi9CB9eVUtVbf2m3oXQ-4Ik6w,22282
|
337
|
+
letta_nightly-0.7.5.dev20250425104208.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
338
|
+
letta_nightly-0.7.5.dev20250425104208.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
|
339
|
+
letta_nightly-0.7.5.dev20250425104208.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|