Skip to content

Commit 9c6f25e

Browse files
authored
chore: Move environment to settings [LET-4080] (#4265)
Move environment to settings
1 parent 9003519 commit 9c6f25e

File tree

9 files changed

+24
-32
lines changed

9 files changed

+24
-32
lines changed

letta/agent.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
import json
3-
import os
43
import time
54
import traceback
65
import warnings
@@ -71,7 +70,7 @@
7170
from letta.services.telemetry_manager import NoopTelemetryManager, TelemetryManager
7271
from letta.services.tool_executor.tool_execution_sandbox import ToolExecutionSandbox
7372
from letta.services.tool_manager import ToolManager
74-
from letta.settings import settings, summarizer_settings
73+
from letta.settings import model_settings, settings, summarizer_settings
7574
from letta.streaming_interface import StreamingRefreshCLIInterface
7675
from letta.system import get_heartbeat, get_token_limit_warning, package_function_response, package_summarize_message, package_user_message
7776
from letta.utils import count_tokens, get_friendly_error_msg, get_tool_call_id, log_telemetry, parse_json, validate_function_response
@@ -1304,7 +1303,7 @@ def get_context_window(self) -> ContextWindowOverview:
13041303
)
13051304

13061305
async def get_context_window_async(self) -> ContextWindowOverview:
1307-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION" and os.getenv("ANTHROPIC_API_KEY"):
1306+
if settings.environment == "PRODUCTION" and model_settings.anthropic_api_key:
13081307
return await self.get_context_window_from_anthropic_async()
13091308
return await self.get_context_window_from_tiktoken_async()
13101309

letta/functions/function_sets/multi_agent.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
import json
3-
import os
43
from concurrent.futures import ThreadPoolExecutor, as_completed
54
from typing import TYPE_CHECKING, List
65

@@ -138,7 +137,7 @@ def send_message_to_agent_async(self: "Agent", message: str, other_agent_id: str
138137
Returns:
139138
str: A confirmation message indicating the message was successfully sent.
140139
"""
141-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION":
140+
if settings.environment == "PRODUCTION":
142141
raise RuntimeError("This tool is not allowed to be run on Letta Cloud.")
143142

144143
message = (

letta/otel/resource.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import os
21
import sys
32
import uuid
43

54
from opentelemetry.sdk.resources import Resource
65

76
from letta import __version__ as letta_version
7+
from letta.settings import settings
88

99
_resources = {}
1010

1111

1212
def get_resource(service_name: str) -> Resource:
13-
_env = os.getenv("LETTA_ENVIRONMENT")
13+
_env = settings.environment
1414
if service_name not in _resources:
1515
resource_dict = {
1616
"service.name": service_name,

letta/services/agent_manager.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import os
32
from datetime import datetime, timezone
43
from typing import Any, Dict, List, Optional, Set, Tuple
54

@@ -3423,7 +3422,7 @@ async def get_context_window(self, agent_id: str, actor: PydanticUser) -> Contex
34233422
)
34243423
calculator = ContextWindowCalculator()
34253424

3426-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION" or agent_state.llm_config.model_endpoint_type == "anthropic":
3425+
if settings.environment == "PRODUCTION" or agent_state.llm_config.model_endpoint_type == "anthropic":
34273426
anthropic_client = LLMClient.create(provider_type=ProviderType.anthropic, actor=actor)
34283427
model = agent_state.llm_config.model if agent_state.llm_config.model_endpoint_type == "anthropic" else None
34293428

letta/services/helpers/agent_manager_helper.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
import uuid
32
from datetime import datetime
43
from typing import List, Literal, Optional, Set
@@ -1208,7 +1207,7 @@ def calculate_base_tools(is_v2: bool) -> Set[str]:
12081207

12091208
def calculate_multi_agent_tools() -> Set[str]:
12101209
"""Calculate multi-agent tools, excluding local-only tools in production environment."""
1211-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION":
1210+
if settings.environment == "PRODUCTION":
12121211
return set(MULTI_AGENT_TOOLS) - set(LOCAL_ONLY_MULTI_AGENT_TOOLS)
12131212
else:
12141213
return set(MULTI_AGENT_TOOLS)

letta/services/tool_executor/multi_agent_tool_executor.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import os
32
from typing import Any, Dict, List, Optional
43

54
from letta.log import get_logger
@@ -13,6 +12,7 @@
1312
from letta.schemas.tool_execution_result import ToolExecutionResult
1413
from letta.schemas.user import User
1514
from letta.services.tool_executor.tool_executor_base import ToolExecutor
15+
from letta.settings import settings
1616

1717
logger = get_logger(__name__)
1818

@@ -112,7 +112,7 @@ async def _process_agent(self, agent_id: str, message: str) -> Dict[str, Any]:
112112
}
113113

114114
async def send_message_to_agent_async(self, agent_state: AgentState, message: str, other_agent_id: str) -> str:
115-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION":
115+
if settings.environment == "PRODUCTION":
116116
raise RuntimeError("This tool is not allowed to be run on Letta Cloud.")
117117

118118
# 1) Build the prefixed system‐message

letta/services/tool_manager.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import importlib
2-
import os
32
import warnings
43
from typing import List, Optional, Set, Union
54

@@ -331,9 +330,7 @@ async def list_tools_async(
331330
# TODO: This requires a deeper rethink about how we keep all our internal tools up-to-date
332331
if not after and upsert_base_tools:
333332
existing_tool_names = {tool.name for tool in tools}
334-
base_tool_names = (
335-
LETTA_TOOL_SET - set(LOCAL_ONLY_MULTI_AGENT_TOOLS) if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION" else LETTA_TOOL_SET
336-
)
333+
base_tool_names = LETTA_TOOL_SET - set(LOCAL_ONLY_MULTI_AGENT_TOOLS) if settings.environment == "PRODUCTION" else LETTA_TOOL_SET
337334
missing_base_tools = base_tool_names - existing_tool_names
338335

339336
# If any base tools are missing, upsert all base tools

letta/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ class Settings(BaseSettings):
205205
letta_dir: Optional[Path] = Field(Path.home() / ".letta", alias="LETTA_DIR")
206206
debug: Optional[bool] = False
207207
cors_origins: Optional[list] = cors_origins
208+
environment: Optional[str] = Field(default=None, description="Application environment (PRODUCTION, DEV, etc.)")
208209

209210
# SSE Streaming keepalive settings
210211
enable_keepalive: bool = Field(True, description="Enable keepalive messages in SSE streams to prevent timeouts")

tests/test_managers.py

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
from letta.services.block_manager import BlockManager
102102
from letta.services.helpers.agent_manager_helper import calculate_base_tools, calculate_multi_agent_tools, validate_agent_exists_async
103103
from letta.services.step_manager import FeedbackType
104-
from letta.settings import tool_settings
104+
from letta.settings import settings, tool_settings
105105
from letta.utils import calculate_file_defaults_based_on_context_window
106106
from tests.helpers.utils import comprehensive_agent_checks, validate_context_window_overview
107107
from tests.utils import random_string
@@ -862,7 +862,7 @@ def test_calculate_multi_agent_tools(set_letta_environment):
862862
"""Test that calculate_multi_agent_tools excludes local-only tools in production."""
863863
result = calculate_multi_agent_tools()
864864

865-
if set_letta_environment == "PRODUCTION":
865+
if settings.environment == "PRODUCTION":
866866
# Production environment should exclude local-only tools
867867
expected_tools = set(MULTI_AGENT_TOOLS) - set(LOCAL_ONLY_MULTI_AGENT_TOOLS)
868868
assert result == expected_tools, "Production should exclude local-only multi-agent tools"
@@ -889,7 +889,7 @@ async def test_upsert_base_tools_excludes_local_only_in_production(server: SyncS
889889
tools = await server.tool_manager.upsert_base_tools_async(actor=default_user)
890890
tool_names = {tool.name for tool in tools}
891891

892-
if set_letta_environment == "PRODUCTION":
892+
if settings.environment == "PRODUCTION":
893893
# Production environment should exclude local-only multi-agent tools
894894
for local_only_tool in LOCAL_ONLY_MULTI_AGENT_TOOLS:
895895
assert local_only_tool not in tool_names, f"Local-only tool '{local_only_tool}' should not be upserted in production"
@@ -912,7 +912,7 @@ async def test_upsert_multi_agent_tools_only(server: SyncServer, default_user, s
912912
tools = await server.tool_manager.upsert_base_tools_async(actor=default_user, allowed_types={ToolType.LETTA_MULTI_AGENT_CORE})
913913
tool_names = {tool.name for tool in tools}
914914

915-
if set_letta_environment == "PRODUCTION":
915+
if settings.environment == "PRODUCTION":
916916
# Should only have non-local multi-agent tools
917917
expected_tools = set(MULTI_AGENT_TOOLS) - set(LOCAL_ONLY_MULTI_AGENT_TOOLS)
918918
assert tool_names == expected_tools, "Production multi-agent upsert should exclude local-only tools"
@@ -991,16 +991,14 @@ async def test_create_agent_with_default_source(server: SyncServer, default_user
991991
server.agent_manager.delete_agent(created_agent_no_source.id, default_user)
992992

993993

994-
@pytest.fixture(params=["", "PRODUCTION"])
995-
def set_letta_environment(request):
996-
original = os.environ.get("LETTA_ENVIRONMENT")
997-
os.environ["LETTA_ENVIRONMENT"] = request.param
994+
@pytest.fixture(params=[None, "PRODUCTION"])
995+
def set_letta_environment(request, monkeypatch):
996+
# Patch the settings.environment attribute
997+
original = settings.environment
998+
monkeypatch.setattr(settings, "environment", request.param)
998999
yield request.param
999-
# Restore original environment variable
1000-
if original is not None:
1001-
os.environ["LETTA_ENVIRONMENT"] = original
1002-
else:
1003-
os.environ.pop("LETTA_ENVIRONMENT", None)
1000+
# Restore original environment
1001+
monkeypatch.setattr(settings, "environment", original)
10041002

10051003

10061004
async def test_get_context_window_basic(
@@ -3801,7 +3799,7 @@ async def test_upsert_base_tools(server: SyncServer, default_user):
38013799
tools = await server.tool_manager.upsert_base_tools_async(actor=default_user)
38023800

38033801
# Calculate expected tools accounting for production filtering
3804-
if os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION":
3802+
if settings.environment == "PRODUCTION":
38053803
expected_tool_names = sorted(LETTA_TOOL_SET - set(LOCAL_ONLY_MULTI_AGENT_TOOLS))
38063804
else:
38073805
expected_tool_names = sorted(LETTA_TOOL_SET)
@@ -3853,7 +3851,7 @@ async def test_upsert_filtered_base_tools(server: SyncServer, default_user, tool
38533851
tool_names = sorted([t.name for t in tools])
38543852

38553853
# Adjust expected names for multi-agent tools in production
3856-
if tool_type == ToolType.LETTA_MULTI_AGENT_CORE and os.getenv("LETTA_ENVIRONMENT") == "PRODUCTION":
3854+
if tool_type == ToolType.LETTA_MULTI_AGENT_CORE and settings.environment == "PRODUCTION":
38573855
expected_sorted = sorted(set(expected_names) - set(LOCAL_ONLY_MULTI_AGENT_TOOLS))
38583856
else:
38593857
expected_sorted = sorted(expected_names)

0 commit comments

Comments
 (0)