letta-nightly 0.11.6.dev20250903104037__py3-none-any.whl → 0.11.7.dev20250904104046__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 +10 -14
- letta/agents/base_agent.py +18 -0
- letta/agents/helpers.py +32 -7
- letta/agents/letta_agent.py +953 -762
- letta/agents/voice_agent.py +1 -1
- letta/client/streaming.py +0 -1
- letta/constants.py +11 -8
- letta/errors.py +9 -0
- letta/functions/function_sets/base.py +77 -69
- letta/functions/function_sets/builtin.py +41 -22
- letta/functions/function_sets/multi_agent.py +1 -2
- letta/functions/schema_generator.py +0 -1
- letta/helpers/converters.py +8 -3
- letta/helpers/datetime_helpers.py +5 -4
- letta/helpers/message_helper.py +1 -2
- letta/helpers/pinecone_utils.py +0 -1
- letta/helpers/tool_rule_solver.py +10 -0
- letta/helpers/tpuf_client.py +848 -0
- letta/interface.py +8 -8
- letta/interfaces/anthropic_streaming_interface.py +7 -0
- letta/interfaces/openai_streaming_interface.py +29 -6
- letta/llm_api/anthropic_client.py +188 -18
- letta/llm_api/azure_client.py +0 -1
- letta/llm_api/bedrock_client.py +1 -2
- letta/llm_api/deepseek_client.py +319 -5
- letta/llm_api/google_vertex_client.py +75 -17
- letta/llm_api/groq_client.py +0 -1
- letta/llm_api/helpers.py +2 -2
- letta/llm_api/llm_api_tools.py +1 -50
- letta/llm_api/llm_client.py +6 -8
- letta/llm_api/mistral.py +1 -1
- letta/llm_api/openai.py +16 -13
- letta/llm_api/openai_client.py +31 -16
- letta/llm_api/together_client.py +0 -1
- letta/llm_api/xai_client.py +0 -1
- letta/local_llm/chat_completion_proxy.py +7 -6
- letta/local_llm/settings/settings.py +1 -1
- letta/orm/__init__.py +1 -0
- letta/orm/agent.py +8 -6
- letta/orm/archive.py +9 -1
- letta/orm/block.py +3 -4
- letta/orm/block_history.py +3 -1
- letta/orm/group.py +2 -3
- letta/orm/identity.py +1 -2
- letta/orm/job.py +1 -2
- letta/orm/llm_batch_items.py +1 -2
- letta/orm/message.py +8 -4
- letta/orm/mixins.py +18 -0
- letta/orm/organization.py +2 -0
- letta/orm/passage.py +8 -1
- letta/orm/passage_tag.py +55 -0
- letta/orm/sandbox_config.py +1 -3
- letta/orm/step.py +1 -2
- letta/orm/tool.py +1 -0
- letta/otel/resource.py +2 -2
- letta/plugins/plugins.py +1 -1
- letta/prompts/prompt_generator.py +10 -2
- letta/schemas/agent.py +11 -0
- letta/schemas/archive.py +4 -0
- letta/schemas/block.py +13 -0
- letta/schemas/embedding_config.py +0 -1
- letta/schemas/enums.py +24 -7
- letta/schemas/group.py +12 -0
- letta/schemas/letta_message.py +55 -1
- letta/schemas/letta_message_content.py +28 -0
- letta/schemas/letta_request.py +21 -4
- letta/schemas/letta_stop_reason.py +9 -1
- letta/schemas/llm_config.py +24 -8
- letta/schemas/mcp.py +0 -3
- letta/schemas/memory.py +14 -0
- letta/schemas/message.py +245 -141
- letta/schemas/openai/chat_completion_request.py +2 -1
- letta/schemas/passage.py +1 -0
- letta/schemas/providers/bedrock.py +1 -1
- letta/schemas/providers/openai.py +2 -2
- letta/schemas/tool.py +11 -5
- letta/schemas/tool_execution_result.py +0 -1
- letta/schemas/tool_rule.py +71 -0
- letta/serialize_schemas/marshmallow_agent.py +1 -2
- letta/server/rest_api/app.py +3 -3
- letta/server/rest_api/auth/index.py +0 -1
- letta/server/rest_api/interface.py +3 -11
- letta/server/rest_api/redis_stream_manager.py +3 -4
- letta/server/rest_api/routers/v1/agents.py +143 -84
- letta/server/rest_api/routers/v1/blocks.py +1 -1
- letta/server/rest_api/routers/v1/folders.py +1 -1
- letta/server/rest_api/routers/v1/groups.py +23 -22
- letta/server/rest_api/routers/v1/internal_templates.py +68 -0
- letta/server/rest_api/routers/v1/sandbox_configs.py +11 -5
- letta/server/rest_api/routers/v1/sources.py +1 -1
- letta/server/rest_api/routers/v1/tools.py +167 -15
- letta/server/rest_api/streaming_response.py +4 -3
- letta/server/rest_api/utils.py +75 -18
- letta/server/server.py +24 -35
- letta/services/agent_manager.py +359 -45
- letta/services/agent_serialization_manager.py +23 -3
- letta/services/archive_manager.py +72 -3
- letta/services/block_manager.py +1 -2
- letta/services/context_window_calculator/token_counter.py +11 -6
- letta/services/file_manager.py +1 -3
- letta/services/files_agents_manager.py +2 -4
- letta/services/group_manager.py +73 -12
- letta/services/helpers/agent_manager_helper.py +5 -5
- letta/services/identity_manager.py +8 -3
- letta/services/job_manager.py +2 -14
- letta/services/llm_batch_manager.py +1 -3
- letta/services/mcp/base_client.py +1 -2
- letta/services/mcp_manager.py +5 -6
- letta/services/message_manager.py +536 -15
- letta/services/organization_manager.py +1 -2
- letta/services/passage_manager.py +287 -12
- letta/services/provider_manager.py +1 -3
- letta/services/sandbox_config_manager.py +12 -7
- letta/services/source_manager.py +1 -2
- letta/services/step_manager.py +0 -1
- letta/services/summarizer/summarizer.py +4 -2
- letta/services/telemetry_manager.py +1 -3
- letta/services/tool_executor/builtin_tool_executor.py +136 -316
- letta/services/tool_executor/core_tool_executor.py +231 -74
- letta/services/tool_executor/files_tool_executor.py +2 -2
- letta/services/tool_executor/mcp_tool_executor.py +0 -1
- letta/services/tool_executor/multi_agent_tool_executor.py +2 -2
- letta/services/tool_executor/sandbox_tool_executor.py +0 -1
- letta/services/tool_executor/tool_execution_sandbox.py +2 -3
- letta/services/tool_manager.py +181 -64
- letta/services/tool_sandbox/modal_deployment_manager.py +2 -2
- letta/services/user_manager.py +1 -2
- letta/settings.py +5 -3
- letta/streaming_interface.py +3 -3
- letta/system.py +1 -1
- letta/utils.py +0 -1
- {letta_nightly-0.11.6.dev20250903104037.dist-info → letta_nightly-0.11.7.dev20250904104046.dist-info}/METADATA +11 -7
- {letta_nightly-0.11.6.dev20250903104037.dist-info → letta_nightly-0.11.7.dev20250904104046.dist-info}/RECORD +137 -135
- letta/llm_api/deepseek.py +0 -303
- {letta_nightly-0.11.6.dev20250903104037.dist-info → letta_nightly-0.11.7.dev20250904104046.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.6.dev20250903104037.dist-info → letta_nightly-0.11.7.dev20250904104046.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.6.dev20250903104037.dist-info → letta_nightly-0.11.7.dev20250904104046.dist-info}/licenses/LICENSE +0 -0
letta/__init__.py
CHANGED
letta/agent.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import asyncio
|
2
2
|
import json
|
3
|
-
import os
|
4
3
|
import time
|
5
4
|
import traceback
|
6
5
|
import warnings
|
@@ -50,9 +49,7 @@ from letta.schemas.enums import MessageRole, ProviderType, StepStatus, ToolType
|
|
50
49
|
from letta.schemas.letta_message_content import ImageContent, TextContent
|
51
50
|
from letta.schemas.memory import ContextWindowOverview, Memory
|
52
51
|
from letta.schemas.message import Message, MessageCreate, ToolReturn
|
53
|
-
from letta.schemas.openai.chat_completion_response import ChatCompletionResponse
|
54
|
-
from letta.schemas.openai.chat_completion_response import Message as ChatCompletionMessage
|
55
|
-
from letta.schemas.openai.chat_completion_response import UsageStatistics
|
52
|
+
from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Message as ChatCompletionMessage, UsageStatistics
|
56
53
|
from letta.schemas.response_format import ResponseFormatType
|
57
54
|
from letta.schemas.tool import Tool
|
58
55
|
from letta.schemas.tool_execution_result import ToolExecutionResult
|
@@ -71,7 +68,7 @@ from letta.services.step_manager import StepManager
|
|
71
68
|
from letta.services.telemetry_manager import NoopTelemetryManager, TelemetryManager
|
72
69
|
from letta.services.tool_executor.tool_execution_sandbox import ToolExecutionSandbox
|
73
70
|
from letta.services.tool_manager import ToolManager
|
74
|
-
from letta.settings import settings, summarizer_settings
|
71
|
+
from letta.settings import model_settings, settings, summarizer_settings
|
75
72
|
from letta.streaming_interface import StreamingRefreshCLIInterface
|
76
73
|
from letta.system import get_heartbeat, get_token_limit_warning, package_function_response, package_summarize_message, package_user_message
|
77
74
|
from letta.utils import count_tokens, get_friendly_error_msg, get_tool_call_id, log_telemetry, parse_json, validate_function_response
|
@@ -872,7 +869,6 @@ class Agent(BaseAgent):
|
|
872
869
|
) -> AgentStepResponse:
|
873
870
|
"""Runs a single step in the agent loop (generates at most one LLM call)"""
|
874
871
|
try:
|
875
|
-
|
876
872
|
# Extract job_id from metadata if present
|
877
873
|
job_id = metadata.get("job_id") if metadata else None
|
878
874
|
|
@@ -1085,9 +1081,9 @@ class Agent(BaseAgent):
|
|
1085
1081
|
-> agent.step(messages=[Message(role='user', text=...)])
|
1086
1082
|
"""
|
1087
1083
|
# Wrap with metadata, dumps to JSON
|
1088
|
-
assert user_message_str and isinstance(
|
1089
|
-
user_message_str,
|
1090
|
-
)
|
1084
|
+
assert user_message_str and isinstance(user_message_str, str), (
|
1085
|
+
f"user_message_str should be a non-empty string, got {type(user_message_str)}"
|
1086
|
+
)
|
1091
1087
|
user_message_json_str = package_user_message(user_message_str, self.agent_state.timezone)
|
1092
1088
|
|
1093
1089
|
# Validate JSON via save/load
|
@@ -1110,7 +1106,7 @@ class Agent(BaseAgent):
|
|
1110
1106
|
|
1111
1107
|
def summarize_messages_inplace(self):
|
1112
1108
|
in_context_messages = self.agent_manager.get_in_context_messages(agent_id=self.agent_state.id, actor=self.user)
|
1113
|
-
in_context_messages_openai =
|
1109
|
+
in_context_messages_openai = Message.to_openai_dicts_from_list(in_context_messages)
|
1114
1110
|
in_context_messages_openai_no_system = in_context_messages_openai[1:]
|
1115
1111
|
token_counts = get_token_counts_for_messages(in_context_messages)
|
1116
1112
|
logger.info(f"System message token count={token_counts[0]}")
|
@@ -1216,7 +1212,7 @@ class Agent(BaseAgent):
|
|
1216
1212
|
# Grab the in-context messages
|
1217
1213
|
# conversion of messages to OpenAI dict format, which is passed to the token counter
|
1218
1214
|
in_context_messages = self.agent_manager.get_in_context_messages(agent_id=self.agent_state.id, actor=self.user)
|
1219
|
-
in_context_messages_openai =
|
1215
|
+
in_context_messages_openai = Message.to_openai_dicts_from_list(in_context_messages)
|
1220
1216
|
|
1221
1217
|
# Check if there's a summary message in the message queue
|
1222
1218
|
if (
|
@@ -1304,7 +1300,7 @@ class Agent(BaseAgent):
|
|
1304
1300
|
)
|
1305
1301
|
|
1306
1302
|
async def get_context_window_async(self) -> ContextWindowOverview:
|
1307
|
-
if
|
1303
|
+
if settings.environment == "PRODUCTION" and model_settings.anthropic_api_key:
|
1308
1304
|
return await self.get_context_window_from_anthropic_async()
|
1309
1305
|
return await self.get_context_window_from_tiktoken_async()
|
1310
1306
|
|
@@ -1316,7 +1312,7 @@ class Agent(BaseAgent):
|
|
1316
1312
|
)
|
1317
1313
|
|
1318
1314
|
# conversion of messages to OpenAI dict format, which is passed to the token counter
|
1319
|
-
in_context_messages_openai =
|
1315
|
+
in_context_messages_openai = Message.to_openai_dicts_from_list(in_context_messages)
|
1320
1316
|
|
1321
1317
|
# Extract system, memory and external summary
|
1322
1318
|
if (
|
@@ -1446,7 +1442,7 @@ class Agent(BaseAgent):
|
|
1446
1442
|
)
|
1447
1443
|
|
1448
1444
|
# conversion of messages to anthropic dict format, which is passed to the token counter
|
1449
|
-
in_context_messages_anthropic =
|
1445
|
+
in_context_messages_anthropic = Message.to_anthropic_dicts_from_list(in_context_messages)
|
1450
1446
|
|
1451
1447
|
# Extract system, memory and external summary
|
1452
1448
|
if (
|
letta/agents/base_agent.py
CHANGED
@@ -102,6 +102,23 @@ class BaseAgent(ABC):
|
|
102
102
|
if tool_rules_solver is not None:
|
103
103
|
tool_constraint_block = tool_rules_solver.compile_tool_rule_prompts()
|
104
104
|
|
105
|
+
# compile archive tags if there's an attached archive
|
106
|
+
from letta.services.archive_manager import ArchiveManager
|
107
|
+
|
108
|
+
archive_manager = ArchiveManager()
|
109
|
+
archive = await archive_manager.get_default_archive_for_agent_async(
|
110
|
+
agent_id=agent_state.id,
|
111
|
+
actor=self.actor,
|
112
|
+
)
|
113
|
+
|
114
|
+
if archive:
|
115
|
+
archive_tags = await self.passage_manager.get_unique_tags_for_archive_async(
|
116
|
+
archive_id=archive.id,
|
117
|
+
actor=self.actor,
|
118
|
+
)
|
119
|
+
else:
|
120
|
+
archive_tags = None
|
121
|
+
|
105
122
|
# TODO: This is a pretty brittle pattern established all over our code, need to get rid of this
|
106
123
|
curr_system_message = in_context_messages[0]
|
107
124
|
curr_system_message_text = curr_system_message.content[0].text
|
@@ -149,6 +166,7 @@ class BaseAgent(ABC):
|
|
149
166
|
timezone=agent_state.timezone,
|
150
167
|
previous_message_count=num_messages - len(in_context_messages),
|
151
168
|
archival_memory_size=num_archival_memories,
|
169
|
+
archive_tags=archive_tags,
|
152
170
|
)
|
153
171
|
|
154
172
|
diff = united_diff(curr_system_message_text, new_system_message_str)
|
letta/agents/helpers.py
CHANGED
@@ -9,11 +9,11 @@ from letta.schemas.agent import AgentState
|
|
9
9
|
from letta.schemas.letta_message import MessageType
|
10
10
|
from letta.schemas.letta_response import LettaResponse
|
11
11
|
from letta.schemas.letta_stop_reason import LettaStopReason, StopReasonType
|
12
|
-
from letta.schemas.message import Message, MessageCreate
|
12
|
+
from letta.schemas.message import Message, MessageCreate, MessageCreateBase
|
13
13
|
from letta.schemas.tool_execution_result import ToolExecutionResult
|
14
14
|
from letta.schemas.usage import LettaUsageStatistics
|
15
15
|
from letta.schemas.user import User
|
16
|
-
from letta.server.rest_api.utils import create_input_messages
|
16
|
+
from letta.server.rest_api.utils import create_approval_response_message_from_input, create_input_messages
|
17
17
|
from letta.services.message_manager import MessageManager
|
18
18
|
|
19
19
|
logger = get_logger(__name__)
|
@@ -36,6 +36,8 @@ def _create_letta_response(
|
|
36
36
|
response_messages = Message.to_letta_messages_from_list(
|
37
37
|
messages=filter_user_messages, use_assistant_message=use_assistant_message, reverse=False
|
38
38
|
)
|
39
|
+
# Filter approval response messages
|
40
|
+
response_messages = [m for m in response_messages if m.message_type != "approval_response_message"]
|
39
41
|
|
40
42
|
# Apply message type filtering if specified
|
41
43
|
if include_return_message_types is not None:
|
@@ -115,13 +117,14 @@ async def _prepare_in_context_messages_async(
|
|
115
117
|
new_in_context_messages = await message_manager.create_many_messages_async(
|
116
118
|
create_input_messages(input_messages=input_messages, agent_id=agent_state.id, timezone=agent_state.timezone, actor=actor),
|
117
119
|
actor=actor,
|
120
|
+
embedding_config=agent_state.embedding_config,
|
118
121
|
)
|
119
122
|
|
120
123
|
return current_in_context_messages, new_in_context_messages
|
121
124
|
|
122
125
|
|
123
126
|
async def _prepare_in_context_messages_no_persist_async(
|
124
|
-
input_messages: List[
|
127
|
+
input_messages: List[MessageCreateBase],
|
125
128
|
agent_state: AgentState,
|
126
129
|
message_manager: MessageManager,
|
127
130
|
actor: User,
|
@@ -148,10 +151,32 @@ async def _prepare_in_context_messages_no_persist_async(
|
|
148
151
|
# Otherwise, include the full list of messages by ID for context
|
149
152
|
current_in_context_messages = await message_manager.get_messages_by_ids_async(message_ids=agent_state.message_ids, actor=actor)
|
150
153
|
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
154
|
+
# Check for approval-related message validation
|
155
|
+
if len(input_messages) == 1 and input_messages[0].type == "approval":
|
156
|
+
# User is trying to send an approval response
|
157
|
+
if current_in_context_messages[-1].role != "approval":
|
158
|
+
raise ValueError(
|
159
|
+
"Cannot process approval response: No tool call is currently awaiting approval. "
|
160
|
+
"Please send a regular message to interact with the agent."
|
161
|
+
)
|
162
|
+
if input_messages[0].approval_request_id != current_in_context_messages[-1].id:
|
163
|
+
raise ValueError(
|
164
|
+
f"Invalid approval request ID. Expected '{current_in_context_messages[-1].id}' "
|
165
|
+
f"but received '{input_messages[0].approval_request_id}'."
|
166
|
+
)
|
167
|
+
new_in_context_messages = create_approval_response_message_from_input(agent_state=agent_state, input_message=input_messages[0])
|
168
|
+
else:
|
169
|
+
# User is trying to send a regular message
|
170
|
+
if current_in_context_messages[-1].role == "approval":
|
171
|
+
raise ValueError(
|
172
|
+
"Cannot send a new message: The agent is waiting for approval on a tool call. "
|
173
|
+
"Please approve or deny the pending request before continuing."
|
174
|
+
)
|
175
|
+
|
176
|
+
# Create a new user message from the input but dont store it yet
|
177
|
+
new_in_context_messages = create_input_messages(
|
178
|
+
input_messages=input_messages, agent_id=agent_state.id, timezone=agent_state.timezone, actor=actor
|
179
|
+
)
|
155
180
|
|
156
181
|
return current_in_context_messages, new_in_context_messages
|
157
182
|
|