diff --git a/compose.yaml b/compose.yaml index 0ecdadb1..f6d13abc 100644 --- a/compose.yaml +++ b/compose.yaml @@ -49,9 +49,12 @@ services: - VLLM_API_BASE=${VLLM_API_BASE} - OPENLLM_AUTH_TYPE=${OPENLLM_AUTH_TYPE} - OPENLLM_API_KEY=${OPENLLM_API_KEY} - #volumes: - #- ./configs/server_config.yaml:/root/.letta/config # config file - #- ~/.letta/credentials:/root/.letta/credentials # credentials file + # volumes: + # - ./configs/server_config.yaml:/root/.letta/config # config file + # - ~/.letta/credentials:/root/.letta/credentials # credentials file + # Uncomment this line to mount a local directory for tool execution, and specify the mount path + # before running docker compose: `export LETTA_SANDBOX_MOUNT_PATH=$PWD/directory` + # - ${LETTA_SANDBOX_MOUNT_PATH:?}:/root/.letta/tool_execution_dir # mounted volume for tool execution letta_nginx: hostname: letta-nginx image: nginx:stable-alpine3.17-slim diff --git a/letta/constants.py b/letta/constants.py index 95db4282..bf6679e5 100644 --- a/letta/constants.py +++ b/letta/constants.py @@ -2,7 +2,7 @@ import os from logging import CRITICAL, DEBUG, ERROR, INFO, NOTSET, WARN, WARNING LETTA_DIR = os.path.join(os.path.expanduser("~"), ".letta") -LETTA_DIR_TOOL_SANDBOX = os.path.join(LETTA_DIR, "tool_sandbox_dir") +LETTA_TOOL_EXECUTION_DIR = os.path.join(LETTA_DIR, "tool_execution_dir") ADMIN_PREFIX = "/v1/admin" API_PREFIX = "/v1" diff --git a/letta/schemas/sandbox_config.py b/letta/schemas/sandbox_config.py index 51f13919..80e93c11 100644 --- a/letta/schemas/sandbox_config.py +++ b/letta/schemas/sandbox_config.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List, Literal, Optional, Union from pydantic import BaseModel, Field, model_validator +from letta.constants import LETTA_TOOL_EXECUTION_DIR from letta.schemas.agent import AgentState from letta.schemas.letta_base import LettaBase, OrmMetadataBase from letta.settings import tool_settings @@ -71,7 +72,7 @@ class LocalSandboxConfig(BaseModel): if tool_settings.local_sandbox_dir: data["sandbox_dir"] = tool_settings.local_sandbox_dir else: - data["sandbox_dir"] = "~/.letta" + data["sandbox_dir"] = LETTA_TOOL_EXECUTION_DIR return data diff --git a/letta/server/startup.sh b/letta/server/startup.sh index d4523cce..44b790f1 100755 --- a/letta/server/startup.sh +++ b/letta/server/startup.sh @@ -38,6 +38,15 @@ if ! alembic upgrade head; then fi echo "Database migration completed successfully." +# Set permissions for tool execution directory if configured +if [ -n "$LETTA_SANDBOX_MOUNT_PATH" ]; then + if ! chmod 777 "$LETTA_SANDBOX_MOUNT_PATH"; then + echo "ERROR: Failed to set permissions for tool execution directory at: $LETTA_SANDBOX_MOUNT_PATH" + echo "Please check that the directory exists and is accessible" + exit 1 + fi +fi + # If ADE is enabled, add the --ade flag to the command CMD="letta server --host $HOST --port $PORT" if [ "${SECURE:-false}" = "true" ]; then diff --git a/letta/services/sandbox_config_manager.py b/letta/services/sandbox_config_manager.py index e4e01111..9feaf2a0 100644 --- a/letta/services/sandbox_config_manager.py +++ b/letta/services/sandbox_config_manager.py @@ -1,6 +1,6 @@ from typing import Dict, List, Optional -from letta.constants import LETTA_DIR_TOOL_SANDBOX +from letta.constants import LETTA_TOOL_EXECUTION_DIR from letta.log import get_logger from letta.orm.errors import NoResultFound from letta.orm.sandbox_config import SandboxConfig as SandboxConfigModel @@ -35,7 +35,7 @@ class SandboxConfigManager: default_config = {} # Empty else: # TODO: May want to move this to environment variables v.s. persisting in database - default_local_sandbox_path = LETTA_DIR_TOOL_SANDBOX + default_local_sandbox_path = LETTA_TOOL_EXECUTION_DIR default_config = LocalSandboxConfig(sandbox_dir=default_local_sandbox_path).model_dump(exclude_none=True) sandbox_config = self.create_or_update_sandbox_config(SandboxConfigCreate(config=default_config), actor=actor) diff --git a/tests/test_managers.py b/tests/test_managers.py index 52206d72..334e72ed 100644 --- a/tests/test_managers.py +++ b/tests/test_managers.py @@ -8,7 +8,7 @@ from openai.types.chat.chat_completion_message_tool_call import Function as Open from sqlalchemy.exc import IntegrityError from letta.config import LettaConfig -from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, MULTI_AGENT_TOOLS +from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, LETTA_TOOL_EXECUTION_DIR, MULTI_AGENT_TOOLS from letta.embeddings import embedding_model from letta.functions.functions import derive_openai_json_schema, parse_source_code from letta.orm import Base @@ -2340,7 +2340,7 @@ def test_create_local_sandbox_config_defaults(server: SyncServer, default_user): # Assertions assert created_config.type == SandboxType.LOCAL assert created_config.get_local_config() == sandbox_config_create.config - assert created_config.get_local_config().sandbox_dir in {"~/.letta", tool_settings.local_sandbox_dir} + assert created_config.get_local_config().sandbox_dir in {LETTA_TOOL_EXECUTION_DIR, tool_settings.local_sandbox_dir} assert created_config.organization_id == default_user.organization_id