letta-nightly 0.11.7.dev20251006104136__py3-none-any.whl → 0.11.7.dev20251008104128__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/adapters/letta_llm_adapter.py +1 -0
- letta/adapters/letta_llm_request_adapter.py +0 -1
- letta/adapters/letta_llm_stream_adapter.py +7 -2
- letta/adapters/simple_llm_request_adapter.py +88 -0
- letta/adapters/simple_llm_stream_adapter.py +192 -0
- letta/agents/agent_loop.py +6 -0
- letta/agents/ephemeral_summary_agent.py +2 -1
- letta/agents/helpers.py +142 -6
- letta/agents/letta_agent.py +13 -33
- letta/agents/letta_agent_batch.py +2 -4
- letta/agents/letta_agent_v2.py +87 -77
- letta/agents/letta_agent_v3.py +899 -0
- letta/agents/voice_agent.py +2 -6
- letta/constants.py +8 -4
- letta/errors.py +40 -0
- letta/functions/function_sets/base.py +84 -4
- letta/functions/function_sets/multi_agent.py +0 -3
- letta/functions/schema_generator.py +113 -71
- letta/groups/dynamic_multi_agent.py +3 -2
- letta/groups/helpers.py +1 -2
- letta/groups/round_robin_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent_v2.py +1 -1
- letta/groups/sleeptime_multi_agent_v3.py +17 -17
- letta/groups/supervisor_multi_agent.py +84 -80
- letta/helpers/converters.py +3 -0
- letta/helpers/message_helper.py +4 -0
- letta/helpers/tool_rule_solver.py +92 -5
- letta/interfaces/anthropic_streaming_interface.py +409 -0
- letta/interfaces/gemini_streaming_interface.py +296 -0
- letta/interfaces/openai_streaming_interface.py +752 -1
- letta/llm_api/anthropic_client.py +126 -16
- letta/llm_api/bedrock_client.py +4 -2
- letta/llm_api/deepseek_client.py +4 -1
- letta/llm_api/google_vertex_client.py +123 -42
- letta/llm_api/groq_client.py +4 -1
- letta/llm_api/llm_api_tools.py +11 -4
- letta/llm_api/llm_client_base.py +6 -2
- letta/llm_api/openai.py +32 -2
- letta/llm_api/openai_client.py +423 -18
- letta/llm_api/xai_client.py +4 -1
- letta/main.py +9 -5
- letta/memory.py +1 -0
- letta/orm/__init__.py +1 -1
- letta/orm/agent.py +10 -0
- letta/orm/block.py +7 -16
- letta/orm/blocks_agents.py +8 -2
- letta/orm/files_agents.py +2 -0
- letta/orm/job.py +7 -5
- letta/orm/mcp_oauth.py +1 -0
- letta/orm/message.py +21 -6
- letta/orm/organization.py +2 -0
- letta/orm/provider.py +6 -2
- letta/orm/run.py +71 -0
- letta/orm/sandbox_config.py +7 -1
- letta/orm/sqlalchemy_base.py +0 -306
- letta/orm/step.py +6 -5
- letta/orm/step_metrics.py +5 -5
- letta/otel/tracing.py +28 -3
- letta/plugins/defaults.py +4 -4
- letta/prompts/system_prompts/__init__.py +2 -0
- letta/prompts/system_prompts/letta_v1.py +25 -0
- letta/schemas/agent.py +3 -2
- letta/schemas/agent_file.py +9 -3
- letta/schemas/block.py +23 -10
- letta/schemas/enums.py +21 -2
- letta/schemas/job.py +17 -4
- letta/schemas/letta_message_content.py +71 -2
- letta/schemas/letta_stop_reason.py +5 -5
- letta/schemas/llm_config.py +53 -3
- letta/schemas/memory.py +1 -1
- letta/schemas/message.py +504 -117
- letta/schemas/openai/responses_request.py +64 -0
- letta/schemas/providers/__init__.py +2 -0
- letta/schemas/providers/anthropic.py +16 -0
- letta/schemas/providers/ollama.py +115 -33
- letta/schemas/providers/openrouter.py +52 -0
- letta/schemas/providers/vllm.py +2 -1
- letta/schemas/run.py +48 -42
- letta/schemas/step.py +2 -2
- letta/schemas/step_metrics.py +1 -1
- letta/schemas/tool.py +15 -107
- letta/schemas/tool_rule.py +88 -5
- letta/serialize_schemas/marshmallow_agent.py +1 -0
- letta/server/db.py +86 -408
- letta/server/rest_api/app.py +61 -10
- letta/server/rest_api/dependencies.py +14 -0
- letta/server/rest_api/redis_stream_manager.py +19 -8
- letta/server/rest_api/routers/v1/agents.py +364 -292
- letta/server/rest_api/routers/v1/blocks.py +14 -20
- letta/server/rest_api/routers/v1/identities.py +45 -110
- letta/server/rest_api/routers/v1/internal_templates.py +21 -0
- letta/server/rest_api/routers/v1/jobs.py +23 -6
- letta/server/rest_api/routers/v1/messages.py +1 -1
- letta/server/rest_api/routers/v1/runs.py +126 -85
- letta/server/rest_api/routers/v1/sandbox_configs.py +10 -19
- letta/server/rest_api/routers/v1/tools.py +281 -594
- letta/server/rest_api/routers/v1/voice.py +1 -1
- letta/server/rest_api/streaming_response.py +29 -29
- letta/server/rest_api/utils.py +122 -64
- letta/server/server.py +160 -887
- letta/services/agent_manager.py +236 -919
- letta/services/agent_serialization_manager.py +16 -0
- letta/services/archive_manager.py +0 -100
- letta/services/block_manager.py +211 -168
- letta/services/file_manager.py +1 -1
- letta/services/files_agents_manager.py +24 -33
- letta/services/group_manager.py +0 -142
- letta/services/helpers/agent_manager_helper.py +7 -2
- letta/services/helpers/run_manager_helper.py +85 -0
- letta/services/job_manager.py +96 -411
- letta/services/lettuce/__init__.py +6 -0
- letta/services/lettuce/lettuce_client_base.py +86 -0
- letta/services/mcp_manager.py +38 -6
- letta/services/message_manager.py +165 -362
- letta/services/organization_manager.py +0 -36
- letta/services/passage_manager.py +0 -345
- letta/services/provider_manager.py +0 -80
- letta/services/run_manager.py +301 -0
- letta/services/sandbox_config_manager.py +0 -234
- letta/services/step_manager.py +62 -39
- letta/services/summarizer/summarizer.py +9 -7
- letta/services/telemetry_manager.py +0 -16
- letta/services/tool_executor/builtin_tool_executor.py +35 -0
- letta/services/tool_executor/core_tool_executor.py +397 -2
- letta/services/tool_executor/files_tool_executor.py +3 -3
- letta/services/tool_executor/multi_agent_tool_executor.py +30 -15
- letta/services/tool_executor/tool_execution_manager.py +6 -8
- letta/services/tool_executor/tool_executor_base.py +3 -3
- letta/services/tool_manager.py +85 -339
- letta/services/tool_sandbox/base.py +24 -13
- letta/services/tool_sandbox/e2b_sandbox.py +16 -1
- letta/services/tool_schema_generator.py +123 -0
- letta/services/user_manager.py +0 -99
- letta/settings.py +20 -4
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/METADATA +3 -5
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/RECORD +140 -132
- letta/agents/temporal/activities/__init__.py +0 -4
- letta/agents/temporal/activities/example_activity.py +0 -7
- letta/agents/temporal/activities/prepare_messages.py +0 -10
- letta/agents/temporal/temporal_agent_workflow.py +0 -56
- letta/agents/temporal/types.py +0 -25
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
2
3
|
|
3
4
|
from e2b.sandbox.commands.command_handle import CommandExitException
|
@@ -43,6 +44,7 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
|
|
43
44
|
agent_state: Optional[AgentState] = None,
|
44
45
|
additional_env_vars: Optional[Dict] = None,
|
45
46
|
) -> ToolExecutionResult:
|
47
|
+
await self._init_async()
|
46
48
|
if self.provided_sandbox_config:
|
47
49
|
sbx_config = self.provided_sandbox_config
|
48
50
|
else:
|
@@ -65,14 +67,24 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
|
|
65
67
|
code = await self.generate_execution_script(agent_state=agent_state)
|
66
68
|
|
67
69
|
try:
|
70
|
+
logger.info(f"E2B execution started for ID {e2b_sandbox.sandbox_id}: {self.tool_name}")
|
68
71
|
log_event(
|
69
72
|
"e2b_execution_started",
|
70
73
|
{"tool": self.tool_name, "sandbox_id": e2b_sandbox.sandbox_id, "code": code, "env_vars": envs},
|
71
74
|
)
|
72
|
-
|
75
|
+
try:
|
76
|
+
execution = await e2b_sandbox.run_code(code, envs=envs)
|
77
|
+
except asyncio.CancelledError:
|
78
|
+
logger.info(f"E2B execution cancelled for ID {e2b_sandbox.sandbox_id}: {self.tool_name}")
|
79
|
+
log_event(
|
80
|
+
"e2b_execution_cancelled",
|
81
|
+
{"tool": self.tool_name, "sandbox_id": e2b_sandbox.sandbox_id},
|
82
|
+
)
|
83
|
+
raise Exception("Execution cancelled. Transient failure, please retry.")
|
73
84
|
|
74
85
|
if execution.results:
|
75
86
|
func_return, agent_state = parse_stdout_best_effort(execution.results[0].text)
|
87
|
+
logger.info(f"E2B execution succeeded for ID {e2b_sandbox.sandbox_id}: {self.tool_name}")
|
76
88
|
log_event(
|
77
89
|
"e2b_execution_succeeded",
|
78
90
|
{
|
@@ -90,6 +102,7 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
|
|
90
102
|
function_name=self.tool_name, exception_name=execution.error.name, exception_message=execution.error.value
|
91
103
|
)
|
92
104
|
execution.logs.stderr.append(execution.error.traceback)
|
105
|
+
logger.info(f"E2B execution failed for ID {e2b_sandbox.sandbox_id}: {self.tool_name}")
|
93
106
|
log_event(
|
94
107
|
"e2b_execution_failed",
|
95
108
|
{
|
@@ -101,6 +114,7 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
|
|
101
114
|
},
|
102
115
|
)
|
103
116
|
else:
|
117
|
+
logger.info(f"E2B execution empty for ID {e2b_sandbox.sandbox_id}: {self.tool_name}")
|
104
118
|
log_event(
|
105
119
|
"e2b_execution_empty",
|
106
120
|
{
|
@@ -120,6 +134,7 @@ class AsyncToolSandboxE2B(AsyncToolSandboxBase):
|
|
120
134
|
sandbox_config_fingerprint=sbx_config.fingerprint(),
|
121
135
|
)
|
122
136
|
finally:
|
137
|
+
logger.info(f"E2B sandbox {e2b_sandbox.sandbox_id} killed")
|
123
138
|
await e2b_sandbox.kill()
|
124
139
|
|
125
140
|
@staticmethod
|
@@ -0,0 +1,123 @@
|
|
1
|
+
"""Schema generation utilities for tool creation and updates."""
|
2
|
+
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
from letta.functions.ast_parsers import get_function_name_and_docstring
|
6
|
+
from letta.functions.functions import derive_openai_json_schema
|
7
|
+
from letta.functions.helpers import generate_model_from_args_json_schema
|
8
|
+
from letta.functions.schema_generator import generate_schema_from_args_schema_v2
|
9
|
+
from letta.log import get_logger
|
10
|
+
from letta.schemas.enums import ToolSourceType, ToolType
|
11
|
+
from letta.schemas.tool import Tool as PydanticTool
|
12
|
+
|
13
|
+
logger = get_logger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def generate_schema_for_tool_creation(
|
17
|
+
tool: PydanticTool,
|
18
|
+
) -> Optional[dict]:
|
19
|
+
"""
|
20
|
+
Generate JSON schema for tool creation based on the provided parameters.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
tool: The tool being created
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
Generated JSON schema or None if not applicable
|
27
|
+
"""
|
28
|
+
# Only generate schema for custom tools
|
29
|
+
if tool.tool_type != ToolType.CUSTOM:
|
30
|
+
return None
|
31
|
+
|
32
|
+
# If json_schema is already provided, use it
|
33
|
+
if tool.json_schema:
|
34
|
+
return tool.json_schema
|
35
|
+
|
36
|
+
# Must have source code for custom tools
|
37
|
+
if not tool.source_code:
|
38
|
+
logger.error("Custom tool is missing source_code field")
|
39
|
+
raise ValueError("Custom tool is missing source_code field.")
|
40
|
+
|
41
|
+
# TypeScript tools
|
42
|
+
if tool.source_type == ToolSourceType.typescript:
|
43
|
+
try:
|
44
|
+
from letta.functions.typescript_parser import derive_typescript_json_schema
|
45
|
+
|
46
|
+
return derive_typescript_json_schema(source_code=tool.source_code)
|
47
|
+
except Exception as e:
|
48
|
+
logger.error(f"Failed to derive TypeScript json schema: {e}")
|
49
|
+
raise ValueError(f"Failed to derive TypeScript json schema: {e}")
|
50
|
+
|
51
|
+
# Python tools (default if not specified for backwards compatibility)
|
52
|
+
elif tool.source_type == ToolSourceType.python or tool.source_type is None:
|
53
|
+
# If args_json_schema is provided, use it to generate full schema
|
54
|
+
if tool.args_json_schema:
|
55
|
+
name, description = get_function_name_and_docstring(tool.source_code, tool.name)
|
56
|
+
args_schema = generate_model_from_args_json_schema(tool.args_json_schema)
|
57
|
+
return generate_schema_from_args_schema_v2(
|
58
|
+
args_schema=args_schema,
|
59
|
+
name=name,
|
60
|
+
description=description,
|
61
|
+
append_heartbeat=False,
|
62
|
+
)
|
63
|
+
# Otherwise, attempt to parse from docstring with best effort
|
64
|
+
else:
|
65
|
+
try:
|
66
|
+
return derive_openai_json_schema(source_code=tool.source_code)
|
67
|
+
except Exception as e:
|
68
|
+
logger.error(f"Failed to derive json schema: {e}")
|
69
|
+
raise ValueError(f"Failed to derive json schema: {e}")
|
70
|
+
else:
|
71
|
+
# TODO: convert to explicit error
|
72
|
+
raise ValueError(f"Unknown tool source type: {tool.source_type}")
|
73
|
+
|
74
|
+
|
75
|
+
def generate_schema_for_tool_update(
|
76
|
+
current_tool: PydanticTool,
|
77
|
+
json_schema: Optional[dict] = None,
|
78
|
+
args_json_schema: Optional[dict] = None,
|
79
|
+
source_code: Optional[str] = None,
|
80
|
+
source_type: Optional[ToolSourceType] = None,
|
81
|
+
) -> Optional[dict]:
|
82
|
+
"""
|
83
|
+
Generate JSON schema for tool update based on the provided parameters.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
current_tool: The current tool being updated
|
87
|
+
json_schema: Directly provided JSON schema (takes precedence)
|
88
|
+
args_json_schema: Schema for just the arguments
|
89
|
+
source_code: Updated source code (only used if explicitly updating source)
|
90
|
+
source_type: Source type for the tool
|
91
|
+
|
92
|
+
Returns:
|
93
|
+
Updated JSON schema or None if no update needed
|
94
|
+
"""
|
95
|
+
# Only handle custom tools
|
96
|
+
if current_tool.tool_type != ToolType.CUSTOM:
|
97
|
+
return None
|
98
|
+
|
99
|
+
# If json_schema is directly provided, use it
|
100
|
+
if json_schema is not None:
|
101
|
+
# If args_json_schema is also provided, that's an error
|
102
|
+
if args_json_schema is not None:
|
103
|
+
raise ValueError("Cannot provide both json_schema and args_json_schema in update")
|
104
|
+
return json_schema
|
105
|
+
|
106
|
+
# If args_json_schema is provided, generate full schema from it
|
107
|
+
if args_json_schema is not None:
|
108
|
+
# Use updated source_code if provided, otherwise use current
|
109
|
+
code_to_parse = source_code if source_code is not None else current_tool.source_code
|
110
|
+
if not code_to_parse:
|
111
|
+
raise ValueError("Source code required when updating with args_json_schema")
|
112
|
+
|
113
|
+
name, description = get_function_name_and_docstring(code_to_parse, current_tool.name)
|
114
|
+
args_schema = generate_model_from_args_json_schema(args_json_schema)
|
115
|
+
return generate_schema_from_args_schema_v2(
|
116
|
+
args_schema=args_schema,
|
117
|
+
name=name,
|
118
|
+
description=description,
|
119
|
+
append_heartbeat=False,
|
120
|
+
)
|
121
|
+
|
122
|
+
# Otherwise, no schema updates (don't parse docstring)
|
123
|
+
return None
|
letta/services/user_manager.py
CHANGED
@@ -23,27 +23,6 @@ class UserManager:
|
|
23
23
|
DEFAULT_USER_NAME = "default_user"
|
24
24
|
DEFAULT_USER_ID = "user-00000000-0000-4000-8000-000000000000"
|
25
25
|
|
26
|
-
@enforce_types
|
27
|
-
@trace_method
|
28
|
-
def create_default_user(self, org_id: str = DEFAULT_ORG_ID) -> PydanticUser:
|
29
|
-
"""Create the default user."""
|
30
|
-
with db_registry.session() as session:
|
31
|
-
# Make sure the org id exists
|
32
|
-
try:
|
33
|
-
OrganizationModel.read(db_session=session, identifier=org_id)
|
34
|
-
except NoResultFound:
|
35
|
-
raise ValueError(f"No organization with {org_id} exists in the organization table.")
|
36
|
-
|
37
|
-
# Try to retrieve the user
|
38
|
-
try:
|
39
|
-
user = UserModel.read(db_session=session, identifier=self.DEFAULT_USER_ID)
|
40
|
-
except NoResultFound:
|
41
|
-
# If it doesn't exist, make it
|
42
|
-
user = UserModel(id=self.DEFAULT_USER_ID, name=self.DEFAULT_USER_NAME, organization_id=org_id)
|
43
|
-
user.create(session)
|
44
|
-
|
45
|
-
return user.to_pydantic()
|
46
|
-
|
47
26
|
@enforce_types
|
48
27
|
@trace_method
|
49
28
|
async def create_default_actor_async(self, org_id: str = DEFAULT_ORG_ID) -> PydanticUser:
|
@@ -66,15 +45,6 @@ class UserManager:
|
|
66
45
|
|
67
46
|
return actor.to_pydantic()
|
68
47
|
|
69
|
-
@enforce_types
|
70
|
-
@trace_method
|
71
|
-
def create_user(self, pydantic_user: PydanticUser) -> PydanticUser:
|
72
|
-
"""Create a new user if it doesn't already exist."""
|
73
|
-
with db_registry.session() as session:
|
74
|
-
new_user = UserModel(**pydantic_user.model_dump(to_orm=True))
|
75
|
-
new_user.create(session)
|
76
|
-
return new_user.to_pydantic()
|
77
|
-
|
78
48
|
@enforce_types
|
79
49
|
@trace_method
|
80
50
|
async def create_actor_async(self, pydantic_user: PydanticUser) -> PydanticUser:
|
@@ -85,23 +55,6 @@ class UserManager:
|
|
85
55
|
await self._invalidate_actor_cache(new_user.id)
|
86
56
|
return new_user.to_pydantic()
|
87
57
|
|
88
|
-
@enforce_types
|
89
|
-
@trace_method
|
90
|
-
def update_user(self, user_update: UserUpdate) -> PydanticUser:
|
91
|
-
"""Update user details."""
|
92
|
-
with db_registry.session() as session:
|
93
|
-
# Retrieve the existing user by ID
|
94
|
-
existing_user = UserModel.read(db_session=session, identifier=user_update.id)
|
95
|
-
|
96
|
-
# Update only the fields that are provided in UserUpdate
|
97
|
-
update_data = user_update.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
98
|
-
for key, value in update_data.items():
|
99
|
-
setattr(existing_user, key, value)
|
100
|
-
|
101
|
-
# Commit the updated user
|
102
|
-
existing_user.update(session)
|
103
|
-
return existing_user.to_pydantic()
|
104
|
-
|
105
58
|
@enforce_types
|
106
59
|
@trace_method
|
107
60
|
async def update_actor_async(self, user_update: UserUpdate) -> PydanticUser:
|
@@ -120,17 +73,6 @@ class UserManager:
|
|
120
73
|
await self._invalidate_actor_cache(user_update.id)
|
121
74
|
return existing_user.to_pydantic()
|
122
75
|
|
123
|
-
@enforce_types
|
124
|
-
@trace_method
|
125
|
-
def delete_user_by_id(self, user_id: str):
|
126
|
-
"""Delete a user and their associated records (agents, sources, mappings)."""
|
127
|
-
with db_registry.session() as session:
|
128
|
-
# Delete from user table
|
129
|
-
user = UserModel.read(db_session=session, identifier=user_id)
|
130
|
-
user.hard_delete(session)
|
131
|
-
|
132
|
-
session.commit()
|
133
|
-
|
134
76
|
@enforce_types
|
135
77
|
@trace_method
|
136
78
|
async def delete_actor_by_id_async(self, user_id: str):
|
@@ -141,14 +83,6 @@ class UserManager:
|
|
141
83
|
await user.hard_delete_async(session)
|
142
84
|
await self._invalidate_actor_cache(user_id)
|
143
85
|
|
144
|
-
@enforce_types
|
145
|
-
@trace_method
|
146
|
-
def get_user_by_id(self, user_id: str) -> PydanticUser:
|
147
|
-
"""Fetch a user by ID."""
|
148
|
-
with db_registry.session() as session:
|
149
|
-
user = UserModel.read(db_session=session, identifier=user_id)
|
150
|
-
return user.to_pydantic()
|
151
|
-
|
152
86
|
@enforce_types
|
153
87
|
@trace_method
|
154
88
|
@async_redis_cache(key_func=lambda self, actor_id: f"actor_id:{actor_id}", model_class=PydanticUser)
|
@@ -164,27 +98,6 @@ class UserManager:
|
|
164
98
|
|
165
99
|
return user.to_pydantic()
|
166
100
|
|
167
|
-
@enforce_types
|
168
|
-
@trace_method
|
169
|
-
def get_default_user(self) -> PydanticUser:
|
170
|
-
"""Fetch the default user. If it doesn't exist, create it."""
|
171
|
-
try:
|
172
|
-
return self.get_user_by_id(self.DEFAULT_USER_ID)
|
173
|
-
except NoResultFound:
|
174
|
-
return self.create_default_user()
|
175
|
-
|
176
|
-
@enforce_types
|
177
|
-
@trace_method
|
178
|
-
def get_user_or_default(self, user_id: Optional[str] = None):
|
179
|
-
"""Fetch the user or default user."""
|
180
|
-
if not user_id:
|
181
|
-
return self.get_default_user()
|
182
|
-
|
183
|
-
try:
|
184
|
-
return self.get_user_by_id(user_id=user_id)
|
185
|
-
except NoResultFound:
|
186
|
-
return self.get_default_user()
|
187
|
-
|
188
101
|
@enforce_types
|
189
102
|
@trace_method
|
190
103
|
async def get_default_actor_async(self) -> PydanticUser:
|
@@ -206,18 +119,6 @@ class UserManager:
|
|
206
119
|
user = await self.create_default_actor_async(org_id=DEFAULT_ORG_ID)
|
207
120
|
return user
|
208
121
|
|
209
|
-
@enforce_types
|
210
|
-
@trace_method
|
211
|
-
def list_users(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticUser]:
|
212
|
-
"""List all users with optional pagination."""
|
213
|
-
with db_registry.session() as session:
|
214
|
-
users = UserModel.list(
|
215
|
-
db_session=session,
|
216
|
-
after=after,
|
217
|
-
limit=limit,
|
218
|
-
)
|
219
|
-
return [user.to_pydantic() for user in users]
|
220
|
-
|
221
122
|
@enforce_types
|
222
123
|
@trace_method
|
223
124
|
async def list_actors_async(self, after: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticUser]:
|
letta/settings.py
CHANGED
@@ -12,8 +12,6 @@ from letta.services.summarizer.enums import SummarizationMode
|
|
12
12
|
|
13
13
|
|
14
14
|
class ToolSettings(BaseSettings):
|
15
|
-
composio_api_key: str | None = Field(default=None, description="API key for Composio")
|
16
|
-
|
17
15
|
# Sandbox Configurations
|
18
16
|
e2b_api_key: str | None = Field(default=None, description="API key for using E2B as a tool sandbox")
|
19
17
|
e2b_sandbox_template_id: str | None = Field(default=None, description="Template ID for E2B Sandbox. Updated Manually.")
|
@@ -111,6 +109,14 @@ class ModelSettings(BaseSettings):
|
|
111
109
|
validation_alias=AliasChoices("OPENAI_BASE_URL", "OPENAI_API_BASE"), # pydantic-settings v1
|
112
110
|
)
|
113
111
|
|
112
|
+
# openrouter
|
113
|
+
openrouter_api_key: Optional[str] = None
|
114
|
+
# Optional additional headers recommended by OpenRouter
|
115
|
+
# See https://openrouter.ai/docs/quick-start for details
|
116
|
+
openrouter_referer: Optional[str] = None # e.g., your site URL
|
117
|
+
openrouter_title: Optional[str] = None # e.g., your app name
|
118
|
+
openrouter_handle_base: Optional[str] = None
|
119
|
+
|
114
120
|
# deepseek
|
115
121
|
deepseek_api_key: Optional[str] = None
|
116
122
|
|
@@ -129,6 +135,16 @@ class ModelSettings(BaseSettings):
|
|
129
135
|
# anthropic
|
130
136
|
anthropic_api_key: Optional[str] = None
|
131
137
|
anthropic_max_retries: int = 3
|
138
|
+
anthropic_sonnet_1m: bool = Field(
|
139
|
+
default=False,
|
140
|
+
description=(
|
141
|
+
"Enable 1M-token context window for Claude Sonnet 4/4.5. When true, adds the"
|
142
|
+
" 'context-1m-2025-08-07' beta to Anthropic requests and sets model context_window"
|
143
|
+
" to 1,000,000 instead of 200,000. Note: This feature is in beta and not available"
|
144
|
+
" to all orgs; once GA, this flag can be removed and behavior can default to on."
|
145
|
+
),
|
146
|
+
alias="ANTHROPIC_SONNET_1M",
|
147
|
+
)
|
132
148
|
|
133
149
|
# ollama
|
134
150
|
ollama_base_url: Optional[str] = None
|
@@ -156,6 +172,7 @@ class ModelSettings(BaseSettings):
|
|
156
172
|
|
157
173
|
# vLLM
|
158
174
|
vllm_api_base: Optional[str] = None
|
175
|
+
vllm_handle_base: Optional[str] = None
|
159
176
|
|
160
177
|
# lmstudio
|
161
178
|
lmstudio_base_url: Optional[str] = None
|
@@ -232,7 +249,7 @@ class Settings(BaseSettings):
|
|
232
249
|
pg_echo: bool = False # Logging
|
233
250
|
pool_pre_ping: bool = True # Pre ping to check for dead connections
|
234
251
|
pool_use_lifo: bool = True
|
235
|
-
disable_sqlalchemy_pooling: bool =
|
252
|
+
disable_sqlalchemy_pooling: bool = True
|
236
253
|
db_max_concurrent_sessions: Optional[int] = None
|
237
254
|
|
238
255
|
redis_host: Optional[str] = Field(default=None, description="Host for Redis instance")
|
@@ -271,7 +288,6 @@ class Settings(BaseSettings):
|
|
271
288
|
event_loop_threadpool_max_workers: int = 43
|
272
289
|
|
273
290
|
# experimental toggle
|
274
|
-
use_experimental: bool = False
|
275
291
|
use_vertex_structured_outputs_experimental: bool = False
|
276
292
|
use_asyncio_shield: bool = True
|
277
293
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: letta-nightly
|
3
|
-
Version: 0.11.7.
|
3
|
+
Version: 0.11.7.dev20251008104128
|
4
4
|
Summary: Create LLM agents with long-term memory and custom tools
|
5
5
|
Author-email: Letta Team <contact@letta.com>
|
6
6
|
License: Apache License
|
@@ -14,12 +14,12 @@ Requires-Dist: black[jupyter]>=24.2.0
|
|
14
14
|
Requires-Dist: brotli>=1.1.0
|
15
15
|
Requires-Dist: certifi>=2025.6.15
|
16
16
|
Requires-Dist: colorama>=0.4.6
|
17
|
-
Requires-Dist: composio-core>=0.7.7
|
18
17
|
Requires-Dist: datamodel-code-generator[http]>=0.25.0
|
19
18
|
Requires-Dist: demjson3>=3.0.6
|
20
19
|
Requires-Dist: docstring-parser<0.17,>=0.16
|
21
20
|
Requires-Dist: exa-py>=1.15.4
|
22
21
|
Requires-Dist: faker>=36.1.0
|
22
|
+
Requires-Dist: google-genai>=1.15.0
|
23
23
|
Requires-Dist: grpcio-tools>=1.68.1
|
24
24
|
Requires-Dist: grpcio>=1.68.1
|
25
25
|
Requires-Dist: html2text>=2020.1.16
|
@@ -109,8 +109,6 @@ Requires-Dist: langchain-community>=0.3.7; extra == 'external-tools'
|
|
109
109
|
Requires-Dist: langchain>=0.3.7; extra == 'external-tools'
|
110
110
|
Requires-Dist: turbopuffer>=0.5.17; extra == 'external-tools'
|
111
111
|
Requires-Dist: wikipedia>=1.4.0; extra == 'external-tools'
|
112
|
-
Provides-Extra: google
|
113
|
-
Requires-Dist: google-genai>=1.15.0; extra == 'google'
|
114
112
|
Provides-Extra: modal
|
115
113
|
Requires-Dist: modal>=1.1.0; extra == 'modal'
|
116
114
|
Provides-Extra: pinecone
|
@@ -417,7 +415,7 @@ agent_state = client.agents.create(
|
|
417
415
|
|
418
416
|
# Or attach tools to an existing agent
|
419
417
|
client.agents.tool.attach(
|
420
|
-
agent_id=agent_state.id
|
418
|
+
agent_id=agent_state.id
|
421
419
|
tool_id=tool.id
|
422
420
|
)
|
423
421
|
|