letta-nightly 0.6.39.dev20250314104053__py3-none-any.whl → 0.6.40.dev20250314222759__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.

Files changed (67) hide show
  1. letta/__init__.py +1 -1
  2. letta/agent.py +14 -4
  3. letta/agents/ephemeral_agent.py +2 -1
  4. letta/agents/low_latency_agent.py +8 -0
  5. letta/dynamic_multi_agent.py +274 -0
  6. letta/functions/function_sets/base.py +1 -0
  7. letta/functions/function_sets/extras.py +2 -1
  8. letta/functions/function_sets/multi_agent.py +17 -0
  9. letta/functions/helpers.py +41 -0
  10. letta/functions/mcp_client/__init__.py +0 -0
  11. letta/functions/mcp_client/base_client.py +61 -0
  12. letta/functions/mcp_client/sse_client.py +21 -0
  13. letta/functions/mcp_client/stdio_client.py +103 -0
  14. letta/functions/mcp_client/types.py +48 -0
  15. letta/functions/schema_generator.py +1 -1
  16. letta/helpers/converters.py +67 -0
  17. letta/llm_api/openai.py +1 -1
  18. letta/memory.py +2 -1
  19. letta/orm/__init__.py +2 -0
  20. letta/orm/agent.py +69 -20
  21. letta/orm/custom_columns.py +15 -0
  22. letta/orm/group.py +33 -0
  23. letta/orm/groups_agents.py +13 -0
  24. letta/orm/message.py +7 -4
  25. letta/orm/organization.py +1 -0
  26. letta/orm/sqlalchemy_base.py +3 -3
  27. letta/round_robin_multi_agent.py +152 -0
  28. letta/schemas/agent.py +3 -0
  29. letta/schemas/enums.py +0 -4
  30. letta/schemas/group.py +65 -0
  31. letta/schemas/letta_message.py +167 -106
  32. letta/schemas/letta_message_content.py +192 -0
  33. letta/schemas/message.py +28 -36
  34. letta/schemas/tool.py +1 -1
  35. letta/serialize_schemas/__init__.py +1 -1
  36. letta/serialize_schemas/marshmallow_agent.py +108 -0
  37. letta/serialize_schemas/{agent_environment_variable.py → marshmallow_agent_environment_variable.py} +1 -1
  38. letta/serialize_schemas/marshmallow_base.py +52 -0
  39. letta/serialize_schemas/{block.py → marshmallow_block.py} +1 -1
  40. letta/serialize_schemas/{custom_fields.py → marshmallow_custom_fields.py} +12 -0
  41. letta/serialize_schemas/marshmallow_message.py +42 -0
  42. letta/serialize_schemas/{tag.py → marshmallow_tag.py} +12 -2
  43. letta/serialize_schemas/{tool.py → marshmallow_tool.py} +1 -1
  44. letta/serialize_schemas/pydantic_agent_schema.py +111 -0
  45. letta/server/rest_api/app.py +15 -0
  46. letta/server/rest_api/routers/v1/__init__.py +2 -0
  47. letta/server/rest_api/routers/v1/agents.py +46 -40
  48. letta/server/rest_api/routers/v1/groups.py +233 -0
  49. letta/server/rest_api/routers/v1/tools.py +31 -3
  50. letta/server/rest_api/utils.py +1 -1
  51. letta/server/server.py +272 -22
  52. letta/services/agent_manager.py +65 -28
  53. letta/services/group_manager.py +147 -0
  54. letta/services/helpers/agent_manager_helper.py +151 -1
  55. letta/services/message_manager.py +11 -3
  56. letta/services/passage_manager.py +15 -0
  57. letta/settings.py +5 -0
  58. letta/supervisor_multi_agent.py +103 -0
  59. {letta_nightly-0.6.39.dev20250314104053.dist-info → letta_nightly-0.6.40.dev20250314222759.dist-info}/METADATA +1 -2
  60. {letta_nightly-0.6.39.dev20250314104053.dist-info → letta_nightly-0.6.40.dev20250314222759.dist-info}/RECORD +63 -49
  61. letta/helpers/mcp_helpers.py +0 -108
  62. letta/serialize_schemas/agent.py +0 -80
  63. letta/serialize_schemas/base.py +0 -64
  64. letta/serialize_schemas/message.py +0 -29
  65. {letta_nightly-0.6.39.dev20250314104053.dist-info → letta_nightly-0.6.40.dev20250314222759.dist-info}/LICENSE +0 -0
  66. {letta_nightly-0.6.39.dev20250314104053.dist-info → letta_nightly-0.6.40.dev20250314222759.dist-info}/WHEEL +0 -0
  67. {letta_nightly-0.6.39.dev20250314104053.dist-info → letta_nightly-0.6.40.dev20250314222759.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,147 @@
1
+ from typing import List, Optional
2
+
3
+ from sqlalchemy.orm import Session
4
+
5
+ from letta.orm.agent import Agent as AgentModel
6
+ from letta.orm.errors import NoResultFound
7
+ from letta.orm.group import Group as GroupModel
8
+ from letta.orm.message import Message as MessageModel
9
+ from letta.schemas.group import Group as PydanticGroup
10
+ from letta.schemas.group import GroupCreate, ManagerType
11
+ from letta.schemas.user import User as PydanticUser
12
+ from letta.utils import enforce_types
13
+
14
+
15
+ class GroupManager:
16
+
17
+ def __init__(self):
18
+ from letta.server.db import db_context
19
+
20
+ self.session_maker = db_context
21
+
22
+ @enforce_types
23
+ def list_groups(
24
+ self,
25
+ project_id: Optional[str] = None,
26
+ manager_type: Optional[ManagerType] = None,
27
+ before: Optional[str] = None,
28
+ after: Optional[str] = None,
29
+ limit: Optional[int] = 50,
30
+ actor: PydanticUser = None,
31
+ ) -> list[PydanticGroup]:
32
+ with self.session_maker() as session:
33
+ filters = {"organization_id": actor.organization_id}
34
+ if project_id:
35
+ filters["project_id"] = project_id
36
+ if manager_type:
37
+ filters["manager_type"] = manager_type
38
+ groups = GroupModel.list(
39
+ db_session=session,
40
+ before=before,
41
+ after=after,
42
+ limit=limit,
43
+ **filters,
44
+ )
45
+ return [group.to_pydantic() for group in groups]
46
+
47
+ @enforce_types
48
+ def retrieve_group(self, group_id: str, actor: PydanticUser) -> PydanticGroup:
49
+ with self.session_maker() as session:
50
+ group = GroupModel.read(db_session=session, identifier=group_id, actor=actor)
51
+ return group.to_pydantic()
52
+
53
+ @enforce_types
54
+ def create_group(self, group: GroupCreate, actor: PydanticUser) -> PydanticGroup:
55
+ with self.session_maker() as session:
56
+ new_group = GroupModel()
57
+ new_group.organization_id = actor.organization_id
58
+ new_group.description = group.description
59
+ self._process_agent_relationship(session=session, group=new_group, agent_ids=group.agent_ids, allow_partial=False)
60
+ if group.manager_config is None:
61
+ new_group.manager_type = ManagerType.round_robin
62
+ else:
63
+ match group.manager_config.manager_type:
64
+ case ManagerType.round_robin:
65
+ new_group.manager_type = ManagerType.round_robin
66
+ new_group.max_turns = group.manager_config.max_turns
67
+ case ManagerType.dynamic:
68
+ new_group.manager_type = ManagerType.dynamic
69
+ new_group.manager_agent_id = group.manager_config.manager_agent_id
70
+ new_group.max_turns = group.manager_config.max_turns
71
+ new_group.termination_token = group.manager_config.termination_token
72
+ case ManagerType.supervisor:
73
+ new_group.manager_type = ManagerType.supervisor
74
+ new_group.manager_agent_id = group.manager_config.manager_agent_id
75
+ case _:
76
+ raise ValueError(f"Unsupported manager type: {group.manager_config.manager_type}")
77
+ new_group.create(session, actor=actor)
78
+ return new_group.to_pydantic()
79
+
80
+ @enforce_types
81
+ def delete_group(self, group_id: str, actor: PydanticUser) -> None:
82
+ with self.session_maker() as session:
83
+ # Retrieve the agent
84
+ group = GroupModel.read(db_session=session, identifier=group_id, actor=actor)
85
+ group.hard_delete(session)
86
+
87
+ @enforce_types
88
+ def list_group_messages(
89
+ self,
90
+ group_id: Optional[str] = None,
91
+ before: Optional[str] = None,
92
+ after: Optional[str] = None,
93
+ limit: Optional[int] = 50,
94
+ actor: PydanticUser = None,
95
+ use_assistant_message: bool = True,
96
+ assistant_message_tool_name: str = "send_message",
97
+ assistant_message_tool_kwarg: str = "message",
98
+ ) -> list[PydanticGroup]:
99
+ with self.session_maker() as session:
100
+ group = GroupModel.read(db_session=session, identifier=group_id, actor=actor)
101
+ agent_id = group.manager_agent_id if group.manager_agent_id else group.agent_ids[0]
102
+
103
+ filters = {
104
+ "organization_id": actor.organization_id,
105
+ "group_id": group_id,
106
+ "agent_id": agent_id,
107
+ }
108
+ messages = MessageModel.list(
109
+ db_session=session,
110
+ before=before,
111
+ after=after,
112
+ limit=limit,
113
+ **filters,
114
+ )
115
+
116
+ messages = PydanticMessage.to_letta_messages_from_list(
117
+ messages=messages,
118
+ use_assistant_message=use_assistant_message,
119
+ assistant_message_tool_name=assistant_message_tool_name,
120
+ assistant_message_tool_kwarg=assistant_message_tool_kwarg,
121
+ )
122
+
123
+ return messages
124
+
125
+ def _process_agent_relationship(self, session: Session, group: GroupModel, agent_ids: List[str], allow_partial=False, replace=True):
126
+ current_relationship = getattr(group, "agents", [])
127
+ if not agent_ids:
128
+ if replace:
129
+ setattr(group, "agents", [])
130
+ return
131
+
132
+ # Retrieve models for the provided IDs
133
+ found_items = session.query(AgentModel).filter(AgentModel.id.in_(agent_ids)).all()
134
+
135
+ # Validate all items are found if allow_partial is False
136
+ if not allow_partial and len(found_items) != len(agent_ids):
137
+ missing = set(agent_ids) - {item.id for item in found_items}
138
+ raise NoResultFound(f"Items not found in agents: {missing}")
139
+
140
+ if replace:
141
+ # Replace the relationship
142
+ setattr(group, "agents", found_items)
143
+ else:
144
+ # Extend the relationship (only add new items)
145
+ current_ids = {item.id for item in current_relationship}
146
+ new_items = [item for item in found_items if item.id not in current_ids]
147
+ current_relationship.extend(new_items)
@@ -1,6 +1,8 @@
1
1
  import datetime
2
2
  from typing import List, Literal, Optional
3
3
 
4
+ from sqlalchemy import and_, func, literal, or_, select
5
+
4
6
  from letta import system
5
7
  from letta.constants import IN_CONTEXT_MEMORY_KEYWORD, STRUCTURED_OUTPUT_MODELS
6
8
  from letta.helpers import ToolRulesSolver
@@ -8,11 +10,13 @@ from letta.helpers.datetime_helpers import get_local_time
8
10
  from letta.orm.agent import Agent as AgentModel
9
11
  from letta.orm.agents_tags import AgentsTags
10
12
  from letta.orm.errors import NoResultFound
13
+ from letta.orm.identity import Identity
11
14
  from letta.prompts import gpt_system
12
15
  from letta.schemas.agent import AgentState, AgentType
13
16
  from letta.schemas.enums import MessageRole
17
+ from letta.schemas.letta_message_content import TextContent
14
18
  from letta.schemas.memory import Memory
15
- from letta.schemas.message import Message, MessageCreate, TextContent
19
+ from letta.schemas.message import Message, MessageCreate
16
20
  from letta.schemas.passage import Passage as PydanticPassage
17
21
  from letta.schemas.tool_rule import ToolRule
18
22
  from letta.schemas.user import User
@@ -293,3 +297,149 @@ def check_supports_structured_output(model: str, tool_rules: List[ToolRule]) ->
293
297
  return False
294
298
  else:
295
299
  return True
300
+
301
+
302
+ def _apply_pagination(query, before: Optional[str], after: Optional[str], session) -> any:
303
+ """
304
+ Apply cursor-based pagination filters using the agent's created_at timestamp with id as a tie-breaker.
305
+
306
+ Instead of relying on the UUID ordering, this function uses the agent's creation time
307
+ (and id for tie-breaking) to paginate the results. It performs a minimal lookup to fetch
308
+ only the created_at and id for the agent corresponding to the provided cursor.
309
+
310
+ Args:
311
+ query: The SQLAlchemy query object to modify.
312
+ before (Optional[str]): Cursor (agent id) to return agents created before this agent.
313
+ after (Optional[str]): Cursor (agent id) to return agents created after this agent.
314
+ session: The active database session used to execute the minimal lookup.
315
+
316
+ Returns:
317
+ The modified query with pagination filters applied and ordered by created_at and id.
318
+ """
319
+ if after:
320
+ # Retrieve only the created_at and id for the agent corresponding to the 'after' cursor.
321
+ result = session.execute(select(AgentModel.created_at, AgentModel.id).where(AgentModel.id == after)).first()
322
+ if result:
323
+ after_created_at, after_id = result
324
+ # Filter: include agents created after the reference, or at the same time but with a greater id.
325
+ query = query.where(
326
+ or_(
327
+ AgentModel.created_at > after_created_at,
328
+ and_(
329
+ AgentModel.created_at == after_created_at,
330
+ AgentModel.id > after_id,
331
+ ),
332
+ )
333
+ )
334
+ if before:
335
+ # Retrieve only the created_at and id for the agent corresponding to the 'before' cursor.
336
+ result = session.execute(select(AgentModel.created_at, AgentModel.id).where(AgentModel.id == before)).first()
337
+ if result:
338
+ before_created_at, before_id = result
339
+ # Filter: include agents created before the reference, or at the same time but with a smaller id.
340
+ query = query.where(
341
+ or_(
342
+ AgentModel.created_at < before_created_at,
343
+ and_(
344
+ AgentModel.created_at == before_created_at,
345
+ AgentModel.id < before_id,
346
+ ),
347
+ )
348
+ )
349
+ # Enforce a deterministic ordering: first by created_at, then by id.
350
+ query = query.order_by(AgentModel.created_at.asc(), AgentModel.id.asc())
351
+ return query
352
+
353
+
354
+ def _apply_tag_filter(query, tags: Optional[List[str]], match_all_tags: bool):
355
+ """
356
+ Apply tag-based filtering to the agent query.
357
+
358
+ This helper function creates a subquery that groups agent IDs by their tags.
359
+ If `match_all_tags` is True, it filters agents that have all of the specified tags.
360
+ Otherwise, it filters agents that have any of the tags.
361
+
362
+ Args:
363
+ query: The SQLAlchemy query object to be modified.
364
+ tags (Optional[List[str]]): A list of tags to filter agents.
365
+ match_all_tags (bool): If True, only return agents that match all provided tags.
366
+
367
+ Returns:
368
+ The modified query with tag filters applied.
369
+ """
370
+ if tags:
371
+ # Build a subquery to select agent IDs that have the specified tags.
372
+ subquery = select(AgentsTags.agent_id).where(AgentsTags.tag.in_(tags)).group_by(AgentsTags.agent_id)
373
+ # If all tags must match, add a HAVING clause to ensure the count of tags equals the number provided.
374
+ if match_all_tags:
375
+ subquery = subquery.having(func.count(AgentsTags.tag) == literal(len(tags)))
376
+ # Filter the main query to include only agents present in the subquery.
377
+ query = query.where(AgentModel.id.in_(subquery))
378
+ return query
379
+
380
+
381
+ def _apply_identity_filters(query, identity_id: Optional[str], identifier_keys: Optional[List[str]]):
382
+ """
383
+ Apply identity-related filters to the agent query.
384
+
385
+ This helper function joins the identities relationship and filters the agents based on
386
+ a specific identity ID and/or a list of identifier keys.
387
+
388
+ Args:
389
+ query: The SQLAlchemy query object to be modified.
390
+ identity_id (Optional[str]): The identity ID to filter by.
391
+ identifier_keys (Optional[List[str]]): A list of identifier keys to filter agents.
392
+
393
+ Returns:
394
+ The modified query with identity filters applied.
395
+ """
396
+ # Join the identities relationship and filter by a specific identity ID.
397
+ if identity_id:
398
+ query = query.join(AgentModel.identities).where(Identity.id == identity_id)
399
+ # Join the identities relationship and filter by a set of identifier keys.
400
+ if identifier_keys:
401
+ query = query.join(AgentModel.identities).where(Identity.identifier_key.in_(identifier_keys))
402
+ return query
403
+
404
+
405
+ def _apply_filters(
406
+ query,
407
+ name: Optional[str],
408
+ query_text: Optional[str],
409
+ project_id: Optional[str],
410
+ template_id: Optional[str],
411
+ base_template_id: Optional[str],
412
+ ):
413
+ """
414
+ Apply basic filtering criteria to the agent query.
415
+
416
+ This helper function adds WHERE clauses based on provided parameters such as
417
+ exact name, partial name match (using ILIKE), project ID, template ID, and base template ID.
418
+
419
+ Args:
420
+ query: The SQLAlchemy query object to be modified.
421
+ name (Optional[str]): Exact name to filter by.
422
+ query_text (Optional[str]): Partial text to search in the agent's name (case-insensitive).
423
+ project_id (Optional[str]): Filter for agents belonging to a specific project.
424
+ template_id (Optional[str]): Filter for agents using a specific template.
425
+ base_template_id (Optional[str]): Filter for agents using a specific base template.
426
+
427
+ Returns:
428
+ The modified query with the applied filters.
429
+ """
430
+ # Filter by exact agent name if provided.
431
+ if name:
432
+ query = query.where(AgentModel.name == name)
433
+ # Apply a case-insensitive partial match for the agent's name.
434
+ if query_text:
435
+ query = query.where(AgentModel.name.ilike(f"%{query_text}%"))
436
+ # Filter agents by project ID.
437
+ if project_id:
438
+ query = query.where(AgentModel.project_id == project_id)
439
+ # Filter agents by template ID.
440
+ if template_id:
441
+ query = query.where(AgentModel.template_id == template_id)
442
+ # Filter agents by base template ID.
443
+ if base_template_id:
444
+ query = query.where(AgentModel.base_template_id == base_template_id)
445
+ return query
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from typing import List, Optional
3
3
 
4
- from sqlalchemy import and_, or_
4
+ from sqlalchemy import and_, exists, func, or_, select, text
5
5
 
6
6
  from letta.log import get_logger
7
7
  from letta.orm.agent import Agent as AgentModel
@@ -233,9 +233,17 @@ class MessageManager:
233
233
  # Build a query that directly filters the Message table by agent_id.
234
234
  query = session.query(MessageModel).filter(MessageModel.agent_id == agent_id)
235
235
 
236
- # If query_text is provided, filter messages by partial match on text.
236
+ # If query_text is provided, filter messages using subquery.
237
237
  if query_text:
238
- query = query.filter(MessageModel.text.ilike(f"%{query_text}%"))
238
+ content_element = func.json_array_elements(MessageModel.content).alias("content_element")
239
+ query = query.filter(
240
+ exists(
241
+ select(1)
242
+ .select_from(content_element)
243
+ .where(text("content_element->>'type' = 'text' AND content_element->>'text' ILIKE :query_text"))
244
+ .params(query_text=f"%{query_text}%")
245
+ )
246
+ )
239
247
 
240
248
  # If role is provided, filter messages by role.
241
249
  if role:
@@ -203,3 +203,18 @@ class PassageManager:
203
203
  for passage in passages:
204
204
  self.delete_passage_by_id(passage_id=passage.id, actor=actor)
205
205
  return True
206
+
207
+ @enforce_types
208
+ def size(
209
+ self,
210
+ actor: PydanticUser,
211
+ agent_id: Optional[str] = None,
212
+ ) -> int:
213
+ """Get the total count of messages with optional filters.
214
+
215
+ Args:
216
+ actor: The user requesting the count
217
+ agent_id: The agent ID of the messages
218
+ """
219
+ with self.session_maker() as session:
220
+ return AgentPassage.size(db_session=session, actor=actor, agent_id=agent_id)
letta/settings.py CHANGED
@@ -174,6 +174,11 @@ class Settings(BaseSettings):
174
174
  # telemetry logging
175
175
  verbose_telemetry_logging: bool = False
176
176
 
177
+ # uvicorn settings
178
+ uvicorn_workers: int = 1
179
+ uvicorn_reload: bool = False
180
+ uvicorn_timeout_keep_alive: int = 5
181
+
177
182
  @property
178
183
  def letta_pg_uri(self) -> str:
179
184
  if self.pg_uri:
@@ -0,0 +1,103 @@
1
+ from typing import List, Optional
2
+
3
+ from letta.agent import Agent, AgentState
4
+ from letta.constants import DEFAULT_MESSAGE_TOOL
5
+ from letta.functions.function_sets.multi_agent import send_message_to_all_agents_in_group
6
+ from letta.interface import AgentInterface
7
+ from letta.orm import User
8
+ from letta.orm.enums import ToolType
9
+ from letta.schemas.letta_message_content import TextContent
10
+ from letta.schemas.message import Message, MessageCreate
11
+ from letta.schemas.tool_rule import ChildToolRule, InitToolRule, TerminalToolRule
12
+ from letta.schemas.usage import LettaUsageStatistics
13
+ from letta.services.agent_manager import AgentManager
14
+ from letta.services.tool_manager import ToolManager
15
+ from tests.helpers.utils import create_tool_from_func
16
+
17
+
18
+ class SupervisorMultiAgent(Agent):
19
+ def __init__(
20
+ self,
21
+ interface: AgentInterface,
22
+ agent_state: AgentState,
23
+ user: User = None,
24
+ # custom
25
+ group_id: str = "",
26
+ agent_ids: List[str] = [],
27
+ description: str = "",
28
+ ):
29
+ super().__init__(interface, agent_state, user)
30
+ self.group_id = group_id
31
+ self.agent_ids = agent_ids
32
+ self.description = description
33
+ self.agent_manager = AgentManager()
34
+ self.tool_manager = ToolManager()
35
+
36
+ def step(
37
+ self,
38
+ messages: List[MessageCreate],
39
+ chaining: bool = True,
40
+ max_chaining_steps: Optional[int] = None,
41
+ put_inner_thoughts_first: bool = True,
42
+ assistant_message_tool_name: str = DEFAULT_MESSAGE_TOOL,
43
+ **kwargs,
44
+ ) -> LettaUsageStatistics:
45
+ token_streaming = self.interface.streaming_mode if hasattr(self.interface, "streaming_mode") else False
46
+ metadata = self.interface.metadata if hasattr(self.interface, "metadata") else None
47
+
48
+ # add multi agent tool
49
+ if self.tool_manager.get_tool_by_name(tool_name="send_message_to_all_agents_in_group", actor=self.user) is None:
50
+ multi_agent_tool = create_tool_from_func(send_message_to_all_agents_in_group)
51
+ multi_agent_tool.tool_type = ToolType.LETTA_MULTI_AGENT_CORE
52
+ multi_agent_tool = self.tool_manager.create_or_update_tool(
53
+ pydantic_tool=multi_agent_tool,
54
+ actor=self.user,
55
+ )
56
+ self.agent_state = self.agent_manager.attach_tool(agent_id=self.agent_state.id, tool_id=multi_agent_tool.id, actor=self.user)
57
+
58
+ # override tool rules
59
+ self.agent_state.tool_rules = [
60
+ InitToolRule(
61
+ tool_name="send_message_to_all_agents_in_group",
62
+ ),
63
+ TerminalToolRule(
64
+ tool_name=assistant_message_tool_name,
65
+ ),
66
+ ChildToolRule(
67
+ tool_name="send_message_to_all_agents_in_group",
68
+ children=[assistant_message_tool_name],
69
+ ),
70
+ ]
71
+
72
+ supervisor_messages = [
73
+ Message(
74
+ agent_id=self.agent_state.id,
75
+ role="user",
76
+ content=[TextContent(text=message.content)],
77
+ name=None,
78
+ model=None,
79
+ tool_calls=None,
80
+ tool_call_id=None,
81
+ group_id=self.group_id,
82
+ )
83
+ for message in messages
84
+ ]
85
+ try:
86
+ supervisor_agent = Agent(agent_state=self.agent_state, interface=self.interface, user=self.user)
87
+ usage_stats = supervisor_agent.step(
88
+ messages=supervisor_messages,
89
+ chaining=chaining,
90
+ max_chaining_steps=max_chaining_steps,
91
+ stream=token_streaming,
92
+ skip_verify=True,
93
+ metadata=metadata,
94
+ put_inner_thoughts_first=put_inner_thoughts_first,
95
+ )
96
+ except Exception as e:
97
+ raise e
98
+ finally:
99
+ self.interface.step_yield()
100
+
101
+ self.interface.step_complete()
102
+
103
+ return usage_stats
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.6.39.dev20250314104053
3
+ Version: 0.6.40.dev20250314222759
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -32,7 +32,6 @@ Requires-Dist: colorama (>=0.4.6,<0.5.0)
32
32
  Requires-Dist: composio-core (>=0.7.7,<0.8.0)
33
33
  Requires-Dist: composio-langchain (>=0.7.7,<0.8.0)
34
34
  Requires-Dist: datamodel-code-generator[http] (>=0.25.0,<0.26.0) ; extra == "desktop" or extra == "all"
35
- Requires-Dist: datasets (>=2.14.6,<3.0.0)
36
35
  Requires-Dist: demjson3 (>=3.0.6,<4.0.0)
37
36
  Requires-Dist: docker (>=7.1.0,<8.0.0) ; extra == "external-tools" or extra == "desktop" or extra == "all"
38
37
  Requires-Dist: docstring-parser (>=0.16,<0.17)