letta-nightly 0.12.1.dev20251023104211__py3-none-any.whl → 0.13.0.dev20251024223017__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.
Potentially problematic release.
This version of letta-nightly might be problematic. Click here for more details.
- letta/__init__.py +2 -3
- letta/adapters/letta_llm_adapter.py +1 -0
- letta/adapters/simple_llm_request_adapter.py +8 -5
- letta/adapters/simple_llm_stream_adapter.py +22 -6
- letta/agents/agent_loop.py +10 -3
- letta/agents/base_agent.py +4 -1
- letta/agents/helpers.py +41 -9
- letta/agents/letta_agent.py +11 -10
- letta/agents/letta_agent_v2.py +47 -37
- letta/agents/letta_agent_v3.py +395 -300
- letta/agents/voice_agent.py +8 -6
- letta/agents/voice_sleeptime_agent.py +3 -3
- letta/constants.py +30 -7
- letta/errors.py +20 -0
- letta/functions/function_sets/base.py +55 -3
- letta/functions/mcp_client/types.py +33 -57
- letta/functions/schema_generator.py +135 -23
- letta/groups/sleeptime_multi_agent_v3.py +6 -11
- letta/groups/sleeptime_multi_agent_v4.py +227 -0
- letta/helpers/converters.py +78 -4
- letta/helpers/crypto_utils.py +6 -2
- letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py +9 -11
- letta/interfaces/anthropic_streaming_interface.py +3 -4
- letta/interfaces/gemini_streaming_interface.py +4 -6
- letta/interfaces/openai_streaming_interface.py +63 -28
- letta/llm_api/anthropic_client.py +7 -4
- letta/llm_api/deepseek_client.py +6 -4
- letta/llm_api/google_ai_client.py +3 -12
- letta/llm_api/google_vertex_client.py +1 -1
- letta/llm_api/helpers.py +90 -61
- letta/llm_api/llm_api_tools.py +4 -1
- letta/llm_api/openai.py +12 -12
- letta/llm_api/openai_client.py +53 -16
- letta/local_llm/constants.py +4 -3
- letta/local_llm/json_parser.py +5 -2
- letta/local_llm/utils.py +2 -3
- letta/log.py +171 -7
- letta/orm/agent.py +43 -9
- letta/orm/archive.py +4 -0
- letta/orm/custom_columns.py +15 -0
- letta/orm/identity.py +11 -11
- letta/orm/mcp_server.py +9 -0
- letta/orm/message.py +6 -1
- letta/orm/run_metrics.py +7 -2
- letta/orm/sqlalchemy_base.py +2 -2
- letta/orm/tool.py +3 -0
- letta/otel/tracing.py +2 -0
- letta/prompts/prompt_generator.py +7 -2
- letta/schemas/agent.py +41 -10
- letta/schemas/agent_file.py +3 -0
- letta/schemas/archive.py +4 -2
- letta/schemas/block.py +2 -1
- letta/schemas/enums.py +36 -3
- letta/schemas/file.py +3 -3
- letta/schemas/folder.py +2 -1
- letta/schemas/group.py +2 -1
- letta/schemas/identity.py +18 -9
- letta/schemas/job.py +3 -1
- letta/schemas/letta_message.py +71 -12
- letta/schemas/letta_request.py +7 -3
- letta/schemas/letta_stop_reason.py +0 -25
- letta/schemas/llm_config.py +8 -2
- letta/schemas/mcp.py +80 -83
- letta/schemas/mcp_server.py +349 -0
- letta/schemas/memory.py +20 -8
- letta/schemas/message.py +212 -67
- letta/schemas/providers/anthropic.py +13 -6
- letta/schemas/providers/azure.py +6 -4
- letta/schemas/providers/base.py +8 -4
- letta/schemas/providers/bedrock.py +6 -2
- letta/schemas/providers/cerebras.py +7 -3
- letta/schemas/providers/deepseek.py +2 -1
- letta/schemas/providers/google_gemini.py +15 -6
- letta/schemas/providers/groq.py +2 -1
- letta/schemas/providers/lmstudio.py +9 -6
- letta/schemas/providers/mistral.py +2 -1
- letta/schemas/providers/openai.py +7 -2
- letta/schemas/providers/together.py +9 -3
- letta/schemas/providers/xai.py +7 -3
- letta/schemas/run.py +7 -2
- letta/schemas/run_metrics.py +2 -1
- letta/schemas/sandbox_config.py +2 -2
- letta/schemas/secret.py +3 -158
- letta/schemas/source.py +2 -2
- letta/schemas/step.py +2 -2
- letta/schemas/tool.py +24 -1
- letta/schemas/usage.py +0 -1
- letta/server/rest_api/app.py +123 -7
- letta/server/rest_api/dependencies.py +3 -0
- letta/server/rest_api/interface.py +7 -4
- letta/server/rest_api/redis_stream_manager.py +16 -1
- letta/server/rest_api/routers/v1/__init__.py +7 -0
- letta/server/rest_api/routers/v1/agents.py +332 -322
- letta/server/rest_api/routers/v1/archives.py +127 -40
- letta/server/rest_api/routers/v1/blocks.py +54 -6
- letta/server/rest_api/routers/v1/chat_completions.py +146 -0
- letta/server/rest_api/routers/v1/folders.py +27 -35
- letta/server/rest_api/routers/v1/groups.py +23 -35
- letta/server/rest_api/routers/v1/identities.py +24 -10
- letta/server/rest_api/routers/v1/internal_runs.py +107 -0
- letta/server/rest_api/routers/v1/internal_templates.py +162 -179
- letta/server/rest_api/routers/v1/jobs.py +15 -27
- letta/server/rest_api/routers/v1/mcp_servers.py +309 -0
- letta/server/rest_api/routers/v1/messages.py +23 -34
- letta/server/rest_api/routers/v1/organizations.py +6 -27
- letta/server/rest_api/routers/v1/providers.py +35 -62
- letta/server/rest_api/routers/v1/runs.py +30 -43
- letta/server/rest_api/routers/v1/sandbox_configs.py +6 -4
- letta/server/rest_api/routers/v1/sources.py +26 -42
- letta/server/rest_api/routers/v1/steps.py +16 -29
- letta/server/rest_api/routers/v1/tools.py +17 -13
- letta/server/rest_api/routers/v1/users.py +5 -17
- letta/server/rest_api/routers/v1/voice.py +18 -27
- letta/server/rest_api/streaming_response.py +5 -2
- letta/server/rest_api/utils.py +187 -25
- letta/server/server.py +27 -22
- letta/server/ws_api/server.py +5 -4
- letta/services/agent_manager.py +148 -26
- letta/services/agent_serialization_manager.py +6 -1
- letta/services/archive_manager.py +168 -15
- letta/services/block_manager.py +14 -4
- letta/services/file_manager.py +33 -29
- letta/services/group_manager.py +10 -0
- letta/services/helpers/agent_manager_helper.py +65 -11
- letta/services/identity_manager.py +105 -4
- letta/services/job_manager.py +11 -1
- letta/services/mcp/base_client.py +2 -2
- letta/services/mcp/oauth_utils.py +33 -8
- letta/services/mcp_manager.py +174 -78
- letta/services/mcp_server_manager.py +1331 -0
- letta/services/message_manager.py +109 -4
- letta/services/organization_manager.py +4 -4
- letta/services/passage_manager.py +9 -25
- letta/services/provider_manager.py +91 -15
- letta/services/run_manager.py +72 -15
- letta/services/sandbox_config_manager.py +45 -3
- letta/services/source_manager.py +15 -8
- letta/services/step_manager.py +24 -1
- letta/services/streaming_service.py +581 -0
- letta/services/summarizer/summarizer.py +1 -1
- letta/services/tool_executor/core_tool_executor.py +111 -0
- letta/services/tool_executor/files_tool_executor.py +5 -3
- letta/services/tool_executor/sandbox_tool_executor.py +2 -2
- letta/services/tool_executor/tool_execution_manager.py +1 -1
- letta/services/tool_manager.py +10 -3
- letta/services/tool_sandbox/base.py +61 -1
- letta/services/tool_sandbox/local_sandbox.py +1 -3
- letta/services/user_manager.py +2 -2
- letta/settings.py +49 -5
- letta/system.py +14 -5
- letta/utils.py +73 -1
- letta/validators.py +105 -0
- {letta_nightly-0.12.1.dev20251023104211.dist-info → letta_nightly-0.13.0.dev20251024223017.dist-info}/METADATA +4 -2
- {letta_nightly-0.12.1.dev20251023104211.dist-info → letta_nightly-0.13.0.dev20251024223017.dist-info}/RECORD +157 -151
- letta/schemas/letta_ping.py +0 -28
- letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
- {letta_nightly-0.12.1.dev20251023104211.dist-info → letta_nightly-0.13.0.dev20251024223017.dist-info}/WHEEL +0 -0
- {letta_nightly-0.12.1.dev20251023104211.dist-info → letta_nightly-0.13.0.dev20251024223017.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.12.1.dev20251023104211.dist-info → letta_nightly-0.13.0.dev20251024223017.dist-info}/licenses/LICENSE +0 -0
letta/orm/identity.py
CHANGED
|
@@ -44,15 +44,15 @@ class Identity(SqlalchemyBase, OrganizationMixin, ProjectMixin):
|
|
|
44
44
|
"Block", secondary="identities_blocks", lazy="selectin", passive_deletes=True, back_populates="identities"
|
|
45
45
|
)
|
|
46
46
|
|
|
47
|
-
@property
|
|
48
|
-
def agent_ids(self) -> List[str]:
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
# @property
|
|
48
|
+
# def agent_ids(self) -> List[str]:
|
|
49
|
+
# """Get just the agent IDs without loading the full agent objects"""
|
|
50
|
+
# return [agent.id for agent in self.agents]
|
|
51
51
|
|
|
52
|
-
@property
|
|
53
|
-
def block_ids(self) -> List[str]:
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
# @property
|
|
53
|
+
# def block_ids(self) -> List[str]:
|
|
54
|
+
# """Get just the block IDs without loading the full agent objects"""
|
|
55
|
+
# return [block.id for block in self.blocks]
|
|
56
56
|
|
|
57
57
|
def to_pydantic(self) -> PydanticIdentity:
|
|
58
58
|
state = {
|
|
@@ -61,9 +61,9 @@ class Identity(SqlalchemyBase, OrganizationMixin, ProjectMixin):
|
|
|
61
61
|
"name": self.name,
|
|
62
62
|
"identity_type": self.identity_type,
|
|
63
63
|
"project_id": self.project_id,
|
|
64
|
-
"agent_ids":
|
|
65
|
-
"block_ids":
|
|
64
|
+
"agent_ids": [],
|
|
65
|
+
"block_ids": [],
|
|
66
66
|
"organization_id": self.organization_id,
|
|
67
|
-
"properties": self.properties,
|
|
67
|
+
"properties": self.properties or [],
|
|
68
68
|
}
|
|
69
69
|
return PydanticIdentity(**state)
|
letta/orm/mcp_server.py
CHANGED
|
@@ -56,3 +56,12 @@ class MCPServer(SqlalchemyBase, OrganizationMixin):
|
|
|
56
56
|
metadata_: Mapped[Optional[dict]] = mapped_column(
|
|
57
57
|
JSON, default=lambda: {}, doc="A dictionary of additional metadata for the MCP server."
|
|
58
58
|
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class MCPTools(SqlalchemyBase, OrganizationMixin):
|
|
62
|
+
"""Represents a mapping of MCP server ID to tool ID"""
|
|
63
|
+
|
|
64
|
+
__tablename__ = "mcp_tools"
|
|
65
|
+
|
|
66
|
+
mcp_server_id: Mapped[str] = mapped_column(String, doc="The ID of the MCP server")
|
|
67
|
+
tool_id: Mapped[str] = mapped_column(String, doc="The ID of the tool")
|
letta/orm/message.py
CHANGED
|
@@ -4,10 +4,11 @@ from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMe
|
|
|
4
4
|
from sqlalchemy import BigInteger, FetchedValue, ForeignKey, Index, event, text
|
|
5
5
|
from sqlalchemy.orm import Mapped, Session, mapped_column, relationship
|
|
6
6
|
|
|
7
|
-
from letta.orm.custom_columns import MessageContentColumn, ToolCallColumn, ToolReturnColumn
|
|
7
|
+
from letta.orm.custom_columns import ApprovalsColumn, MessageContentColumn, ToolCallColumn, ToolReturnColumn
|
|
8
8
|
from letta.orm.mixins import AgentMixin, OrganizationMixin
|
|
9
9
|
from letta.orm.sqlalchemy_base import SqlalchemyBase
|
|
10
10
|
from letta.schemas.enums import MessageRole
|
|
11
|
+
from letta.schemas.letta_message import ApprovalReturn
|
|
11
12
|
from letta.schemas.letta_message_content import MessageContent, TextContent, TextContent as PydanticTextContent
|
|
12
13
|
from letta.schemas.message import Message as PydanticMessage, ToolReturn
|
|
13
14
|
from letta.settings import DatabaseChoice, settings
|
|
@@ -63,6 +64,9 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
|
|
|
63
64
|
)
|
|
64
65
|
approve: Mapped[Optional[bool]] = mapped_column(nullable=True, doc="Whether tool call is approved.")
|
|
65
66
|
denial_reason: Mapped[Optional[str]] = mapped_column(nullable=True, doc="The reason the tool call request was denied.")
|
|
67
|
+
approvals: Mapped[Optional[List[ApprovalReturn | ToolReturn]]] = mapped_column(
|
|
68
|
+
ApprovalsColumn, nullable=True, doc="Approval responses for tool call requests"
|
|
69
|
+
)
|
|
66
70
|
|
|
67
71
|
# Monotonically increasing sequence for efficient/correct listing
|
|
68
72
|
sequence_id: Mapped[int] = mapped_column(
|
|
@@ -100,6 +104,7 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
|
|
|
100
104
|
and len(self.content) == 1
|
|
101
105
|
and isinstance(self.content[0], TextContent)
|
|
102
106
|
):
|
|
107
|
+
self.tool_call_id = self.tool_returns[0].tool_call_id
|
|
103
108
|
self.tool_returns[0].func_response = self.content[0].text
|
|
104
109
|
|
|
105
110
|
return model
|
letta/orm/run_metrics.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from datetime import datetime, timezone
|
|
2
|
-
from typing import TYPE_CHECKING, Optional
|
|
2
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
3
3
|
|
|
4
|
-
from sqlalchemy import BigInteger, ForeignKey, Integer, String
|
|
4
|
+
from sqlalchemy import JSON, BigInteger, ForeignKey, Integer, String
|
|
5
5
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
6
6
|
from sqlalchemy.orm import Mapped, Session, mapped_column, relationship
|
|
7
7
|
|
|
@@ -43,6 +43,11 @@ class RunMetrics(SqlalchemyBase, ProjectMixin, AgentMixin, OrganizationMixin, Te
|
|
|
43
43
|
nullable=True,
|
|
44
44
|
doc="The number of steps in the run",
|
|
45
45
|
)
|
|
46
|
+
tools_used: Mapped[Optional[List[str]]] = mapped_column(
|
|
47
|
+
JSON,
|
|
48
|
+
nullable=True,
|
|
49
|
+
doc="List of tool IDs that were used in this run",
|
|
50
|
+
)
|
|
46
51
|
run: Mapped[Optional["Run"]] = relationship("Run", foreign_keys=[id])
|
|
47
52
|
agent: Mapped[Optional["Agent"]] = relationship("Agent")
|
|
48
53
|
|
letta/orm/sqlalchemy_base.py
CHANGED
|
@@ -269,14 +269,14 @@ class SqlalchemyBase(CommonSqlalchemyMetaMixins, Base):
|
|
|
269
269
|
if before_obj:
|
|
270
270
|
conditions.append(
|
|
271
271
|
or_(
|
|
272
|
-
cls.created_at < before_obj.created_at,
|
|
272
|
+
cls.created_at < before_obj.created_at if ascending else cls.created_at > before_obj.created_at,
|
|
273
273
|
and_(cls.created_at == before_obj.created_at, cls.id < before_obj.id),
|
|
274
274
|
)
|
|
275
275
|
)
|
|
276
276
|
if after_obj:
|
|
277
277
|
conditions.append(
|
|
278
278
|
or_(
|
|
279
|
-
cls.created_at > after_obj.created_at,
|
|
279
|
+
cls.created_at > after_obj.created_at if ascending else cls.created_at < after_obj.created_at,
|
|
280
280
|
and_(cls.created_at == after_obj.created_at, cls.id > after_obj.id),
|
|
281
281
|
)
|
|
282
282
|
)
|
letta/orm/tool.py
CHANGED
|
@@ -50,6 +50,9 @@ class Tool(SqlalchemyBase, OrganizationMixin):
|
|
|
50
50
|
)
|
|
51
51
|
npm_requirements: Mapped[list | None] = mapped_column(JSON, doc="Optional list of npm packages required by this tool.")
|
|
52
52
|
default_requires_approval: Mapped[bool] = mapped_column(nullable=True, doc="Whether or not to require approval.")
|
|
53
|
+
enable_parallel_execution: Mapped[bool] = mapped_column(
|
|
54
|
+
nullable=True, doc="If set to True, then this tool will potentially be executed concurrently with other tools. Default False."
|
|
55
|
+
)
|
|
53
56
|
metadata_: Mapped[Optional[dict]] = mapped_column(JSON, default=lambda: {}, doc="A dictionary of additional metadata for the tool.")
|
|
54
57
|
# relationships
|
|
55
58
|
organization: Mapped["Organization"] = relationship("Organization", back_populates="tools", lazy="selectin")
|
letta/otel/tracing.py
CHANGED
|
@@ -87,6 +87,8 @@ async def _update_trace_attributes(request: Request):
|
|
|
87
87
|
"x-template-id": "template.id",
|
|
88
88
|
"x-base-template-id": "base_template.id",
|
|
89
89
|
"user-agent": "client",
|
|
90
|
+
"x-stainless-package-version": "sdk.version",
|
|
91
|
+
"x-stainless-lang": "sdk.language",
|
|
90
92
|
}
|
|
91
93
|
for header_key, span_key in header_attributes.items():
|
|
92
94
|
header_value = request.headers.get(header_key)
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import List, Literal, Optional
|
|
3
3
|
|
|
4
|
+
from letta.log import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
4
8
|
from letta.constants import IN_CONTEXT_MEMORY_KEYWORD
|
|
5
9
|
from letta.helpers import ToolRulesSolver
|
|
6
10
|
from letta.helpers.datetime_helpers import format_datetime, get_local_time_fast
|
|
@@ -137,7 +141,7 @@ class PromptGenerator:
|
|
|
137
141
|
if append_icm_if_missing:
|
|
138
142
|
if memory_variable_string not in system_prompt:
|
|
139
143
|
# In this case, append it to the end to make sure memory is still injected
|
|
140
|
-
#
|
|
144
|
+
# logger.warning(f"{IN_CONTEXT_MEMORY_KEYWORD} variable was missing from system prompt, appending instead")
|
|
141
145
|
system_prompt += "\n\n" + memory_variable_string
|
|
142
146
|
|
|
143
147
|
# render the variables using the built-in templater
|
|
@@ -170,6 +174,7 @@ class PromptGenerator:
|
|
|
170
174
|
tool_rules_solver: Optional[ToolRulesSolver] = None,
|
|
171
175
|
sources: Optional[List] = None,
|
|
172
176
|
max_files_open: Optional[int] = None,
|
|
177
|
+
llm_config: Optional[object] = None,
|
|
173
178
|
) -> str:
|
|
174
179
|
tool_constraint_block = None
|
|
175
180
|
if tool_rules_solver is not None:
|
|
@@ -182,7 +187,7 @@ class PromptGenerator:
|
|
|
182
187
|
pass
|
|
183
188
|
|
|
184
189
|
memory_with_sources = in_context_memory.compile(
|
|
185
|
-
tool_usage_rules=tool_constraint_block, sources=sources, max_files_open=max_files_open
|
|
190
|
+
tool_usage_rules=tool_constraint_block, sources=sources, max_files_open=max_files_open, llm_config=llm_config
|
|
186
191
|
)
|
|
187
192
|
|
|
188
193
|
return PromptGenerator.get_system_message_from_compiled_memory(
|
letta/schemas/agent.py
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from enum import Enum
|
|
3
|
-
from typing import Dict, List, Optional
|
|
3
|
+
from typing import Dict, List, Literal, Optional
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
6
6
|
|
|
7
7
|
from letta.constants import CORE_MEMORY_LINE_NUMBER_WARNING, DEFAULT_EMBEDDING_CHUNK_SIZE
|
|
8
|
-
from letta.
|
|
8
|
+
from letta.errors import AgentExportProcessingError
|
|
9
|
+
from letta.schemas.block import Block, CreateBlock
|
|
9
10
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
11
|
+
from letta.schemas.enums import PrimitiveType
|
|
10
12
|
from letta.schemas.environment_variables import AgentEnvironmentVariable
|
|
11
13
|
from letta.schemas.file import FileStatus
|
|
12
14
|
from letta.schemas.group import Group
|
|
15
|
+
from letta.schemas.identity import Identity
|
|
13
16
|
from letta.schemas.letta_base import OrmMetadataBase
|
|
14
17
|
from letta.schemas.llm_config import LLMConfig
|
|
15
18
|
from letta.schemas.memory import Memory
|
|
@@ -40,6 +43,18 @@ class AgentType(str, Enum):
|
|
|
40
43
|
voice_sleeptime_agent = "voice_sleeptime_agent"
|
|
41
44
|
|
|
42
45
|
|
|
46
|
+
# Relationship field literal type for AgentState include field to join related objects
|
|
47
|
+
AgentRelationships = Literal[
|
|
48
|
+
"agent.blocks",
|
|
49
|
+
"agent.identities",
|
|
50
|
+
"agent.managed_group",
|
|
51
|
+
"agent.secrets",
|
|
52
|
+
"agent.sources",
|
|
53
|
+
"agent.tags",
|
|
54
|
+
"agent.tools",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
43
58
|
class AgentState(OrmMetadataBase, validate_assignment=True):
|
|
44
59
|
"""
|
|
45
60
|
Representation of an agent's state. This is the state of the agent at a given time, and is persisted in the DB backend. The state has all the information needed to recreate a persisted agent.
|
|
@@ -56,7 +71,7 @@ class AgentState(OrmMetadataBase, validate_assignment=True):
|
|
|
56
71
|
embedding_config (EmbeddingConfig): The embedding configuration used by the agent.
|
|
57
72
|
"""
|
|
58
73
|
|
|
59
|
-
__id_prefix__ =
|
|
74
|
+
__id_prefix__ = PrimitiveType.AGENT.value
|
|
60
75
|
|
|
61
76
|
# NOTE: this is what is returned to the client and also what is used to initialize `Agent`
|
|
62
77
|
id: str = Field(..., description="The id of the agent. Assigned by the database.")
|
|
@@ -84,7 +99,8 @@ class AgentState(OrmMetadataBase, validate_assignment=True):
|
|
|
84
99
|
description: Optional[str] = Field(None, description="The description of the agent.")
|
|
85
100
|
metadata: Optional[Dict] = Field(None, description="The metadata of the agent.")
|
|
86
101
|
|
|
87
|
-
memory: Memory = Field(..., description="The in-context memory of the agent.")
|
|
102
|
+
memory: Memory = Field(..., description="The in-context memory of the agent.", deprecated=True)
|
|
103
|
+
blocks: List[Block] = Field(..., description="The memory blocks used by the agent.")
|
|
88
104
|
tools: List[Tool] = Field(..., description="The tools used by the agent.")
|
|
89
105
|
sources: List[Source] = Field(..., description="The sources used by the agent.")
|
|
90
106
|
tags: List[str] = Field(..., description="The tags associated with the agent.")
|
|
@@ -101,7 +117,8 @@ class AgentState(OrmMetadataBase, validate_assignment=True):
|
|
|
101
117
|
base_template_id: Optional[str] = Field(None, description="The base template id of the agent.")
|
|
102
118
|
deployment_id: Optional[str] = Field(None, description="The id of the deployment.")
|
|
103
119
|
entity_id: Optional[str] = Field(None, description="The id of the entity within the template.")
|
|
104
|
-
identity_ids: List[str] = Field([], description="The ids of the identities associated with this agent.")
|
|
120
|
+
identity_ids: List[str] = Field([], description="The ids of the identities associated with this agent.", deprecated=True)
|
|
121
|
+
identities: List[Identity] = Field([], description="The identities associated with this agent.")
|
|
105
122
|
|
|
106
123
|
# An advanced configuration that makes it so this agent does not remember any previous messages
|
|
107
124
|
message_buffer_autoclear: bool = Field(
|
|
@@ -113,8 +130,8 @@ class AgentState(OrmMetadataBase, validate_assignment=True):
|
|
|
113
130
|
description="If set to True, memory management will move to a background agent thread.",
|
|
114
131
|
)
|
|
115
132
|
|
|
116
|
-
multi_agent_group: Optional[Group] = Field(None, description="The multi-agent group that this agent manages")
|
|
117
|
-
|
|
133
|
+
multi_agent_group: Optional[Group] = Field(None, description="The multi-agent group that this agent manages", deprecated=True)
|
|
134
|
+
managed_group: Optional[Group] = Field(None, description="The multi-agent group that this agent manages")
|
|
118
135
|
# Run metrics
|
|
119
136
|
last_run_completion: Optional[datetime] = Field(None, description="The timestamp when the agent last completed a run.")
|
|
120
137
|
last_run_duration_ms: Optional[int] = Field(None, description="The duration in milliseconds of the agent's last run.")
|
|
@@ -256,6 +273,7 @@ class CreateAgent(BaseModel, validate_assignment=True): #
|
|
|
256
273
|
None,
|
|
257
274
|
description="If set to True, the agent will be hidden.",
|
|
258
275
|
)
|
|
276
|
+
parallel_tool_calls: Optional[bool] = Field(False, description="If set to True, enables parallel tool calling. Defaults to False.")
|
|
259
277
|
|
|
260
278
|
@field_validator("name")
|
|
261
279
|
@classmethod
|
|
@@ -268,9 +286,16 @@ class CreateAgent(BaseModel, validate_assignment=True): #
|
|
|
268
286
|
# don't check if not provided
|
|
269
287
|
return name
|
|
270
288
|
|
|
271
|
-
# Regex for allowed characters (
|
|
272
|
-
|
|
273
|
-
|
|
289
|
+
# Regex for allowed characters (Unicode letters, digits, spaces, hyphens, underscores, apostrophes)
|
|
290
|
+
# \w in Python 3 with re.UNICODE matches Unicode letters, digits, and underscores
|
|
291
|
+
# We explicitly allow: letters (any language), digits, spaces, hyphens, underscores, apostrophes
|
|
292
|
+
# We block filesystem-unsafe characters: / \ : * ? " < > |
|
|
293
|
+
if not re.match(r"^[\w '\-]+$", name, re.UNICODE):
|
|
294
|
+
raise AgentExportProcessingError(
|
|
295
|
+
f"Agent name '{name}' contains invalid characters. Only letters (any language), digits, spaces, "
|
|
296
|
+
f"hyphens, underscores, and apostrophes are allowed. Please avoid filesystem-unsafe characters "
|
|
297
|
+
f'like: / \\ : * ? " < > |'
|
|
298
|
+
)
|
|
274
299
|
|
|
275
300
|
# Further checks can be added here...
|
|
276
301
|
# TODO
|
|
@@ -353,6 +378,11 @@ class UpdateAgent(BaseModel):
|
|
|
353
378
|
embedding: Optional[str] = Field(
|
|
354
379
|
None, description="The embedding configuration handle used by the agent, specified in the format provider/model-name."
|
|
355
380
|
)
|
|
381
|
+
context_window_limit: Optional[int] = Field(None, description="The context window limit used by the agent.")
|
|
382
|
+
max_tokens: Optional[int] = Field(
|
|
383
|
+
None,
|
|
384
|
+
description="The maximum number of tokens to generate, including reasoning step. If not set, the model will use its default value.",
|
|
385
|
+
)
|
|
356
386
|
reasoning: Optional[bool] = Field(None, description="Whether to enable reasoning for this agent.")
|
|
357
387
|
enable_sleeptime: Optional[bool] = Field(None, description="If set to True, memory management will move to a background agent thread.")
|
|
358
388
|
response_format: Optional[ResponseFormatUnion] = Field(None, description="The response format for the agent.")
|
|
@@ -371,6 +401,7 @@ class UpdateAgent(BaseModel):
|
|
|
371
401
|
None,
|
|
372
402
|
description="If set to True, the agent will be hidden.",
|
|
373
403
|
)
|
|
404
|
+
parallel_tool_calls: Optional[bool] = Field(False, description="If set to True, enables parallel tool calling. Defaults to False.")
|
|
374
405
|
|
|
375
406
|
model_config = ConfigDict(extra="ignore") # Ignores extra fields
|
|
376
407
|
|
letta/schemas/agent_file.py
CHANGED
|
@@ -10,6 +10,7 @@ from letta.schemas.block import Block, CreateBlock
|
|
|
10
10
|
from letta.schemas.enums import MessageRole
|
|
11
11
|
from letta.schemas.file import FileAgent, FileAgentBase, FileMetadata, FileMetadataBase
|
|
12
12
|
from letta.schemas.group import Group, GroupCreate
|
|
13
|
+
from letta.schemas.letta_message import ApprovalReturn
|
|
13
14
|
from letta.schemas.mcp import MCPServer
|
|
14
15
|
from letta.schemas.message import Message, MessageCreate, ToolReturn
|
|
15
16
|
from letta.schemas.source import Source, SourceCreate
|
|
@@ -59,6 +60,7 @@ class MessageSchema(MessageCreate):
|
|
|
59
60
|
approve: Optional[bool] = Field(None, description="Whether the tool has been approved")
|
|
60
61
|
approval_request_id: Optional[str] = Field(None, description="The message ID of the approval request")
|
|
61
62
|
denial_reason: Optional[str] = Field(None, description="An optional explanation for the provided approval status")
|
|
63
|
+
approvals: Optional[List[ApprovalReturn | ToolReturn]] = Field(None, description="Approval returns for the message")
|
|
62
64
|
|
|
63
65
|
# TODO: Should we also duplicate the steps here?
|
|
64
66
|
# TODO: What about tool_return?
|
|
@@ -87,6 +89,7 @@ class MessageSchema(MessageCreate):
|
|
|
87
89
|
approve=message.approve,
|
|
88
90
|
approval_request_id=message.approval_request_id,
|
|
89
91
|
denial_reason=message.denial_reason,
|
|
92
|
+
approvals=message.approvals,
|
|
90
93
|
)
|
|
91
94
|
|
|
92
95
|
|
letta/schemas/archive.py
CHANGED
|
@@ -3,12 +3,13 @@ from typing import Dict, Optional
|
|
|
3
3
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
|
-
from letta.schemas.
|
|
6
|
+
from letta.schemas.embedding_config import EmbeddingConfig
|
|
7
|
+
from letta.schemas.enums import PrimitiveType, VectorDBProvider
|
|
7
8
|
from letta.schemas.letta_base import OrmMetadataBase
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class ArchiveBase(OrmMetadataBase):
|
|
11
|
-
__id_prefix__ =
|
|
12
|
+
__id_prefix__ = PrimitiveType.ARCHIVE.value
|
|
12
13
|
|
|
13
14
|
name: str = Field(..., description="The name of the archive")
|
|
14
15
|
description: Optional[str] = Field(None, description="A description of the archive")
|
|
@@ -16,6 +17,7 @@ class ArchiveBase(OrmMetadataBase):
|
|
|
16
17
|
vector_db_provider: VectorDBProvider = Field(
|
|
17
18
|
default=VectorDBProvider.NATIVE, description="The vector database provider used for this archive's passages"
|
|
18
19
|
)
|
|
20
|
+
embedding_config: EmbeddingConfig = Field(..., description="Embedding configuration for passages in this archive")
|
|
19
21
|
metadata: Optional[Dict] = Field(default_factory=dict, validation_alias="metadata_", description="Additional metadata")
|
|
20
22
|
|
|
21
23
|
|
letta/schemas/block.py
CHANGED
|
@@ -4,6 +4,7 @@ from typing import Any, Optional
|
|
|
4
4
|
from pydantic import ConfigDict, Field, model_validator
|
|
5
5
|
|
|
6
6
|
from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT, DEFAULT_HUMAN_BLOCK_DESCRIPTION, DEFAULT_PERSONA_BLOCK_DESCRIPTION
|
|
7
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
8
|
from letta.schemas.letta_base import LettaBase
|
|
8
9
|
|
|
9
10
|
# block of the LLM context
|
|
@@ -12,7 +13,7 @@ from letta.schemas.letta_base import LettaBase
|
|
|
12
13
|
class BaseBlock(LettaBase, validate_assignment=True):
|
|
13
14
|
"""Base block of the LLM context"""
|
|
14
15
|
|
|
15
|
-
__id_prefix__ =
|
|
16
|
+
__id_prefix__ = PrimitiveType.BLOCK.value
|
|
16
17
|
|
|
17
18
|
# data value
|
|
18
19
|
value: str = Field(..., description="Value of the block.")
|
letta/schemas/enums.py
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
from enum import Enum, StrEnum
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
class PrimitiveType(str, Enum):
|
|
5
|
+
"""
|
|
6
|
+
Enum for all primitive resource types in Letta.
|
|
7
|
+
|
|
8
|
+
The enum values ARE the actual ID prefixes used in the system.
|
|
9
|
+
This serves as the single source of truth for all ID prefixes.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
AGENT = "agent"
|
|
13
|
+
MESSAGE = "message"
|
|
14
|
+
RUN = "run"
|
|
15
|
+
JOB = "job"
|
|
16
|
+
GROUP = "group"
|
|
17
|
+
BLOCK = "block"
|
|
18
|
+
FILE = "file"
|
|
19
|
+
FOLDER = "source" # Note: folder IDs use "source" prefix for historical reasons
|
|
20
|
+
SOURCE = "source"
|
|
21
|
+
TOOL = "tool"
|
|
22
|
+
ARCHIVE = "archive"
|
|
23
|
+
PASSAGE = "passage"
|
|
24
|
+
PROVIDER = "provider"
|
|
25
|
+
SANDBOX_CONFIG = "sandbox" # Note: sandbox_config IDs use "sandbox" prefix
|
|
26
|
+
STEP = "step"
|
|
27
|
+
IDENTITY = "identity"
|
|
28
|
+
|
|
29
|
+
|
|
4
30
|
class ProviderType(str, Enum):
|
|
5
31
|
anthropic = "anthropic"
|
|
6
32
|
azure = "azure"
|
|
@@ -152,13 +178,12 @@ class ToolType(str, Enum):
|
|
|
152
178
|
LETTA_VOICE_SLEEPTIME_CORE = "letta_voice_sleeptime_core"
|
|
153
179
|
LETTA_BUILTIN = "letta_builtin"
|
|
154
180
|
LETTA_FILES_CORE = "letta_files_core"
|
|
155
|
-
EXTERNAL_LANGCHAIN = "external_langchain"
|
|
156
|
-
EXTERNAL_COMPOSIO = "external_composio"
|
|
181
|
+
EXTERNAL_LANGCHAIN = "external_langchain" # DEPRECATED
|
|
182
|
+
EXTERNAL_COMPOSIO = "external_composio" # DEPRECATED
|
|
157
183
|
# TODO is "external" the right name here? Since as of now, MCP is local / doesn't support remote?
|
|
158
184
|
EXTERNAL_MCP = "external_mcp"
|
|
159
185
|
|
|
160
186
|
|
|
161
|
-
|
|
162
187
|
class JobType(str, Enum):
|
|
163
188
|
JOB = "job"
|
|
164
189
|
RUN = "run"
|
|
@@ -222,3 +247,11 @@ class TagMatchMode(str, Enum):
|
|
|
222
247
|
|
|
223
248
|
ANY = "any"
|
|
224
249
|
ALL = "all"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ComparisonOperator(str, Enum):
|
|
253
|
+
"""Comparison operators for filtering numeric values"""
|
|
254
|
+
|
|
255
|
+
EQ = "eq" # equals
|
|
256
|
+
GTE = "gte" # greater than or equal
|
|
257
|
+
LTE = "lte" # less than or equal
|
letta/schemas/file.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import List, Optional
|
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
|
|
7
|
-
from letta.schemas.enums import FileProcessingStatus
|
|
7
|
+
from letta.schemas.enums import FileProcessingStatus, PrimitiveType
|
|
8
8
|
from letta.schemas.letta_base import LettaBase
|
|
9
9
|
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ class FileStatus(str, Enum):
|
|
|
20
20
|
class FileMetadataBase(LettaBase):
|
|
21
21
|
"""Base class for FileMetadata schemas"""
|
|
22
22
|
|
|
23
|
-
__id_prefix__ =
|
|
23
|
+
__id_prefix__ = PrimitiveType.FILE.value
|
|
24
24
|
|
|
25
25
|
# Core file metadata fields
|
|
26
26
|
source_id: str = Field(..., description="The unique identifier of the source associated with the document.")
|
|
@@ -61,7 +61,7 @@ class FileMetadata(FileMetadataBase):
|
|
|
61
61
|
class FileAgentBase(LettaBase):
|
|
62
62
|
"""Base class for the FileMetadata-⇄-Agent association schemas"""
|
|
63
63
|
|
|
64
|
-
__id_prefix__ =
|
|
64
|
+
__id_prefix__ = PrimitiveType.FILE.value
|
|
65
65
|
|
|
66
66
|
# Core file-agent association fields
|
|
67
67
|
agent_id: str = Field(..., description="Unique identifier of the agent.")
|
letta/schemas/folder.py
CHANGED
|
@@ -4,6 +4,7 @@ from typing import Optional
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
6
|
from letta.schemas.embedding_config import EmbeddingConfig
|
|
7
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
8
|
from letta.schemas.letta_base import LettaBase
|
|
8
9
|
|
|
9
10
|
|
|
@@ -12,7 +13,7 @@ class BaseFolder(LettaBase):
|
|
|
12
13
|
Shared attributes across all folder schemas.
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
|
-
__id_prefix__ =
|
|
16
|
+
__id_prefix__ = PrimitiveType.FOLDER.value # TODO: change to "folder"
|
|
16
17
|
|
|
17
18
|
# Core folder fields
|
|
18
19
|
name: str = Field(..., description="The name of the folder.")
|
letta/schemas/group.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Annotated, List, Literal, Optional, Union
|
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
|
+
from letta.schemas.enums import PrimitiveType
|
|
6
7
|
from letta.schemas.letta_base import LettaBase
|
|
7
8
|
|
|
8
9
|
|
|
@@ -20,7 +21,7 @@ class ManagerConfig(BaseModel):
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class GroupBase(LettaBase):
|
|
23
|
-
__id_prefix__ =
|
|
24
|
+
__id_prefix__ = PrimitiveType.GROUP.value
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class Group(GroupBase):
|
letta/schemas/identity.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import List, Optional, Union
|
|
|
3
3
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
|
|
6
|
+
from letta.schemas.enums import PrimitiveType
|
|
6
7
|
from letta.schemas.letta_base import LettaBase
|
|
7
8
|
|
|
8
9
|
|
|
@@ -28,7 +29,7 @@ class IdentityPropertyType(str, Enum):
|
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class IdentityBase(LettaBase):
|
|
31
|
-
__id_prefix__ =
|
|
32
|
+
__id_prefix__ = PrimitiveType.IDENTITY.value
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
class IdentityProperty(LettaBase):
|
|
@@ -45,8 +46,8 @@ class Identity(IdentityBase):
|
|
|
45
46
|
name: str = Field(..., description="The name of the identity.")
|
|
46
47
|
identity_type: IdentityType = Field(..., description="The type of the identity.")
|
|
47
48
|
project_id: Optional[str] = Field(None, description="The project id of the identity, if applicable.")
|
|
48
|
-
agent_ids: List[str] = Field(..., description="The IDs of the agents associated with the identity.")
|
|
49
|
-
block_ids: List[str] = Field(..., description="The IDs of the blocks associated with the identity.")
|
|
49
|
+
agent_ids: List[str] = Field(..., description="The IDs of the agents associated with the identity.", deprecated=True)
|
|
50
|
+
block_ids: List[str] = Field(..., description="The IDs of the blocks associated with the identity.", deprecated=True)
|
|
50
51
|
organization_id: Optional[str] = Field(None, description="The organization id of the user")
|
|
51
52
|
properties: List[IdentityProperty] = Field(default_factory=list, description="List of properties associated with the identity")
|
|
52
53
|
|
|
@@ -56,8 +57,8 @@ class IdentityCreate(LettaBase):
|
|
|
56
57
|
name: str = Field(..., description="The name of the identity.")
|
|
57
58
|
identity_type: IdentityType = Field(..., description="The type of the identity.")
|
|
58
59
|
project_id: Optional[str] = Field(None, description="The project id of the identity, if applicable.")
|
|
59
|
-
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.")
|
|
60
|
-
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.")
|
|
60
|
+
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.", deprecated=True)
|
|
61
|
+
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.", deprecated=True)
|
|
61
62
|
properties: Optional[List[IdentityProperty]] = Field(None, description="List of properties associated with the identity.")
|
|
62
63
|
|
|
63
64
|
|
|
@@ -66,8 +67,8 @@ class IdentityUpsert(LettaBase):
|
|
|
66
67
|
name: str = Field(..., description="The name of the identity.")
|
|
67
68
|
identity_type: IdentityType = Field(..., description="The type of the identity.")
|
|
68
69
|
project_id: Optional[str] = Field(None, description="The project id of the identity, if applicable.")
|
|
69
|
-
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.")
|
|
70
|
-
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.")
|
|
70
|
+
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.", deprecated=True)
|
|
71
|
+
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.", deprecated=True)
|
|
71
72
|
properties: Optional[List[IdentityProperty]] = Field(None, description="List of properties associated with the identity.")
|
|
72
73
|
|
|
73
74
|
|
|
@@ -75,6 +76,14 @@ class IdentityUpdate(LettaBase):
|
|
|
75
76
|
identifier_key: Optional[str] = Field(None, description="External, user-generated identifier key of the identity.")
|
|
76
77
|
name: Optional[str] = Field(None, description="The name of the identity.")
|
|
77
78
|
identity_type: Optional[IdentityType] = Field(None, description="The type of the identity.")
|
|
78
|
-
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.")
|
|
79
|
-
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.")
|
|
79
|
+
agent_ids: Optional[List[str]] = Field(None, description="The agent ids that are associated with the identity.", deprecated=True)
|
|
80
|
+
block_ids: Optional[List[str]] = Field(None, description="The IDs of the blocks associated with the identity.", deprecated=True)
|
|
80
81
|
properties: Optional[List[IdentityProperty]] = Field(None, description="List of properties associated with the identity.")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class PaginatedIdentities(LettaBase):
|
|
85
|
+
"""Paginated response for identities"""
|
|
86
|
+
|
|
87
|
+
data: List[Identity] = Field(..., description="List of identities")
|
|
88
|
+
next_cursor: Optional[str] = Field(None, description="Cursor for fetching the next page")
|
|
89
|
+
has_more: bool = Field(..., description="Whether more results exist after this page")
|
letta/schemas/job.py
CHANGED
|
@@ -3,6 +3,8 @@ from typing import TYPE_CHECKING, List, Optional
|
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel, ConfigDict, Field
|
|
5
5
|
|
|
6
|
+
from letta.schemas.enums import PrimitiveType
|
|
7
|
+
|
|
6
8
|
if TYPE_CHECKING:
|
|
7
9
|
from letta.schemas.letta_request import LettaRequest
|
|
8
10
|
|
|
@@ -15,7 +17,7 @@ from letta.schemas.letta_stop_reason import StopReasonType
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class JobBase(OrmMetadataBase):
|
|
18
|
-
__id_prefix__ =
|
|
20
|
+
__id_prefix__ = PrimitiveType.JOB.value
|
|
19
21
|
status: JobStatus = Field(default=JobStatus.created, description="The status of the job.")
|
|
20
22
|
created_at: datetime = Field(default_factory=get_utc_time, description="The unix timestamp of when the job was created.")
|
|
21
23
|
|