letta-nightly 0.6.54.dev20250420104127__py3-none-any.whl → 0.7.0.dev20250421104249__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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.54"
1
+ __version__ = "0.7.0"
2
2
 
3
3
  # import clients
4
4
  from letta.client.client import LocalClient, RESTClient, create_client
letta/constants.py CHANGED
@@ -60,9 +60,9 @@ BASE_SLEEPTIME_TOOLS = [
60
60
  "memory_insert",
61
61
  "memory_rethink",
62
62
  "memory_finish_edits",
63
- "archival_memory_insert",
64
- "archival_memory_search",
65
- "conversation_search",
63
+ # "archival_memory_insert",
64
+ # "archival_memory_search",
65
+ # "conversation_search",
66
66
  ]
67
67
  # Multi agent tools
68
68
  MULTI_AGENT_TOOLS = ["send_message_to_agent_and_wait_for_reply", "send_message_to_agents_matching_tags", "send_message_to_agent_async"]
@@ -159,7 +159,13 @@ class DirectoryConnector(DataConnector):
159
159
  from llama_index.core.node_parser import TokenTextSplitter
160
160
 
161
161
  parser = TokenTextSplitter(chunk_size=chunk_size)
162
- documents = SimpleDirectoryReader(input_files=[file.file_path]).load_data()
162
+ if file.file_type == "application/pdf":
163
+ from llama_index.readers.file import PDFReader
164
+
165
+ reader = PDFReader()
166
+ documents = reader.load_data(file=file.file_path)
167
+ else:
168
+ documents = SimpleDirectoryReader(input_files=[file.file_path]).load_data()
163
169
  nodes = parser.get_nodes_from_documents(documents)
164
170
  for node in nodes:
165
171
  yield node.text, None
letta/errors.py CHANGED
@@ -204,3 +204,13 @@ class InvalidInnerMonologueError(LettaMessageError):
204
204
  """Error raised when a message has a malformed inner monologue."""
205
205
 
206
206
  default_error_message = "The message has a malformed inner monologue."
207
+
208
+
209
+ class HandleNotFoundError(LettaError):
210
+ """Error raised when a handle is not found."""
211
+
212
+ def __init__(self, handle: str, available_handles: List[str]):
213
+ super().__init__(
214
+ message=f"Handle {handle} not found, must be one of {available_handles}",
215
+ code=ErrorCode.NOT_FOUND,
216
+ )
@@ -56,6 +56,9 @@ class GoogleAIClient(LLMClientBase):
56
56
  tool_names = [t.function.name for t in tool_objs]
57
57
  # Convert to the exact payload style Google expects
58
58
  tools = self.convert_tools_to_google_ai_format(tool_objs)
59
+ else:
60
+ tool_names = []
61
+
59
62
  contents = self.add_dummy_model_messages(
60
63
  [m.to_google_ai_dict() for m in messages],
61
64
  )
letta/orm/message.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from typing import List, Optional
2
2
 
3
3
  from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall
4
- from sqlalchemy import BigInteger, FetchedValue, ForeignKey, Index, Sequence, event, text
4
+ from sqlalchemy import BigInteger, FetchedValue, ForeignKey, Index, event, text
5
5
  from sqlalchemy.orm import Mapped, Session, mapped_column, relationship
6
6
 
7
7
  from letta.orm.custom_columns import MessageContentColumn, ToolCallColumn, ToolReturnColumn
@@ -48,7 +48,6 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
48
48
  # Monotonically increasing sequence for efficient/correct listing
49
49
  sequence_id: Mapped[int] = mapped_column(
50
50
  BigInteger,
51
- Sequence("message_seq_id"),
52
51
  server_default=FetchedValue(),
53
52
  unique=True,
54
53
  nullable=False,
@@ -46,7 +46,7 @@ ChatMessage = Union[SystemMessage, UserMessage, AssistantMessage, ToolMessage]
46
46
  def cast_message_to_subtype(m_dict: dict) -> ChatMessage:
47
47
  """Cast a dictionary to one of the individual message types"""
48
48
  role = m_dict.get("role")
49
- if role == "system":
49
+ if role == "system" or role == "developer":
50
50
  return SystemMessage(**m_dict)
51
51
  elif role == "user":
52
52
  return UserMessage(**m_dict)
@@ -55,7 +55,7 @@ def cast_message_to_subtype(m_dict: dict) -> ChatMessage:
55
55
  elif role == "tool":
56
56
  return ToolMessage(**m_dict)
57
57
  else:
58
- raise ValueError("Unknown message role")
58
+ raise ValueError(f"Unknown message role: {role}")
59
59
 
60
60
 
61
61
  class ResponseFormat(BaseModel):
@@ -66,7 +66,6 @@ class ProviderCreate(ProviderBase):
66
66
 
67
67
 
68
68
  class ProviderUpdate(ProviderBase):
69
- id: str = Field(..., description="The id of the provider to update.")
70
69
  api_key: str = Field(..., description="API key used for requests to the provider.")
71
70
 
72
71
 
@@ -11,7 +11,7 @@ if TYPE_CHECKING:
11
11
  router = APIRouter(prefix="/providers", tags=["providers"])
12
12
 
13
13
 
14
- @router.get("/", tags=["providers"], response_model=List[Provider], operation_id="list_providers")
14
+ @router.get("/", response_model=List[Provider], operation_id="list_providers")
15
15
  def list_providers(
16
16
  after: Optional[str] = Query(None),
17
17
  limit: Optional[int] = Query(50),
@@ -31,7 +31,7 @@ def list_providers(
31
31
  return providers
32
32
 
33
33
 
34
- @router.post("/", tags=["providers"], response_model=Provider, operation_id="create_provider")
34
+ @router.post("/", response_model=Provider, operation_id="create_provider")
35
35
  def create_provider(
36
36
  request: ProviderCreate = Body(...),
37
37
  actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
@@ -47,8 +47,9 @@ def create_provider(
47
47
  return provider
48
48
 
49
49
 
50
- @router.patch("/", tags=["providers"], response_model=Provider, operation_id="modify_provider")
50
+ @router.patch("/{provider_id}", response_model=Provider, operation_id="modify_provider")
51
51
  def modify_provider(
52
+ provider_id: str,
52
53
  request: ProviderUpdate = Body(...),
53
54
  actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
54
55
  server: "SyncServer" = Depends(get_letta_server),
@@ -57,13 +58,12 @@ def modify_provider(
57
58
  Update an existing custom provider
58
59
  """
59
60
  actor = server.user_manager.get_user_or_default(user_id=actor_id)
60
- provider = server.provider_manager.update_provider(request, actor=actor)
61
- return provider
61
+ return server.provider_manager.update_provider(provider_id=provider_id, request=request, actor=actor)
62
62
 
63
63
 
64
- @router.delete("/", tags=["providers"], response_model=None, operation_id="delete_provider")
64
+ @router.delete("/{provider_id}", response_model=None, operation_id="delete_provider")
65
65
  def delete_provider(
66
- provider_id: str = Query(..., description="The provider_id key to be deleted."),
66
+ provider_id: str,
67
67
  actor_id: Optional[str] = Header(None, alias="user_id"),
68
68
  server: "SyncServer" = Depends(get_letta_server),
69
69
  ):
@@ -73,6 +73,9 @@ def delete_provider(
73
73
  try:
74
74
  actor = server.user_manager.get_user_or_default(user_id=actor_id)
75
75
  server.provider_manager.delete_provider_by_id(provider_id=provider_id, actor=actor)
76
+ return JSONResponse(status_code=status.HTTP_200_OK, content={"message": f"Provider id={provider_id} successfully deleted"})
77
+ except NoResultFound:
78
+ raise HTTPException(status_code=404, detail=f"Provider provider_id={provider_id} not found for user_id={actor.id}.")
76
79
  except HTTPException:
77
80
  raise
78
81
  except Exception as e:
letta/server/server.py CHANGED
@@ -20,6 +20,7 @@ import letta.system as system
20
20
  from letta.agent import Agent, save_agent
21
21
  from letta.config import LettaConfig
22
22
  from letta.data_sources.connectors import DataConnector, load_data
23
+ from letta.errors import HandleNotFoundError
23
24
  from letta.functions.mcp_client.base_client import BaseMCPClient
24
25
  from letta.functions.mcp_client.sse_client import MCP_CONFIG_TOPLEVEL_KEY, SSEMCPClient
25
26
  from letta.functions.mcp_client.stdio_client import StdioMCPClient
@@ -702,6 +703,8 @@ class SyncServer(Server):
702
703
  @trace_method
703
704
  def get_cached_llm_config(self, **kwargs):
704
705
  key = make_key(**kwargs)
706
+ print(self._llm_config_cache)
707
+ print("KEY", key)
705
708
  if key not in self._llm_config_cache:
706
709
  self._llm_config_cache[key] = self.get_llm_config_from_handle(**kwargs)
707
710
  return self._llm_config_cache[key]
@@ -1179,10 +1182,12 @@ class SyncServer(Server):
1179
1182
  provider = self.get_provider_from_name(provider_name)
1180
1183
 
1181
1184
  llm_configs = [config for config in provider.list_llm_models() if config.handle == handle]
1185
+ print("LLM CONFIGS", llm_configs)
1182
1186
  if not llm_configs:
1183
1187
  llm_configs = [config for config in provider.list_llm_models() if config.model == model_name]
1184
1188
  if not llm_configs:
1185
- raise ValueError(f"LLM model {model_name} is not supported by {provider_name}")
1189
+ available_handles = [config.handle for config in provider.list_llm_models()]
1190
+ raise HandleNotFoundError(handle, available_handles)
1186
1191
  except ValueError as e:
1187
1192
  llm_configs = [config for config in self.get_local_llm_configs() if config.handle == handle]
1188
1193
  if not llm_configs:
@@ -1190,6 +1195,8 @@ class SyncServer(Server):
1190
1195
  if not llm_configs:
1191
1196
  raise e
1192
1197
 
1198
+ print("CONFIGS", llm_configs)
1199
+
1193
1200
  if len(llm_configs) == 1:
1194
1201
  llm_config = llm_configs[0]
1195
1202
  elif len(llm_configs) > 1:
@@ -1,8 +1,10 @@
1
- from datetime import datetime
2
- from typing import Dict, List, Optional
1
+ from datetime import datetime, timezone
2
+ from typing import Dict, List, Optional, Set, Tuple
3
3
 
4
4
  import numpy as np
5
- from sqlalchemy import Select, and_, func, literal, or_, select, union_all
5
+ import sqlalchemy as sa
6
+ from sqlalchemy import Select, and_, delete, func, insert, literal, or_, select, union_all
7
+ from sqlalchemy.dialects.postgresql import insert as pg_insert
6
8
 
7
9
  from letta.constants import (
8
10
  BASE_MEMORY_TOOLS,
@@ -19,13 +21,16 @@ from letta.log import get_logger
19
21
  from letta.orm import Agent as AgentModel
20
22
  from letta.orm import AgentPassage, AgentsTags
21
23
  from letta.orm import Block as BlockModel
24
+ from letta.orm import BlocksAgents
22
25
  from letta.orm import Group as GroupModel
23
- from letta.orm import Identity as IdentityModel
26
+ from letta.orm import IdentitiesAgents
24
27
  from letta.orm import Source as SourceModel
25
28
  from letta.orm import SourcePassage, SourcesAgents
26
29
  from letta.orm import Tool as ToolModel
30
+ from letta.orm import ToolsAgents
27
31
  from letta.orm.enums import ToolType
28
32
  from letta.orm.errors import NoResultFound
33
+ from letta.orm.sandbox_config import AgentEnvironmentVariable
29
34
  from letta.orm.sandbox_config import AgentEnvironmentVariable as AgentEnvironmentVariableModel
30
35
  from letta.orm.sqlalchemy_base import AccessType
31
36
  from letta.orm.sqlite_functions import adapt_array
@@ -36,16 +41,14 @@ from letta.schemas.block import BlockUpdate
36
41
  from letta.schemas.embedding_config import EmbeddingConfig
37
42
  from letta.schemas.group import Group as PydanticGroup
38
43
  from letta.schemas.group import ManagerType
39
- from letta.schemas.llm_config import LLMConfig
40
44
  from letta.schemas.memory import Memory
45
+ from letta.schemas.message import Message
41
46
  from letta.schemas.message import Message as PydanticMessage
42
47
  from letta.schemas.message import MessageCreate, MessageUpdate
43
48
  from letta.schemas.passage import Passage as PydanticPassage
44
49
  from letta.schemas.source import Source as PydanticSource
45
50
  from letta.schemas.tool import Tool as PydanticTool
46
- from letta.schemas.tool_rule import ContinueToolRule as PydanticContinueToolRule
47
- from letta.schemas.tool_rule import TerminalToolRule as PydanticTerminalToolRule
48
- from letta.schemas.tool_rule import ToolRule as PydanticToolRule
51
+ from letta.schemas.tool_rule import ContinueToolRule, TerminalToolRule
49
52
  from letta.schemas.user import User as PydanticUser
50
53
  from letta.serialize_schemas import MarshmallowAgentSchema
51
54
  from letta.serialize_schemas.marshmallow_message import SerializedMessageSchema
@@ -58,7 +61,6 @@ from letta.services.helpers.agent_manager_helper import (
58
61
  _apply_pagination,
59
62
  _apply_tag_filter,
60
63
  _process_relationship,
61
- _process_tags,
62
64
  check_supports_structured_output,
63
65
  compile_system_message,
64
66
  derive_system_message,
@@ -77,7 +79,6 @@ from letta.utils import enforce_types, united_diff
77
79
  logger = get_logger(__name__)
78
80
 
79
81
 
80
- # Agent Manager Class
81
82
  class AgentManager:
82
83
  """Manager class to handle business logic related to Agents."""
83
84
 
@@ -92,124 +93,218 @@ class AgentManager:
92
93
  self.passage_manager = PassageManager()
93
94
  self.identity_manager = IdentityManager()
94
95
 
96
+ @staticmethod
97
+ def _resolve_tools(session, names: Set[str], ids: Set[str], org_id: str) -> Tuple[Dict[str, str], Dict[str, str]]:
98
+ """
99
+ Bulk‑fetch all ToolModel rows matching either name ∈ names or id ∈ ids
100
+ (and scoped to this organization), and return two maps:
101
+ name_to_id, id_to_name.
102
+ Raises if any requested name or id was not found.
103
+ """
104
+ stmt = select(ToolModel.id, ToolModel.name).where(
105
+ ToolModel.organization_id == org_id,
106
+ or_(
107
+ ToolModel.name.in_(names),
108
+ ToolModel.id.in_(ids),
109
+ ),
110
+ )
111
+ rows = session.execute(stmt).all()
112
+ name_to_id = {name: tid for tid, name in rows}
113
+ id_to_name = {tid: name for tid, name in rows}
114
+
115
+ missing_names = names - set(name_to_id.keys())
116
+ missing_ids = ids - set(id_to_name.keys())
117
+ if missing_names:
118
+ raise ValueError(f"Tools not found by name: {missing_names}")
119
+ if missing_ids:
120
+ raise ValueError(f"Tools not found by id: {missing_ids}")
121
+
122
+ return name_to_id, id_to_name
123
+
124
+ @staticmethod
125
+ @trace_method
126
+ def _bulk_insert_pivot(session, table, rows: list[dict]):
127
+ if not rows:
128
+ return
129
+
130
+ dialect = session.bind.dialect.name
131
+ if dialect == "postgresql":
132
+ stmt = pg_insert(table).values(rows).on_conflict_do_nothing()
133
+ elif dialect == "sqlite":
134
+ stmt = sa.insert(table).values(rows).prefix_with("OR IGNORE")
135
+ else:
136
+ # fallback: filter out exact-duplicate dicts in Python
137
+ seen = set()
138
+ filtered = []
139
+ for row in rows:
140
+ key = tuple(sorted(row.items()))
141
+ if key not in seen:
142
+ seen.add(key)
143
+ filtered.append(row)
144
+ stmt = sa.insert(table).values(filtered)
145
+
146
+ session.execute(stmt)
147
+
148
+ @staticmethod
149
+ @trace_method
150
+ def _replace_pivot_rows(session, table, agent_id: str, rows: list[dict]):
151
+ """
152
+ Replace all pivot rows for an agent with *exactly* the provided list.
153
+ Uses two bulk statements (DELETE + INSERT ... ON CONFLICT DO NOTHING).
154
+ """
155
+ # delete all existing rows for this agent
156
+ session.execute(delete(table).where(table.c.agent_id == agent_id))
157
+ if rows:
158
+ AgentManager._bulk_insert_pivot(session, table, rows)
159
+
95
160
  # ======================================================================================================================
96
161
  # Basic CRUD operations
97
162
  # ======================================================================================================================
98
163
  @trace_method
99
- @enforce_types
100
- def create_agent(
101
- self,
102
- agent_create: CreateAgent,
103
- actor: PydanticUser,
104
- ) -> PydanticAgentState:
105
- system = derive_system_message(
106
- agent_type=agent_create.agent_type,
107
- enable_sleeptime=agent_create.enable_sleeptime,
108
- system=agent_create.system,
109
- )
110
-
164
+ def create_agent(self, agent_create: CreateAgent, actor: PydanticUser) -> PydanticAgentState:
165
+ # validate required configs
111
166
  if not agent_create.llm_config or not agent_create.embedding_config:
112
167
  raise ValueError("llm_config and embedding_config are required")
113
168
 
114
- # create blocks (note: cannot be linked into the agent_id is created)
115
- block_ids = list(agent_create.block_ids or []) # Create a local copy to avoid modifying the original
169
+ # blocks
170
+ block_ids = list(agent_create.block_ids or [])
116
171
  if agent_create.memory_blocks:
117
- for create_block in agent_create.memory_blocks:
118
- block = self.block_manager.create_or_update_block(PydanticBlock(**create_block.model_dump(to_orm=True)), actor=actor)
119
- block_ids.append(block.id)
120
-
121
- # add passed in `tools`
122
- tool_names = agent_create.tools or []
172
+ pydantic_blocks = [PydanticBlock(**b.model_dump(to_orm=True)) for b in agent_create.memory_blocks]
173
+ created_blocks = self.block_manager.batch_create_blocks(
174
+ pydantic_blocks,
175
+ actor=actor,
176
+ )
177
+ block_ids.extend([blk.id for blk in created_blocks])
123
178
 
124
- # add base tools
179
+ # tools
180
+ tool_names = set(agent_create.tools or [])
125
181
  if agent_create.include_base_tools:
126
182
  if agent_create.agent_type == AgentType.sleeptime_agent:
127
- tool_names.extend(BASE_SLEEPTIME_TOOLS)
183
+ tool_names |= set(BASE_SLEEPTIME_TOOLS)
184
+ elif agent_create.enable_sleeptime:
185
+ tool_names |= set(BASE_SLEEPTIME_CHAT_TOOLS)
128
186
  else:
129
- if agent_create.enable_sleeptime:
130
- tool_names.extend(BASE_SLEEPTIME_CHAT_TOOLS)
131
- else:
132
- tool_names.extend(BASE_TOOLS + BASE_MEMORY_TOOLS)
187
+ tool_names |= set(BASE_TOOLS + BASE_MEMORY_TOOLS)
133
188
  if agent_create.include_multi_agent_tools:
134
- tool_names.extend(MULTI_AGENT_TOOLS)
135
-
136
- # remove duplicates
137
- tool_names = list(set(tool_names))
138
-
139
- # convert tool names to ids
140
- tool_ids = []
141
- for tool_name in tool_names:
142
- tool = self.tool_manager.get_tool_by_name(tool_name=tool_name, actor=actor)
143
- if not tool:
144
- raise ValueError(f"Tool {tool_name} not found")
145
- tool_ids.append(tool.id)
146
-
147
- # add passed in `tool_ids`
148
- for tool_id in agent_create.tool_ids or []:
149
- if tool_id not in tool_ids:
150
- tool = self.tool_manager.get_tool_by_id(tool_id=tool_id, actor=actor)
151
- if tool:
152
- tool_ids.append(tool.id)
153
- tool_names.append(tool.name)
154
- else:
155
- raise ValueError(f"Tool {tool_id} not found")
156
-
157
- # add default tool rules
158
- tool_rules = agent_create.tool_rules or []
159
- if agent_create.include_base_tool_rules:
160
- # apply default tool rules
161
- for tool_name in tool_names:
162
- if tool_name == "send_message" or tool_name == "send_message_to_agent_async" or tool_name == "memory_finish_edits":
163
- tool_rules.append(PydanticTerminalToolRule(tool_name=tool_name))
164
- elif tool_name in BASE_TOOLS + BASE_MEMORY_TOOLS + BASE_SLEEPTIME_TOOLS:
165
- tool_rules.append(PydanticContinueToolRule(tool_name=tool_name))
166
-
167
- # if custom rules, check tool rules are valid
168
- if agent_create.tool_rules:
169
- check_supports_structured_output(model=agent_create.llm_config.model, tool_rules=agent_create.tool_rules)
170
-
171
- # Create the agent
172
- agent_state = self._create_agent(
173
- name=agent_create.name,
174
- system=system,
175
- agent_type=agent_create.agent_type,
176
- llm_config=agent_create.llm_config,
177
- embedding_config=agent_create.embedding_config,
178
- block_ids=block_ids,
179
- tool_ids=tool_ids,
180
- source_ids=agent_create.source_ids or [],
181
- tags=agent_create.tags or [],
182
- identity_ids=agent_create.identity_ids or [],
183
- description=agent_create.description,
184
- metadata=agent_create.metadata,
185
- tool_rules=tool_rules,
186
- actor=actor,
187
- project_id=agent_create.project_id,
188
- template_id=agent_create.template_id,
189
- base_template_id=agent_create.base_template_id,
190
- message_buffer_autoclear=agent_create.message_buffer_autoclear,
191
- enable_sleeptime=agent_create.enable_sleeptime,
192
- )
189
+ tool_names |= set(MULTI_AGENT_TOOLS)
193
190
 
194
- # If there are provided environment variables, add them in
195
- if agent_create.tool_exec_environment_variables:
196
- agent_state = self._set_environment_variables(
197
- agent_id=agent_state.id,
198
- env_vars=agent_create.tool_exec_environment_variables,
199
- actor=actor,
200
- )
191
+ supplied_ids = set(agent_create.tool_ids or [])
192
+
193
+ source_ids = agent_create.source_ids or []
194
+ identity_ids = agent_create.identity_ids or []
195
+ tag_values = agent_create.tags or []
201
196
 
202
- return self.append_initial_message_sequence_to_in_context_messages(actor, agent_state, agent_create.initial_message_sequence)
197
+ with self.session_maker() as session:
198
+ with session.begin():
199
+ name_to_id, id_to_name = self._resolve_tools(
200
+ session,
201
+ tool_names,
202
+ supplied_ids,
203
+ actor.organization_id,
204
+ )
205
+
206
+ tool_ids = set(name_to_id.values()) | set(id_to_name.keys())
207
+ tool_names = set(name_to_id.keys()) # now canonical
208
+
209
+ tool_rules = list(agent_create.tool_rules or [])
210
+ if agent_create.include_base_tool_rules:
211
+ for tn in tool_names:
212
+ if tn in {"send_message", "send_message_to_agent_async", "memory_finish_edits"}:
213
+ tool_rules.append(TerminalToolRule(tool_name=tn))
214
+ elif tn in (BASE_TOOLS + BASE_MEMORY_TOOLS + BASE_SLEEPTIME_TOOLS):
215
+ tool_rules.append(ContinueToolRule(tool_name=tn))
216
+
217
+ if tool_rules:
218
+ check_supports_structured_output(model=agent_create.llm_config.model, tool_rules=tool_rules)
219
+
220
+ new_agent = AgentModel(
221
+ name=agent_create.name,
222
+ system=derive_system_message(
223
+ agent_type=agent_create.agent_type,
224
+ enable_sleeptime=agent_create.enable_sleeptime,
225
+ system=agent_create.system,
226
+ ),
227
+ agent_type=agent_create.agent_type,
228
+ llm_config=agent_create.llm_config,
229
+ embedding_config=agent_create.embedding_config,
230
+ organization_id=actor.organization_id,
231
+ description=agent_create.description,
232
+ metadata_=agent_create.metadata,
233
+ tool_rules=tool_rules,
234
+ project_id=agent_create.project_id,
235
+ template_id=agent_create.template_id,
236
+ base_template_id=agent_create.base_template_id,
237
+ message_buffer_autoclear=agent_create.message_buffer_autoclear,
238
+ enable_sleeptime=agent_create.enable_sleeptime,
239
+ created_by_id=actor.id,
240
+ last_updated_by_id=actor.id,
241
+ )
242
+ session.add(new_agent)
243
+ session.flush()
244
+ aid = new_agent.id
245
+
246
+ self._bulk_insert_pivot(
247
+ session,
248
+ ToolsAgents.__table__,
249
+ [{"agent_id": aid, "tool_id": tid} for tid in tool_ids],
250
+ )
251
+
252
+ if block_ids:
253
+ rows = [
254
+ {"agent_id": aid, "block_id": bid, "block_label": lbl}
255
+ for bid, lbl in session.execute(select(BlockModel.id, BlockModel.label).where(BlockModel.id.in_(block_ids))).all()
256
+ ]
257
+ self._bulk_insert_pivot(session, BlocksAgents.__table__, rows)
258
+
259
+ self._bulk_insert_pivot(
260
+ session,
261
+ SourcesAgents.__table__,
262
+ [{"agent_id": aid, "source_id": sid} for sid in source_ids],
263
+ )
264
+ self._bulk_insert_pivot(
265
+ session,
266
+ AgentsTags.__table__,
267
+ [{"agent_id": aid, "tag": tag} for tag in tag_values],
268
+ )
269
+ self._bulk_insert_pivot(
270
+ session,
271
+ IdentitiesAgents.__table__,
272
+ [{"agent_id": aid, "identity_id": iid} for iid in identity_ids],
273
+ )
274
+
275
+ if agent_create.tool_exec_environment_variables:
276
+ env_rows = [
277
+ {
278
+ "agent_id": aid,
279
+ "key": key,
280
+ "value": val,
281
+ "organization_id": actor.organization_id,
282
+ }
283
+ for key, val in agent_create.tool_exec_environment_variables.items()
284
+ ]
285
+ session.execute(insert(AgentEnvironmentVariable).values(env_rows))
286
+
287
+ # initial message sequence
288
+ init_messages = self._generate_initial_message_sequence(
289
+ actor,
290
+ agent_state=new_agent.to_pydantic(include_relationships={"memory"}),
291
+ supplied_initial_message_sequence=agent_create.initial_message_sequence,
292
+ )
293
+ new_agent.message_ids = [msg.id for msg in init_messages]
294
+
295
+ session.refresh(new_agent)
296
+
297
+ self.message_manager.create_many_messages(pydantic_msgs=init_messages, actor=actor)
298
+ return new_agent.to_pydantic()
203
299
 
204
300
  @enforce_types
205
- def append_initial_message_sequence_to_in_context_messages(
206
- self, actor: PydanticUser, agent_state: PydanticAgentState, initial_message_sequence: Optional[List[MessageCreate]] = None
207
- ) -> PydanticAgentState:
301
+ def _generate_initial_message_sequence(
302
+ self, actor: PydanticUser, agent_state: PydanticAgentState, supplied_initial_message_sequence: Optional[List[MessageCreate]] = None
303
+ ) -> List[Message]:
208
304
  init_messages = initialize_message_sequence(
209
305
  agent_state=agent_state, memory_edit_timestamp=get_utc_time(), include_initial_boot_message=True
210
306
  )
211
-
212
- if initial_message_sequence is not None:
307
+ if supplied_initial_message_sequence is not None:
213
308
  # We always need the system prompt up front
214
309
  system_message_obj = PydanticMessage.dict_to_message(
215
310
  agent_id=agent_state.id,
@@ -219,7 +314,7 @@ class AgentManager:
219
314
  # Don't use anything else in the pregen sequence, instead use the provided sequence
220
315
  init_messages = [system_message_obj]
221
316
  init_messages.extend(
222
- package_initial_message_sequence(agent_state.id, initial_message_sequence, agent_state.llm_config.model, actor)
317
+ package_initial_message_sequence(agent_state.id, supplied_initial_message_sequence, agent_state.llm_config.model, actor)
223
318
  )
224
319
  else:
225
320
  init_messages = [
@@ -227,145 +322,131 @@ class AgentManager:
227
322
  for msg in init_messages
228
323
  ]
229
324
 
325
+ return init_messages
326
+
327
+ @enforce_types
328
+ def append_initial_message_sequence_to_in_context_messages(
329
+ self, actor: PydanticUser, agent_state: PydanticAgentState, initial_message_sequence: Optional[List[MessageCreate]] = None
330
+ ) -> PydanticAgentState:
331
+ init_messages = self._generate_initial_message_sequence(actor, agent_state, initial_message_sequence)
230
332
  return self.append_to_in_context_messages(init_messages, agent_id=agent_state.id, actor=actor)
231
333
 
232
334
  @enforce_types
233
- def _create_agent(
335
+ def update_agent(
234
336
  self,
337
+ agent_id: str,
338
+ agent_update: UpdateAgent,
235
339
  actor: PydanticUser,
236
- name: str,
237
- system: str,
238
- agent_type: AgentType,
239
- llm_config: LLMConfig,
240
- embedding_config: EmbeddingConfig,
241
- block_ids: List[str],
242
- tool_ids: List[str],
243
- source_ids: List[str],
244
- tags: List[str],
245
- identity_ids: List[str],
246
- description: Optional[str] = None,
247
- metadata: Optional[Dict] = None,
248
- tool_rules: Optional[List[PydanticToolRule]] = None,
249
- project_id: Optional[str] = None,
250
- template_id: Optional[str] = None,
251
- base_template_id: Optional[str] = None,
252
- message_buffer_autoclear: bool = False,
253
- enable_sleeptime: Optional[bool] = None,
254
340
  ) -> PydanticAgentState:
255
- """Create a new agent."""
256
- with self.session_maker() as session:
257
- # Prepare the agent data
258
- data = {
259
- "name": name,
260
- "system": system,
261
- "agent_type": agent_type,
262
- "llm_config": llm_config,
263
- "embedding_config": embedding_config,
264
- "organization_id": actor.organization_id,
265
- "description": description,
266
- "metadata_": metadata,
267
- "tool_rules": tool_rules,
268
- "project_id": project_id,
269
- "template_id": template_id,
270
- "base_template_id": base_template_id,
271
- "message_buffer_autoclear": message_buffer_autoclear,
272
- "enable_sleeptime": enable_sleeptime,
273
- }
274
-
275
- # Create the new agent using SqlalchemyBase.create
276
- new_agent = AgentModel(**data)
277
- _process_relationship(session, new_agent, "tools", ToolModel, tool_ids, replace=True)
278
- _process_relationship(session, new_agent, "sources", SourceModel, source_ids, replace=True)
279
- _process_relationship(session, new_agent, "core_memory", BlockModel, block_ids, replace=True)
280
- _process_tags(new_agent, tags, replace=True)
281
- _process_relationship(session, new_agent, "identities", IdentityModel, identity_ids, replace=True)
282
341
 
283
- new_agent.create(session, actor=actor)
284
-
285
- # Convert to PydanticAgentState and return
286
- return new_agent.to_pydantic()
342
+ new_tools = set(agent_update.tool_ids or [])
343
+ new_sources = set(agent_update.source_ids or [])
344
+ new_blocks = set(agent_update.block_ids or [])
345
+ new_idents = set(agent_update.identity_ids or [])
346
+ new_tags = set(agent_update.tags or [])
347
+
348
+ with self.session_maker() as session, session.begin():
349
+
350
+ agent: AgentModel = AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
351
+ agent.updated_at = datetime.now(timezone.utc)
352
+ agent.last_updated_by_id = actor.id
353
+
354
+ scalar_updates = {
355
+ "name": agent_update.name,
356
+ "system": agent_update.system,
357
+ "llm_config": agent_update.llm_config,
358
+ "embedding_config": agent_update.embedding_config,
359
+ "message_ids": agent_update.message_ids,
360
+ "tool_rules": agent_update.tool_rules,
361
+ "description": agent_update.description,
362
+ "project_id": agent_update.project_id,
363
+ "template_id": agent_update.template_id,
364
+ "base_template_id": agent_update.base_template_id,
365
+ "message_buffer_autoclear": agent_update.message_buffer_autoclear,
366
+ "enable_sleeptime": agent_update.enable_sleeptime,
367
+ }
368
+ for col, val in scalar_updates.items():
369
+ if val is not None:
370
+ setattr(agent, col, val)
287
371
 
288
- @enforce_types
289
- def update_agent(self, agent_id: str, agent_update: UpdateAgent, actor: PydanticUser) -> PydanticAgentState:
290
- agent_state = self._update_agent(agent_id=agent_id, agent_update=agent_update, actor=actor)
372
+ if agent_update.metadata is not None:
373
+ agent.metadata_ = agent_update.metadata
291
374
 
292
- # If there are provided environment variables, add them in
293
- if agent_update.tool_exec_environment_variables:
294
- agent_state = self._set_environment_variables(
295
- agent_id=agent_state.id,
296
- env_vars=agent_update.tool_exec_environment_variables,
297
- actor=actor,
298
- )
375
+ aid = agent.id
299
376
 
300
- # Rebuild the system prompt if it's different
301
- if agent_update.enable_sleeptime and agent_update.system is None:
302
- agent_update.system = derive_system_message(
303
- agent_type=agent_state.agent_type,
304
- enable_sleeptime=agent_update.enable_sleeptime,
305
- system=agent_update.system or agent_state.system,
306
- )
307
- if agent_update.system and agent_update.system != agent_state.system:
308
- agent_state = self.rebuild_system_prompt(agent_id=agent_state.id, actor=actor, force=True, update_timestamp=False)
377
+ if agent_update.tool_ids is not None:
378
+ self._replace_pivot_rows(
379
+ session,
380
+ ToolsAgents.__table__,
381
+ aid,
382
+ [{"agent_id": aid, "tool_id": tid} for tid in new_tools],
383
+ )
384
+ session.expire(agent, ["tools"])
309
385
 
310
- return agent_state
386
+ if agent_update.source_ids is not None:
387
+ self._replace_pivot_rows(
388
+ session,
389
+ SourcesAgents.__table__,
390
+ aid,
391
+ [{"agent_id": aid, "source_id": sid} for sid in new_sources],
392
+ )
393
+ session.expire(agent, ["sources"])
311
394
 
312
- @enforce_types
313
- def _update_agent(self, agent_id: str, agent_update: UpdateAgent, actor: PydanticUser) -> PydanticAgentState:
314
- """
315
- Update an existing agent.
395
+ if agent_update.block_ids is not None:
396
+ rows = []
397
+ if new_blocks:
398
+ label_map = {
399
+ bid: lbl
400
+ for bid, lbl in session.execute(select(BlockModel.id, BlockModel.label).where(BlockModel.id.in_(new_blocks)))
401
+ }
402
+ rows = [{"agent_id": aid, "block_id": bid, "block_label": label_map[bid]} for bid in new_blocks]
316
403
 
317
- Args:
318
- agent_id: The ID of the agent to update.
319
- agent_update: UpdateAgent object containing the updated fields.
320
- actor: User performing the action.
404
+ self._replace_pivot_rows(session, BlocksAgents.__table__, aid, rows)
405
+ session.expire(agent, ["core_memory"])
321
406
 
322
- Returns:
323
- PydanticAgentState: The updated agent as a Pydantic model.
324
- """
325
- with self.session_maker() as session:
326
- # Retrieve the existing agent
327
- agent = AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
407
+ if agent_update.identity_ids is not None:
408
+ self._replace_pivot_rows(
409
+ session,
410
+ IdentitiesAgents.__table__,
411
+ aid,
412
+ [{"agent_id": aid, "identity_id": iid} for iid in new_idents],
413
+ )
414
+ session.expire(agent, ["identities"])
328
415
 
329
- # Update scalar fields directly
330
- scalar_fields = {
331
- "name",
332
- "system",
333
- "llm_config",
334
- "embedding_config",
335
- "message_ids",
336
- "tool_rules",
337
- "description",
338
- "metadata",
339
- "project_id",
340
- "template_id",
341
- "base_template_id",
342
- "message_buffer_autoclear",
343
- "enable_sleeptime",
344
- }
345
- for field in scalar_fields:
346
- value = getattr(agent_update, field, None)
347
- if value is not None:
348
- if field == "metadata":
349
- setattr(agent, "metadata_", value)
350
- else:
351
- setattr(agent, field, value)
352
-
353
- # Update relationships using _process_relationship and _process_tags
354
- if agent_update.tool_ids is not None:
355
- _process_relationship(session, agent, "tools", ToolModel, agent_update.tool_ids, replace=True)
356
- if agent_update.source_ids is not None:
357
- _process_relationship(session, agent, "sources", SourceModel, agent_update.source_ids, replace=True)
358
- if agent_update.block_ids is not None:
359
- _process_relationship(session, agent, "core_memory", BlockModel, agent_update.block_ids, replace=True)
360
416
  if agent_update.tags is not None:
361
- _process_tags(agent, agent_update.tags, replace=True)
362
- if agent_update.identity_ids is not None:
363
- _process_relationship(session, agent, "identities", IdentityModel, agent_update.identity_ids, replace=True)
417
+ self._replace_pivot_rows(
418
+ session,
419
+ AgentsTags.__table__,
420
+ aid,
421
+ [{"agent_id": aid, "tag": tag} for tag in new_tags],
422
+ )
423
+ session.expire(agent, ["tags"])
424
+
425
+ if agent_update.tool_exec_environment_variables is not None:
426
+ session.execute(delete(AgentEnvironmentVariable).where(AgentEnvironmentVariable.agent_id == aid))
427
+ env_rows = [
428
+ {
429
+ "agent_id": aid,
430
+ "key": k,
431
+ "value": v,
432
+ "organization_id": agent.organization_id,
433
+ }
434
+ for k, v in agent_update.tool_exec_environment_variables.items()
435
+ ]
436
+ if env_rows:
437
+ self._bulk_insert_pivot(session, AgentEnvironmentVariable.__table__, env_rows)
438
+ session.expire(agent, ["tool_exec_environment_variables"])
439
+
440
+ if agent_update.enable_sleeptime and agent_update.system is None:
441
+ agent.system = derive_system_message(
442
+ agent_type=agent.agent_type,
443
+ enable_sleeptime=agent_update.enable_sleeptime,
444
+ system=agent.system,
445
+ )
364
446
 
365
- # Commit and refresh the agent
366
- agent.update(session, actor=actor)
447
+ session.flush()
448
+ session.refresh(agent)
367
449
 
368
- # Convert to PydanticAgentState and return
369
450
  return agent.to_pydantic()
370
451
 
371
452
  # TODO: Make this general and think about how to roll this into sqlalchemybase
@@ -37,6 +37,29 @@ class BlockManager:
37
37
  block.create(session, actor=actor)
38
38
  return block.to_pydantic()
39
39
 
40
+ @enforce_types
41
+ def batch_create_blocks(self, blocks: List[PydanticBlock], actor: PydanticUser) -> List[PydanticBlock]:
42
+ """
43
+ Batch-create multiple Blocks in one transaction for better performance.
44
+ Args:
45
+ blocks: List of PydanticBlock schemas to create
46
+ actor: The user performing the operation
47
+ Returns:
48
+ List of created PydanticBlock instances (with IDs, timestamps, etc.)
49
+ """
50
+ if not blocks:
51
+ return []
52
+
53
+ with self.session_maker() as session:
54
+ block_models = [
55
+ BlockModel(**block.model_dump(to_orm=True, exclude_none=True), organization_id=actor.organization_id) for block in blocks
56
+ ]
57
+
58
+ created_models = BlockModel.batch_create(items=block_models, db_session=session, actor=actor)
59
+
60
+ # Convert back to Pydantic
61
+ return [m.to_pydantic() for m in created_models]
62
+
40
63
  @enforce_types
41
64
  def update_block(self, block_id: str, block_update: BlockUpdate, actor: PydanticUser) -> PydanticBlock:
42
65
  """Update a block by its ID with the given BlockUpdate object."""
@@ -21,9 +21,11 @@ from letta.schemas.passage import Passage as PydanticPassage
21
21
  from letta.schemas.tool_rule import ToolRule
22
22
  from letta.schemas.user import User
23
23
  from letta.system import get_initial_boot_messages, get_login_event
24
+ from letta.tracing import trace_method
24
25
 
25
26
 
26
27
  # Static methods
28
+ @trace_method
27
29
  def _process_relationship(
28
30
  session, agent: AgentModel, relationship_name: str, model_class, item_ids: List[str], allow_partial=False, replace=True
29
31
  ):
@@ -29,11 +29,11 @@ class ProviderManager:
29
29
  return new_provider.to_pydantic()
30
30
 
31
31
  @enforce_types
32
- def update_provider(self, provider_update: ProviderUpdate, actor: PydanticUser) -> PydanticProvider:
32
+ def update_provider(self, provider_id: str, provider_update: ProviderUpdate, actor: PydanticUser) -> PydanticProvider:
33
33
  """Update provider details."""
34
34
  with self.session_maker() as session:
35
35
  # Retrieve the existing provider by ID
36
- existing_provider = ProviderModel.read(db_session=session, identifier=provider_update.id, actor=actor)
36
+ existing_provider = ProviderModel.read(db_session=session, identifier=provider_id, actor=actor)
37
37
 
38
38
  # Update only the fields that are provided in ProviderUpdate
39
39
  update_data = provider_update.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.6.54.dev20250420104127
3
+ Version: 0.7.0.dev20250421104249
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -51,11 +51,12 @@ Requires-Dist: isort (>=5.13.2,<6.0.0) ; extra == "dev" or extra == "all"
51
51
  Requires-Dist: jinja2 (>=3.1.5,<4.0.0)
52
52
  Requires-Dist: langchain (>=0.3.7,<0.4.0) ; extra == "external-tools" or extra == "desktop" or extra == "all"
53
53
  Requires-Dist: langchain-community (>=0.3.7,<0.4.0) ; extra == "external-tools" or extra == "desktop" or extra == "all"
54
- Requires-Dist: letta_client (>=0.1.104,<0.2.0)
54
+ Requires-Dist: letta_client (>=0.1.124,<0.2.0)
55
55
  Requires-Dist: llama-index (>=0.12.2,<0.13.0)
56
56
  Requires-Dist: llama-index-embeddings-openai (>=0.3.1,<0.4.0)
57
57
  Requires-Dist: locust (>=2.31.5,<3.0.0) ; extra == "dev" or extra == "desktop" or extra == "all"
58
58
  Requires-Dist: marshmallow-sqlalchemy (>=1.4.1,<2.0.0)
59
+ Requires-Dist: matplotlib (>=3.10.1,<4.0.0)
59
60
  Requires-Dist: mcp (>=1.3.0,<2.0.0)
60
61
  Requires-Dist: nltk (>=3.8.1,<4.0.0)
61
62
  Requires-Dist: numpy (>=1.26.2,<2.0.0)
@@ -107,26 +108,13 @@ Description-Content-Type: text/markdown
107
108
 
108
109
  <div align="center">
109
110
  <h1>Letta (previously MemGPT)</h1>
110
-
111
- **☄️ New release: Letta Agent Development Environment (_read more [here](#-access-the-ade-agent-development-environment)_) ☄️**
112
-
113
- <p align="center">
114
- <picture>
115
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/letta-ai/letta/refs/heads/main/assets/example_ade_screenshot.png">
116
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/letta-ai/letta/refs/heads/main/assets/example_ade_screenshot_light.png">
117
- <img alt="Letta logo" src="https://raw.githubusercontent.com/letta-ai/letta/refs/heads/main/assets/example_ade_screenshot.png" width="800">
118
- </picture>
119
- </p>
120
-
121
- ---
122
-
123
111
  <h3>
124
112
 
125
113
  [Homepage](https://letta.com) // [Documentation](https://docs.letta.com) // [ADE](https://docs.letta.com/agent-development-environment) // [Letta Cloud](https://forms.letta.com/early-access)
126
114
 
127
115
  </h3>
128
116
 
129
- **👾 Letta** is an open source framework for building stateful LLM applications. You can use Letta to build **stateful agents** with advanced reasoning capabilities and transparent long-term memory. The Letta framework is white box and model-agnostic.
117
+ **👾 Letta** is an open source framework for building **stateful agents** with advanced reasoning capabilities and transparent long-term memory. The Letta framework is white box and model-agnostic.
130
118
 
131
119
  [![Discord](https://img.shields.io/discord/1161736243340640419?label=Discord&logo=discord&logoColor=5865F2&style=flat-square&color=5865F2)](https://discord.gg/letta)
132
120
  [![Twitter Follow](https://img.shields.io/badge/Follow-%40Letta__AI-1DA1F2?style=flat-square&logo=x&logoColor=white)](https://twitter.com/Letta_AI)
@@ -1,4 +1,4 @@
1
- letta/__init__.py,sha256=ETKsidgnIBpWqFIzN5tWlkw9uL8R3EtF7_Vu7Ldht7Y,918
1
+ letta/__init__.py,sha256=EQrpCriqdG699Hu-3af2VUn66tanxOkvy4kEeGRrOT8,917
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
3
  letta/agent.py,sha256=V7Hn2xU1wfp16bfuRsaGMntln49OTc05yiemM5A8XI0,70279
4
4
  letta/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -19,11 +19,11 @@ letta/client/client.py,sha256=3uNsMqQyebbjFeWHHyqcManPUH7JzkBzmA9OgOLQrQs,137582
19
19
  letta/client/streaming.py,sha256=UsDS_tDTsA3HgYryIDvGGmx_dWfnfQwtmEwLi4Z89Ik,4701
20
20
  letta/client/utils.py,sha256=VCGV-op5ZSmurd4yw7Vhf93XDQ0BkyBT8qsuV7EqfiU,2859
21
21
  letta/config.py,sha256=JFGY4TWW0Wm5fTbZamOwWqk5G8Nn-TXyhgByGoAqy2c,12375
22
- letta/constants.py,sha256=R4kq9c4FPfnmJXlii0JEBljqBNeouufVPEdF2FwqFMY,8565
23
- letta/data_sources/connectors.py,sha256=R2AssXpqS7wN6VI8AfxvqaZs5S1ZACc4E_FewmR9iZI,7022
22
+ letta/constants.py,sha256=munrDJiDg06_9natYWv9mMu8OOS_tr0CdosA0YF5xQw,8571
23
+ letta/data_sources/connectors.py,sha256=m4ALtjlaZpSlJnlq2VMNzx0onowNpjvoa4ZjAB5SMQw,7243
24
24
  letta/data_sources/connectors_helper.py,sha256=oQpVlc-BjSz9sTZ7sp4PsJSXJbBKpZPi3Dam03CURTQ,3376
25
25
  letta/embeddings.py,sha256=KvC2bl5tARpVY9xcFmw4Cwu1vN0DoH266v2mSUZqwkY,10528
26
- letta/errors.py,sha256=TezN7uPt7TtOof4Iu9_tq3UxrFE0zjn2o-3_ewKCfiw,6641
26
+ letta/errors.py,sha256=GCgFBvluPxebfz1_wB0y-82N8rlVPhGIn-x7UfV3wVU,6961
27
27
  letta/functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  letta/functions/ast_parsers.py,sha256=CQI0rUoIXcLKAev_GYrXcldRIGN5ZQtk5u4FLoHe5sE,5728
29
29
  letta/functions/function_sets/base.py,sha256=Z-trOG7dKbOWpJlsEBNSGlwcQnPuRXqnQ9NMed98nbY,16392
@@ -73,7 +73,7 @@ letta/llm_api/azure_openai.py,sha256=YAkXwKyfnJFNhB45pkJVFsoxUNB_M74rQYchtw_CN6I
73
73
  letta/llm_api/azure_openai_constants.py,sha256=ZaR2IasJThijG0uhLKJThrixdAxLPD2IojfeaJ-KQMQ,294
74
74
  letta/llm_api/cohere.py,sha256=7Be_t5jt5EtWX8UUScJkX-IF9SpiZw9cDEcRC6PaxUM,14841
75
75
  letta/llm_api/deepseek.py,sha256=b1mSW8gnBrpAI8d2GcBpDyLYDnuC-P1UP6xJPalfQS4,12456
76
- letta/llm_api/google_ai_client.py,sha256=mzp06rYcfP1UHPLvFbHuwvcs7NgPncZFMY6VIcrkA1U,22643
76
+ letta/llm_api/google_ai_client.py,sha256=_uXwB5GcDO4kBTs2qtwy3Yz82rkgn8j4ytqYJDmhSW8,22686
77
77
  letta/llm_api/google_constants.py,sha256=1dqwt-YwdYGnAHV5rIPfGHfE3ybPzSn_48vlNYfd-bk,588
78
78
  letta/llm_api/google_vertex_client.py,sha256=hDj0pthZrht-dsdzyE6vZYBuSzyiLazSdcgpI76lEqg,11144
79
79
  letta/llm_api/helpers.py,sha256=sLYv30UnKBRVPuhU_KDXfKFdbkUONiDAyVEwGr86l3A,16780
@@ -147,7 +147,7 @@ letta/orm/job.py,sha256=PuSaDJlUNYRISgtmzjJFX1744q1cx5gixlSAtiIs19o,2597
147
147
  letta/orm/job_messages.py,sha256=SgwaYPYwwAC3dBtl9Xye_TWUl9H_-m95S95TTcfPyOg,1245
148
148
  letta/orm/llm_batch_items.py,sha256=eoB4Vi90GC4FIGQIpv4RYoJtuLFy_14VEGWdW2Wy95U,2756
149
149
  letta/orm/llm_batch_job.py,sha256=LaeOrnNf6FMm6ff2kOCEAjtbSuz4C5KYU5OmM90F1PY,2368
150
- letta/orm/message.py,sha256=3067u36PeDFCS7j3Sx4Y4G_HZiHNW3eQ2tkgXgNC9V0,4731
150
+ letta/orm/message.py,sha256=1IMlyvpfw1HqJel_V_tA5GxkNnJiE9mwO0mNgAIFam8,4685
151
151
  letta/orm/mixins.py,sha256=9c79Kfr-Z1hL-SDYKeoptx_yMTbBwJJBo9nrKEzSDAc,1622
152
152
  letta/orm/organization.py,sha256=STQ5x5zXoPhfagiRQX6j2lWgOqwznPp-K019MPjbY0s,3599
153
153
  letta/orm/passage.py,sha256=luoQMBAm2DwWpcGtQziMDjDP5JZZNv1pnEmx-InNHFo,3090
@@ -214,14 +214,14 @@ letta/schemas/llm_config.py,sha256=UA3_a4qv5Kb_jMQSCEZLZ0PLwVyOC332SxIcvZBM5bA,7
214
214
  letta/schemas/llm_config_overrides.py,sha256=-oRglCTcajF6UAK3RAa0FLWVuKODPI1v403fDIWMAtA,1815
215
215
  letta/schemas/memory.py,sha256=GOYDfPKzbWftUWO9Hv4KW7xAi1EIQmC8zpP7qvEkVHw,10245
216
216
  letta/schemas/message.py,sha256=cXcQjwzNE7tbwZGuFu-x8T_yUb3EyVrbHYZKSJ4GpbA,48520
217
- letta/schemas/openai/chat_completion_request.py,sha256=B07JpvTAuu_mFtKwXmP9_8w7co1a24lha5cMd8JHocE,4139
217
+ letta/schemas/openai/chat_completion_request.py,sha256=PZHzx--ooqJLsHTo1NpHwfywm5L8Id-CUXaKR6eXuko,4171
218
218
  letta/schemas/openai/chat_completion_response.py,sha256=yoepGZkg5PIobGqvATJruPdV4odpIUDHWniodSQo3PY,4433
219
219
  letta/schemas/openai/chat_completions.py,sha256=l0e9sT9boTD5VBU5YtJ0s7qUtCfFGB2K-gQLeEZ2LHU,3599
220
220
  letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNosmLVSuhXYa_xU,357
221
221
  letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
222
222
  letta/schemas/organization.py,sha256=TXrHN4IBQnX-mWvRuCOH57XZSLYCVOY0wWm2_UzDQIA,1279
223
223
  letta/schemas/passage.py,sha256=RG0vkaewEu4a_NAZM-FVyMammHjqpPP0RDYAdu27g6A,3723
224
- letta/schemas/providers.py,sha256=GE1iagHZQowosLaqIiuou1HPN61QwlJGz8QycxW2i0E,51717
224
+ letta/schemas/providers.py,sha256=lUz9QvMm_-wUUJZ5OGRsefsora0Y_55s3xQwnzL8gOw,51643
225
225
  letta/schemas/run.py,sha256=SRqPRziINIiPunjOhE_NlbnQYgxTvqmbauni_yfBQRA,2085
226
226
  letta/schemas/sandbox_config.py,sha256=SZCo3FSMz-DIBMKGu0atT4tsVFXGsqMFPaJnjrxpkX4,5993
227
227
  letta/schemas/source.py,sha256=IuenIFs7B8uOuYJIHXqR1E28wVSa-pUX6NkLZH7cukg,3141
@@ -265,7 +265,7 @@ letta/server/rest_api/routers/v1/jobs.py,sha256=4oeJfI2odNGubU_g7WSORJhn_usFsbRa
265
265
  letta/server/rest_api/routers/v1/llms.py,sha256=lYp5URXtZk1yu_Pe-p1Wq1uQ0qeb6aWtx78rXSB7N_E,881
266
266
  letta/server/rest_api/routers/v1/messages.py,sha256=bHB51Qu1q3IngwaYXLPdn7gmi3K66rf-9iLkdTWcpBI,4530
267
267
  letta/server/rest_api/routers/v1/organizations.py,sha256=r7rj-cA3shgAgM0b2JCMqjYsDIFv3ruZjU7SYbPGGqg,2831
268
- letta/server/rest_api/routers/v1/providers.py,sha256=qyZsNTXgLVsoLZoCVY4qaqiG34zCEVmRUP2Cn6maK_4,2949
268
+ letta/server/rest_api/routers/v1/providers.py,sha256=MVfAUvXj_2jx8XFwSigM-8CuCfEATW60h8J5UEmAhp0,3146
269
269
  letta/server/rest_api/routers/v1/runs.py,sha256=9nuJRjBtRgZPq3CiCEUA_3S2xPHFP5DsJxIenH5OO34,8847
270
270
  letta/server/rest_api/routers/v1/sandbox_configs.py,sha256=9hqnnMwJ3wCwO-Bezu3Xl8i3TDSIuInw3gSeHaKUXfE,8526
271
271
  letta/server/rest_api/routers/v1/sources.py,sha256=igrT2w2fkwMDhw8LZMjRug00YG5mYg08C6ZLpRHeYq4,10270
@@ -276,7 +276,7 @@ letta/server/rest_api/routers/v1/users.py,sha256=G5DBHSkPfBgVHN2Wkm-rVYiLQAudwQc
276
276
  letta/server/rest_api/routers/v1/voice.py,sha256=0lerWjrKLkt4gXLhZl1cIcgstOz9Q2HZwc67L58BCXE,2451
277
277
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
278
278
  letta/server/rest_api/utils.py,sha256=OKUWg7u4vmROVweqRrs83bQvS958bZAoR_bUFDwwqsc,14861
279
- letta/server/server.py,sha256=KgSnxcim23Bd0s5JAiN6OK8_57P-Nzuz8QjTsgcag_0,81224
279
+ letta/server/server.py,sha256=M_x7zMlLu1jCBA2ocMND2ZFT50l50KZ7AqVRcoxu834,81484
280
280
  letta/server/startup.sh,sha256=MRXh1RKbS5lyA7XAsk7O6Q4LEKOqnv5B-dwe0SnTHeQ,2514
281
281
  letta/server/static_files/assets/index-048c9598.js,sha256=mR16XppvselwKCcNgONs4L7kZEVa4OEERm4lNZYtLSk,146819
282
282
  letta/server/static_files/assets/index-0e31b727.css,sha256=SBbja96uiQVLDhDOroHgM6NSl7tS4lpJRCREgSS_hA8,7672
@@ -290,10 +290,10 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
290
290
  letta/server/ws_api/protocol.py,sha256=5mDgpfNZn_kNwHnpt5Dsuw8gdNH298sgxTGed3etzYg,1836
291
291
  letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
292
292
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
293
- letta/services/agent_manager.py,sha256=pcc31ldrMbkVJm_YqEPTPYp55kf8KS3jDYtTPSTPoBo,67566
294
- letta/services/block_manager.py,sha256=5dQT1PBxwoZF4daRSKdAwXzrFQjDzw_GCycYY9Wh3gk,14284
293
+ letta/services/agent_manager.py,sha256=kng5jZenzuA-a2LLB1mDSA-nbDmR3kQdCRltAnIBtwY,70852
294
+ letta/services/block_manager.py,sha256=rphbpGBIEDFvCJ5GJt6A1OfbURGFQZ3WardljELQyAc,15225
295
295
  letta/services/group_manager.py,sha256=z1woWRRnjkWrGG_auSicXr2bJaJ653JV6PYl2N_AUfw,12224
296
- letta/services/helpers/agent_manager_helper.py,sha256=ZVvNYKTc3g9u9Lx-3nwOKTwvpNmJ6picSaxqIQP4wh4,17772
296
+ letta/services/helpers/agent_manager_helper.py,sha256=57ARg5TcmE_JdrWmhz5uaNHAt1NGlJ3wQH1tP2XOiAs,17825
297
297
  letta/services/helpers/tool_execution_helper.py,sha256=lLoebs1kZKjw62y1PxHbIDkHq_heJN2ZT0gKje-R8oo,6941
298
298
  letta/services/identity_manager.py,sha256=PqnUnM3OGCRnd5NPjGonwYBnYAQK8rpiissdNYUouGU,9389
299
299
  letta/services/job_manager.py,sha256=Z9cSD-IqpiOIWE_UOTBUEIxGITrJ10JT1G3ske-0yIY,17166
@@ -302,7 +302,7 @@ letta/services/message_manager.py,sha256=_n7mij6bXyHqn4lppszjiXVQ7gHaiyPKEM_t6k8
302
302
  letta/services/organization_manager.py,sha256=Ax0KmPSc_YYsYaxeld9gc7ST-J6DemHQ542DD7l7AWA,3989
303
303
  letta/services/passage_manager.py,sha256=KY18gHTbx8ROBsOeR7ZAefTMGZwzbxYqOjbadqVFiyQ,9121
304
304
  letta/services/per_agent_lock_manager.py,sha256=porM0cKKANQ1FvcGXOO_qM7ARk5Fgi1HVEAhXsAg9-4,546
305
- letta/services/provider_manager.py,sha256=QOKMSZOM6eAWa2-nANWQc1frKBh8N3gqDq0V87fnSuc,3748
305
+ letta/services/provider_manager.py,sha256=_gEBW0tYIf2vJEGGYxk-nvogrFI9sjFl_97MSL5WC2s,3759
306
306
  letta/services/sandbox_config_manager.py,sha256=ATgZNWNpkdIQDUPy4ABsguHQba2PZf51-c4Ji60MzLE,13361
307
307
  letta/services/source_manager.py,sha256=SE24AiPPhpvZMGDD047H3_ZDD7OM4zHbTW1JXjPEv7U,7672
308
308
  letta/services/step_manager.py,sha256=B64iYn6Dt9yRKsSJ5vLxWQR2t-apvPLfUZyzrUsJTpI,5335
@@ -325,8 +325,8 @@ letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,1194
325
325
  letta/system.py,sha256=dnOrS2FlRMwijQnOvfrky0Lg8wEw-FUq2zzfAJOUSKA,8477
326
326
  letta/tracing.py,sha256=RstWXpfWVF77nmb_ISORVWd9IQw2Ky3de40k_S70yKI,8258
327
327
  letta/utils.py,sha256=IZFvtj9WYcrxUbkoUUYGDxMYQYdn5SgfqsvnARGsAzc,32245
328
- letta_nightly-0.6.54.dev20250420104127.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
329
- letta_nightly-0.6.54.dev20250420104127.dist-info/METADATA,sha256=ZqpL_KrBv82XOi7w4JSHiWYRxRcAv_2NrUgDGvQJJU8,22953
330
- letta_nightly-0.6.54.dev20250420104127.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
331
- letta_nightly-0.6.54.dev20250420104127.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
332
- letta_nightly-0.6.54.dev20250420104127.dist-info/RECORD,,
328
+ letta_nightly-0.7.0.dev20250421104249.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
329
+ letta_nightly-0.7.0.dev20250421104249.dist-info/METADATA,sha256=QWbK2qhFH3whOhriePYciTizbTeu3j8mL8l7xDolra8,22282
330
+ letta_nightly-0.7.0.dev20250421104249.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
331
+ letta_nightly-0.7.0.dev20250421104249.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
332
+ letta_nightly-0.7.0.dev20250421104249.dist-info/RECORD,,