letta-nightly 0.7.14.dev20250513020711__py3-none-any.whl → 0.7.15.dev20250513222014__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.7.14"
1
+ __version__ = "0.7.15"
2
2
 
3
3
  # import clients
4
4
  from letta.client.client import LocalClient, RESTClient, create_client
@@ -127,7 +127,13 @@ class BaseAgent(ABC):
127
127
  logger.exception(f"Failed to rebuild memory for agent id={agent_state.id} and actor=({self.actor.id}, {self.actor.name})")
128
128
  raise
129
129
 
130
- async def _rebuild_memory_async(self, in_context_messages: List[Message], agent_state: AgentState) -> List[Message]:
130
+ async def _rebuild_memory_async(
131
+ self,
132
+ in_context_messages: List[Message],
133
+ agent_state: AgentState,
134
+ num_messages: int | None = None, # storing these calculations is specific to the voice agent
135
+ num_archival_memories: int | None = None,
136
+ ) -> List[Message]:
131
137
  """
132
138
  Async version of function above. For now before breaking up components, changes should be made in both places.
133
139
  """
@@ -165,7 +171,7 @@ class BaseAgent(ABC):
165
171
  logger.debug(f"Rebuilding system with new memory...\nDiff:\n{diff}")
166
172
 
167
173
  # [DB Call] Update Messages
168
- new_system_message = self.message_manager.update_message_by_id_async(
174
+ new_system_message = await self.message_manager.update_message_by_id_async(
169
175
  curr_system_message.id, message_update=MessageUpdate(content=new_system_message_str), actor=self.actor
170
176
  )
171
177
  return [new_system_message] + in_context_messages[1:]
@@ -60,6 +60,10 @@ class LettaAgent(BaseAgent):
60
60
 
61
61
  self.last_function_response = self._load_last_function_response()
62
62
 
63
+ # Cached archival memory/message size
64
+ self.num_messages = self.message_manager.size(actor=self.actor, agent_id=agent_id)
65
+ self.num_archival_memories = self.passage_manager.size(actor=self.actor, agent_id=agent_id)
66
+
63
67
  @trace_method
64
68
  async def step(self, input_messages: List[MessageCreate], max_steps: int = 10) -> LettaResponse:
65
69
  agent_state = self.agent_manager.get_agent_by_id(self.agent_id, actor=self.actor)
@@ -164,6 +168,11 @@ class LettaAgent(BaseAgent):
164
168
  message_ids = [m.id for m in (current_in_context_messages + new_in_context_messages)]
165
169
  self.agent_manager.set_in_context_messages(agent_id=self.agent_id, message_ids=message_ids, actor=self.actor)
166
170
 
171
+ # TODO: This may be out of sync, if in between steps users add files
172
+ # NOTE (cliandy): temporary for now for particlar use cases.
173
+ self.num_messages = self.message_manager.size(actor=self.actor, agent_id=agent_state.id)
174
+ self.num_archival_memories = self.passage_manager.size(actor=self.actor, agent_id=agent_state.id)
175
+
167
176
  # TODO: Also yield out a letta usage stats SSE
168
177
 
169
178
  yield f"data: {MessageStreamStatus.done.model_dump_json()}\n\n"
@@ -179,7 +188,9 @@ class LettaAgent(BaseAgent):
179
188
  stream: bool,
180
189
  ) -> ChatCompletion | AsyncStream[ChatCompletionChunk]:
181
190
  if settings.experimental_enable_async_db_engine:
182
- in_context_messages = await self._rebuild_memory_async(in_context_messages, agent_state)
191
+ in_context_messages = await self._rebuild_memory_async(
192
+ in_context_messages, agent_state, num_messages=self.num_messages, num_archival_memories=self.num_archival_memories
193
+ )
183
194
  else:
184
195
  if settings.experimental_skip_rebuild_memory and agent_state.llm_config.model_endpoint_type == "google_vertex":
185
196
  logger.info("Skipping memory rebuild")
letta/llm_api/openai.py CHANGED
@@ -393,7 +393,9 @@ def openai_chat_completions_process_stream(
393
393
  if tool_call_delta.id is not None:
394
394
  # TODO assert that we're not overwriting?
395
395
  # TODO += instead of =?
396
- if tool_call_delta.index not in range(len(accum_message.tool_calls)):
396
+ try:
397
+ accum_message.tool_calls[tool_call_delta.index].id = tool_call_delta.id
398
+ except IndexError:
397
399
  warnings.warn(
398
400
  f"Tool call index out of range ({tool_call_delta.index})\ncurrent tool calls: {accum_message.tool_calls}\ncurrent delta: {tool_call_delta}"
399
401
  )
@@ -403,25 +405,21 @@ def openai_chat_completions_process_stream(
403
405
  accum_message.tool_calls[tool_call_delta.index].id = tool_call_delta.id
404
406
  if tool_call_delta.function is not None:
405
407
  if tool_call_delta.function.name is not None:
406
- # TODO assert that we're not overwriting?
407
- # TODO += instead of =?
408
- if tool_call_delta.index not in range(len(accum_message.tool_calls)):
408
+ try:
409
+ accum_message.tool_calls[
410
+ tool_call_delta.index
411
+ ].function.name += tool_call_delta.function.name # TODO check for parallel tool calls
412
+ except IndexError:
409
413
  warnings.warn(
410
414
  f"Tool call index out of range ({tool_call_delta.index})\ncurrent tool calls: {accum_message.tool_calls}\ncurrent delta: {tool_call_delta}"
411
415
  )
412
- # force index 0
413
- # accum_message.tool_calls[0].function.name = tool_call_delta.function.name
414
- else:
415
- accum_message.tool_calls[tool_call_delta.index].function.name = tool_call_delta.function.name
416
416
  if tool_call_delta.function.arguments is not None:
417
- if tool_call_delta.index not in range(len(accum_message.tool_calls)):
417
+ try:
418
+ accum_message.tool_calls[tool_call_delta.index].function.arguments += tool_call_delta.function.arguments
419
+ except IndexError:
418
420
  warnings.warn(
419
421
  f"Tool call index out of range ({tool_call_delta.index})\ncurrent tool calls: {accum_message.tool_calls}\ncurrent delta: {tool_call_delta}"
420
422
  )
421
- # force index 0
422
- # accum_message.tool_calls[0].function.arguments += tool_call_delta.function.arguments
423
- else:
424
- accum_message.tool_calls[tool_call_delta.index].function.arguments += tool_call_delta.function.arguments
425
423
 
426
424
  if message_delta.function_call is not None:
427
425
  raise NotImplementedError(f"Old function_call style not support with stream=True")
letta/server/db.py CHANGED
@@ -123,6 +123,7 @@ class DatabaseRegistry:
123
123
  async_pg_uri = pg_uri.replace("postgresql://", "postgresql+asyncpg://")
124
124
  else:
125
125
  async_pg_uri = f"postgresql+asyncpg://{pg_uri.split('://', 1)[1]}" if "://" in pg_uri else pg_uri
126
+ async_pg_uri = async_pg_uri.replace("sslmode=", "ssl=")
126
127
 
127
128
  async_engine = create_async_engine(
128
129
  async_pg_uri,
@@ -15,19 +15,26 @@ router = APIRouter(prefix="/blocks", tags=["blocks"])
15
15
 
16
16
 
17
17
  @router.get("/", response_model=List[Block], operation_id="list_blocks")
18
- def list_blocks(
18
+ async def list_blocks(
19
19
  # query parameters
20
20
  label: Optional[str] = Query(None, description="Labels to include (e.g. human, persona)"),
21
21
  templates_only: bool = Query(False, description="Whether to include only templates"),
22
22
  name: Optional[str] = Query(None, description="Name of the block"),
23
23
  identity_id: Optional[str] = Query(None, description="Search agents by identifier id"),
24
24
  identifier_keys: Optional[List[str]] = Query(None, description="Search agents by identifier keys"),
25
+ limit: Optional[int] = Query(50, description="Number of blocks to return"),
25
26
  server: SyncServer = Depends(get_letta_server),
26
27
  actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
27
28
  ):
28
29
  actor = server.user_manager.get_user_or_default(user_id=actor_id)
29
- return server.block_manager.get_blocks(
30
- actor=actor, label=label, is_template=templates_only, template_name=name, identity_id=identity_id, identifier_keys=identifier_keys
30
+ return await server.block_manager.get_blocks_async(
31
+ actor=actor,
32
+ label=label,
33
+ is_template=templates_only,
34
+ template_name=name,
35
+ identity_id=identity_id,
36
+ identifier_keys=identifier_keys,
37
+ limit=limit,
31
38
  )
32
39
 
33
40
 
@@ -117,6 +117,68 @@ class BlockManager:
117
117
 
118
118
  return [block.to_pydantic() for block in blocks]
119
119
 
120
+ @enforce_types
121
+ async def get_blocks_async(
122
+ self,
123
+ actor: PydanticUser,
124
+ label: Optional[str] = None,
125
+ is_template: Optional[bool] = None,
126
+ template_name: Optional[str] = None,
127
+ identity_id: Optional[str] = None,
128
+ identifier_keys: Optional[List[str]] = None,
129
+ limit: Optional[int] = 50,
130
+ ) -> List[PydanticBlock]:
131
+ """Async version of get_blocks method. Retrieve blocks based on various optional filters."""
132
+ from sqlalchemy import select
133
+ from sqlalchemy.orm import noload
134
+
135
+ from letta.orm.sqlalchemy_base import AccessType
136
+
137
+ async with db_registry.async_session() as session:
138
+ # Start with a basic query
139
+ query = select(BlockModel)
140
+
141
+ # Explicitly avoid loading relationships
142
+ query = query.options(noload(BlockModel.agents), noload(BlockModel.identities), noload(BlockModel.groups))
143
+
144
+ # Apply access control
145
+ query = BlockModel.apply_access_predicate(query, actor, ["read"], AccessType.ORGANIZATION)
146
+
147
+ # Add filters
148
+ query = query.where(BlockModel.organization_id == actor.organization_id)
149
+ if label:
150
+ query = query.where(BlockModel.label == label)
151
+
152
+ if is_template is not None:
153
+ query = query.where(BlockModel.is_template == is_template)
154
+
155
+ if template_name:
156
+ query = query.where(BlockModel.template_name == template_name)
157
+
158
+ if identifier_keys:
159
+ query = (
160
+ query.join(BlockModel.identities)
161
+ .filter(BlockModel.identities.property.mapper.class_.identifier_key.in_(identifier_keys))
162
+ .distinct(BlockModel.id)
163
+ )
164
+
165
+ if identity_id:
166
+ query = (
167
+ query.join(BlockModel.identities)
168
+ .filter(BlockModel.identities.property.mapper.class_.id == identity_id)
169
+ .distinct(BlockModel.id)
170
+ )
171
+
172
+ # Add limit
173
+ if limit:
174
+ query = query.limit(limit)
175
+
176
+ # Execute the query
177
+ result = await session.execute(query)
178
+ blocks = result.scalars().all()
179
+
180
+ return [block.to_pydantic() for block in blocks]
181
+
120
182
  @enforce_types
121
183
  def get_block_by_id(self, block_id: str, actor: Optional[PydanticUser] = None) -> Optional[PydanticBlock]:
122
184
  """Retrieve a block by its name."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.7.14.dev20250513020711
3
+ Version: 0.7.15.dev20250513222014
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -1,12 +1,12 @@
1
- letta/__init__.py,sha256=7Ep42kC5nugZGxpjAao1Kw_lhwmHtPAKOX5qpVNCODs,916
1
+ letta/__init__.py,sha256=Ap4nwYkADwFkMV8IJRB4Ltgfokb5-vyCkz5Df4WsBFo,916
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
3
  letta/agent.py,sha256=Arm-3QuE8nJ8X25HZ2CBnYhs_3j-9ztkjSHKXFwIlpI,71966
4
4
  letta/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- letta/agents/base_agent.py,sha256=pR-6k87V7R1oJBInS5LPPCC05h3v6gaZ7chb08WG5CQ,8087
5
+ letta/agents/base_agent.py,sha256=ZmuSpYF8a5I0QXs8LDgS4jaA9k-6Pu2W-hl4E8A1ELo,8276
6
6
  letta/agents/ephemeral_agent.py,sha256=el-SUF_16vv_7OouIR-6z0pAE9Yc0PLibygvfCKwqfo,2736
7
7
  letta/agents/exceptions.py,sha256=BQY4D4w32OYHM63CM19ko7dPwZiAzUs3NbKvzmCTcJg,318
8
8
  letta/agents/helpers.py,sha256=yAiRTS0sanXmI6fsoQ6LXXDteNaxZ8fY_K66rsGEFT4,4007
9
- letta/agents/letta_agent.py,sha256=lAoEVAVHw4YRebUNyU7gq8XcGCYUnnpkSqD41WKeyWE,18302
9
+ letta/agents/letta_agent.py,sha256=k5K17VPb6ldNKqhiZEFAGSZ5-ET2GUeYek4QVBYxq7s,19002
10
10
  letta/agents/letta_agent_batch.py,sha256=uUmmhF-zmaJc18CMFvmDArxnMljUpYOkJc3RrFaGHUg,24543
11
11
  letta/agents/voice_agent.py,sha256=wCF2adlbDTEk_P3UrGPCHZy4IGvw75TUGDePW6N-sGA,21402
12
12
  letta/agents/voice_sleeptime_agent.py,sha256=Joi3-8emTpV7v86OR_HGYXblkulrNaHhudCvPmMyXz0,7274
@@ -86,7 +86,7 @@ letta/llm_api/llm_api_tools.py,sha256=YbslnPRFiJwaxRMhZ4T8K8SgByw73Ck61k3KNLScPr
86
86
  letta/llm_api/llm_client.py,sha256=qJ92biZVCXyRESzZX3ODeMd-kcgkYtJGdp6oVjV7PBc,2074
87
87
  letta/llm_api/llm_client_base.py,sha256=eID0J0l4VITZDL5765xEDvV8WLL4WzwQAzFEsC7ea6I,6067
88
88
  letta/llm_api/mistral.py,sha256=fHdfD9ug-rQIk2qn8tRKay1U6w9maF11ryhKi91FfXM,1593
89
- letta/llm_api/openai.py,sha256=AG6mljvunOc9meWE_B7j4wmVg55Sjv77The5Pqwr0rY,25796
89
+ letta/llm_api/openai.py,sha256=cogO-BTGE4GgM9emz2NFsjodZ-btNmZpEESKKjnYHqo,25426
90
90
  letta/llm_api/openai_client.py,sha256=NACYguMG_4nb5LqqdnR6XiD9RLqNLpn5TeHUzRL9VMc,15294
91
91
  letta/local_llm/README.md,sha256=hFJyw5B0TU2jrh9nb0zGZMgdH-Ei1dSRfhvPQG_NSoU,168
92
92
  letta/local_llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -252,7 +252,7 @@ letta/serialize_schemas/marshmallow_tool.py,sha256=jwU69BDCakPlYPSk-ta21kuvsURKO
252
252
  letta/serialize_schemas/pydantic_agent_schema.py,sha256=NKq70niUVMI3_lxMKc3u3rOBUhm77bIFaPRj9aidMUQ,3006
253
253
  letta/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
254
254
  letta/server/constants.py,sha256=yAdGbLkzlOU_dLTx0lKDmAnj0ZgRXCEaIcPJWO69eaE,92
255
- letta/server/db.py,sha256=209vKw_Gfjmn6cKCUKTe2fRBK5sr50shhXq2kfdJ2tY,9308
255
+ letta/server/db.py,sha256=atTS-QOBrY7LNbap9cUdM7LCDnK6KWxZyO6f0757-t4,9380
256
256
  letta/server/generate_openapi_schema.sh,sha256=0OtBhkC1g6CobVmNEd_m2B6sTdppjbJLXaM95icejvE,371
257
257
  letta/server/rest_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
258
  letta/server/rest_api/app.py,sha256=8Y5R_t_s4IAG7wRfdbmCeF5YT9pfGG4h6_kpUAaUwoQ,14771
@@ -267,7 +267,7 @@ letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj
267
267
  letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=QBWab1fn2LXVDMtc6li3gOzmrNzDiUw5WUJsMeeMZII,5076
268
268
  letta/server/rest_api/routers/v1/__init__.py,sha256=_skmAcDOK9ovHKfywRaBgigo3IvPmnUSQSR2hGVCOhY,1664
269
269
  letta/server/rest_api/routers/v1/agents.py,sha256=SzRxDV9eXG0LieBZ0OSLse_Iu7LHlWNqPtuk_ri2-tU,35286
270
- letta/server/rest_api/routers/v1/blocks.py,sha256=jrDpSYrEgHaGvlnUCn6wczgWnCZa3ZyHVL5NQv2KJNE,4471
270
+ letta/server/rest_api/routers/v1/blocks.py,sha256=XuHdwdWwDm7qdBIVv95MnUcekSEMJ1New_-CEqVZew8,4631
271
271
  letta/server/rest_api/routers/v1/embeddings.py,sha256=P-Dvt_HNKoTyjRwkScAMg1hlB3cNxMeAQwV7bSatsKI,957
272
272
  letta/server/rest_api/routers/v1/groups.py,sha256=JI9ShKewoE8lB58OP02NuAT7eUzPfqSG7y44a6tBh9s,10710
273
273
  letta/server/rest_api/routers/v1/health.py,sha256=MoOjkydhGcJXTiuJrKIB0etVXiRMdTa51S8RQ8-50DQ,399
@@ -302,7 +302,7 @@ letta/server/ws_api/protocol.py,sha256=5mDgpfNZn_kNwHnpt5Dsuw8gdNH298sgxTGed3etz
302
302
  letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
303
303
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
304
304
  letta/services/agent_manager.py,sha256=LBdJ5-APQyjjPJWWuVs9TUtwbTKAguWDqVi1uP_vWoc,72479
305
- letta/services/block_manager.py,sha256=hIV1OyUMI2GzbAD6Niswg1XJCoHFubxGxO1vzrnaY2I,18332
305
+ letta/services/block_manager.py,sha256=INSCN4McqZM7fPO1xoaXOg7UE0WO6pO_H0NFXQiR46A,20655
306
306
  letta/services/group_manager.py,sha256=X5Z-0j9h95H5p3kKo8m5FZbl0HFN1slxFvcph7fTdvc,15833
307
307
  letta/services/helpers/agent_manager_helper.py,sha256=2W9DpxGOx3rK2LnpGDtQmBJh9u9sKZ_xwAUAYzAMyS0,20350
308
308
  letta/services/helpers/tool_execution_helper.py,sha256=JdH6VTWFrXfwPWsWNSZFKuRFhhXp8qiDYWjbPc8PLLI,7649
@@ -341,8 +341,8 @@ letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,1194
341
341
  letta/system.py,sha256=mKxmvvekuP8mdgsebRINGBoFbUdJhxLJ260crPBNVyk,8386
342
342
  letta/tracing.py,sha256=j9uyBbx02erQZ307XmZmZSNyzQt-d7ZDB7vhFhjDlsU,8448
343
343
  letta/utils.py,sha256=W8J1FfhRADFqoyx3J8-Z1_aWyG433PBoEh_b5wdOZIg,32262
344
- letta_nightly-0.7.14.dev20250513020711.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
345
- letta_nightly-0.7.14.dev20250513020711.dist-info/METADATA,sha256=-HgYrQ-mH6kiG7bkpvyee71IOqjb-HCPTtsr-CbpAks,22274
346
- letta_nightly-0.7.14.dev20250513020711.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
347
- letta_nightly-0.7.14.dev20250513020711.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
348
- letta_nightly-0.7.14.dev20250513020711.dist-info/RECORD,,
344
+ letta_nightly-0.7.15.dev20250513222014.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
345
+ letta_nightly-0.7.15.dev20250513222014.dist-info/METADATA,sha256=6jwxd5EX0GwvMkmIFMO1MDB0memZHZv5e5ylH17eCSg,22274
346
+ letta_nightly-0.7.15.dev20250513222014.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
347
+ letta_nightly-0.7.15.dev20250513222014.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
348
+ letta_nightly-0.7.15.dev20250513222014.dist-info/RECORD,,