letta-nightly 0.6.20.dev20250204104033__py3-none-any.whl → 0.6.21.dev20250205051348__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

letta/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "0.6.20"
1
+ __version__ = "0.6.21"
2
2
 
3
3
 
4
4
  # import clients
letta/agent.py CHANGED
@@ -38,6 +38,7 @@ from letta.schemas.message import Message
38
38
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse
39
39
  from letta.schemas.openai.chat_completion_response import Message as ChatCompletionMessage
40
40
  from letta.schemas.openai.chat_completion_response import UsageStatistics
41
+ from letta.schemas.sandbox_config import SandboxRunResult
41
42
  from letta.schemas.tool import Tool
42
43
  from letta.schemas.tool_rule import TerminalToolRule
43
44
  from letta.schemas.usage import LettaUsageStatistics
@@ -198,7 +199,9 @@ class Agent(BaseAgent):
198
199
  return True
199
200
  return False
200
201
 
201
- def execute_tool_and_persist_state(self, function_name: str, function_args: dict, target_letta_tool: Tool):
202
+ def execute_tool_and_persist_state(
203
+ self, function_name: str, function_args: dict, target_letta_tool: Tool
204
+ ) -> tuple[str, Optional[SandboxRunResult]]:
202
205
  """
203
206
  Execute tool modifications and persist the state of the agent.
204
207
  Note: only some agent state modifications will be persisted, such as data in the AgentState ORM and block data
@@ -242,6 +245,7 @@ class Agent(BaseAgent):
242
245
  assert orig_memory_str == self.agent_state.memory.compile(), "Memory should not be modified in a sandbox tool"
243
246
  if updated_agent_state is not None:
244
247
  self.update_memory_if_changed(updated_agent_state.memory)
248
+ return function_response, sandbox_run_result
245
249
  except Exception as e:
246
250
  # Need to catch error here, or else trunction wont happen
247
251
  # TODO: modify to function execution error
@@ -249,7 +253,44 @@ class Agent(BaseAgent):
249
253
  function_name=function_name, exception_name=type(e).__name__, exception_message=str(e)
250
254
  )
251
255
 
252
- return function_response
256
+ return function_response, None
257
+
258
+ def _handle_function_error_response(
259
+ self,
260
+ error_msg: str,
261
+ tool_call_id: str,
262
+ function_name: str,
263
+ function_response: str,
264
+ messages: List[Message],
265
+ include_function_failed_message: bool = False,
266
+ ) -> List[Message]:
267
+ """
268
+ Handle error from function call response
269
+ """
270
+ # Update tool rules
271
+ self.last_function_response = function_response
272
+ self.tool_rules_solver.update_tool_usage(function_name)
273
+
274
+ # Extend conversation with function response
275
+ function_response = package_function_response(False, error_msg)
276
+ new_message = Message.dict_to_message(
277
+ agent_id=self.agent_state.id,
278
+ user_id=self.agent_state.created_by_id,
279
+ model=self.model,
280
+ openai_message_dict={
281
+ "role": "tool",
282
+ "name": function_name,
283
+ "content": function_response,
284
+ "tool_call_id": tool_call_id,
285
+ },
286
+ )
287
+ messages.append(new_message)
288
+ self.interface.function_message(f"Error: {error_msg}", msg_obj=new_message)
289
+ if include_function_failed_message:
290
+ self.interface.function_message(f"Ran {function_name}({function_args})", msg_obj=new_message)
291
+
292
+ # Return updated messages
293
+ return messages
253
294
 
254
295
  def _get_ai_reply(
255
296
  self,
@@ -261,6 +302,7 @@ class Agent(BaseAgent):
261
302
  backoff_factor: float = 0.5, # delay multiplier for exponential backoff
262
303
  max_delay: float = 10.0, # max delay between retries
263
304
  step_count: Optional[int] = None,
305
+ last_function_failed: bool = False,
264
306
  ) -> ChatCompletionResponse:
265
307
  """Get response from LLM API with robust retry mechanism."""
266
308
 
@@ -273,6 +315,12 @@ class Agent(BaseAgent):
273
315
  else [func for func in agent_state_tool_jsons if func["name"] in allowed_tool_names]
274
316
  )
275
317
 
318
+ # Don't allow a tool to be called if it failed last time
319
+ if last_function_failed and self.tool_rules_solver.last_tool_name:
320
+ allowed_functions = [f for f in allowed_functions if f["name"] != self.tool_rules_solver.last_tool_name]
321
+ if not allowed_functions:
322
+ return None
323
+
276
324
  # For the first message, force the initial tool if one is specified
277
325
  force_tool_call = None
278
326
  if (
@@ -285,6 +333,7 @@ class Agent(BaseAgent):
285
333
  # Force a tool call if exactly one tool is specified
286
334
  elif step_count is not None and step_count > 0 and len(allowed_tool_names) == 1:
287
335
  force_tool_call = allowed_tool_names[0]
336
+
288
337
  for attempt in range(1, empty_response_retry_limit + 1):
289
338
  try:
290
339
  response = create(
@@ -409,21 +458,8 @@ class Agent(BaseAgent):
409
458
 
410
459
  if not target_letta_tool:
411
460
  error_msg = f"No function named {function_name}"
412
- function_response = package_function_response(False, error_msg)
413
- messages.append(
414
- Message.dict_to_message(
415
- agent_id=self.agent_state.id,
416
- user_id=self.agent_state.created_by_id,
417
- model=self.model,
418
- openai_message_dict={
419
- "role": "tool",
420
- "name": function_name,
421
- "content": function_response,
422
- "tool_call_id": tool_call_id,
423
- },
424
- )
425
- ) # extend conversation with function response
426
- self.interface.function_message(f"Error: {error_msg}", msg_obj=messages[-1])
461
+ function_response = "None" # more like "never ran?"
462
+ messages = self._handle_function_error_response(error_msg, tool_call_id, function_name, function_response, messages)
427
463
  return messages, False, True # force a heartbeat to allow agent to handle error
428
464
 
429
465
  # Failure case 2: function name is OK, but function args are bad JSON
@@ -432,21 +468,8 @@ class Agent(BaseAgent):
432
468
  function_args = parse_json(raw_function_args)
433
469
  except Exception:
434
470
  error_msg = f"Error parsing JSON for function '{function_name}' arguments: {function_call.arguments}"
435
- function_response = package_function_response(False, error_msg)
436
- messages.append(
437
- Message.dict_to_message(
438
- agent_id=self.agent_state.id,
439
- user_id=self.agent_state.created_by_id,
440
- model=self.model,
441
- openai_message_dict={
442
- "role": "tool",
443
- "name": function_name,
444
- "content": function_response,
445
- "tool_call_id": tool_call_id,
446
- },
447
- )
448
- ) # extend conversation with function response
449
- self.interface.function_message(f"Error: {error_msg}", msg_obj=messages[-1])
471
+ function_response = "None" # more like "never ran?"
472
+ messages = self._handle_function_error_response(error_msg, tool_call_id, function_name, function_response, messages)
450
473
  return messages, False, True # force a heartbeat to allow agent to handle error
451
474
 
452
475
  # Check if inner thoughts is in the function call arguments (possible apparently if you are using Azure)
@@ -479,7 +502,12 @@ class Agent(BaseAgent):
479
502
  self.interface.function_message(f"Running {function_name}({function_args})", msg_obj=messages[-1])
480
503
  try:
481
504
  # handle tool execution (sandbox) and state updates
482
- function_response = self.execute_tool_and_persist_state(function_name, function_args, target_letta_tool)
505
+ function_response, sandbox_run_result = self.execute_tool_and_persist_state(function_name, function_args, target_letta_tool)
506
+
507
+ if sandbox_run_result and sandbox_run_result.status == "error":
508
+ error_msg = f"Error calling function {function_name} with args {function_args}: {sandbox_run_result.stderr}"
509
+ messages = self._handle_function_error_response(error_msg, tool_call_id, function_name, function_response, messages)
510
+ return messages, False, True # force a heartbeat to allow agent to handle error
483
511
 
484
512
  # handle trunction
485
513
  if function_name in ["conversation_search", "conversation_search_date", "archival_memory_search"]:
@@ -505,45 +533,17 @@ class Agent(BaseAgent):
505
533
  error_msg = get_friendly_error_msg(function_name=function_name, exception_name=type(e).__name__, exception_message=str(e))
506
534
  error_msg_user = f"{error_msg}\n{traceback.format_exc()}"
507
535
  self.logger.error(error_msg_user)
508
- function_response = package_function_response(False, error_msg)
509
- self.last_function_response = function_response
510
- # TODO: truncate error message somehow
511
- messages.append(
512
- Message.dict_to_message(
513
- agent_id=self.agent_state.id,
514
- user_id=self.agent_state.created_by_id,
515
- model=self.model,
516
- openai_message_dict={
517
- "role": "tool",
518
- "name": function_name,
519
- "content": function_response,
520
- "tool_call_id": tool_call_id,
521
- },
522
- )
523
- ) # extend conversation with function response
524
- self.interface.function_message(f"Ran {function_name}({function_args})", msg_obj=messages[-1])
525
- self.interface.function_message(f"Error: {error_msg}", msg_obj=messages[-1])
536
+ messages = self._handle_function_error_response(
537
+ error_msg, tool_call_id, function_name, function_response, messages, include_function_failed_message=True
538
+ )
526
539
  return messages, False, True # force a heartbeat to allow agent to handle error
527
540
 
528
541
  # Step 4: check if function response is an error
529
542
  if function_response_string.startswith(ERROR_MESSAGE_PREFIX):
530
- function_response = package_function_response(False, function_response_string)
531
- # TODO: truncate error message somehow
532
- messages.append(
533
- Message.dict_to_message(
534
- agent_id=self.agent_state.id,
535
- user_id=self.agent_state.created_by_id,
536
- model=self.model,
537
- openai_message_dict={
538
- "role": "tool",
539
- "name": function_name,
540
- "content": function_response,
541
- "tool_call_id": tool_call_id,
542
- },
543
- )
544
- ) # extend conversation with function response
545
- self.interface.function_message(f"Ran {function_name}({function_args})", msg_obj=messages[-1])
546
- self.interface.function_message(f"Error: {function_response_string}", msg_obj=messages[-1])
543
+ error_msg = function_response_string
544
+ messages = self._handle_function_error_response(
545
+ error_msg, tool_call_id, function_name, function_response, messages, include_function_failed_message=True
546
+ )
547
547
  return messages, False, True # force a heartbeat to allow agent to handle error
548
548
 
549
549
  # If no failures happened along the way: ...
@@ -607,9 +607,11 @@ class Agent(BaseAgent):
607
607
  counter = 0
608
608
  total_usage = UsageStatistics()
609
609
  step_count = 0
610
+ function_failed = False
610
611
  while True:
611
612
  kwargs["first_message"] = False
612
613
  kwargs["step_count"] = step_count
614
+ kwargs["last_function_failed"] = function_failed
613
615
  step_response = self.inner_step(
614
616
  messages=next_input_message,
615
617
  **kwargs,
@@ -689,6 +691,7 @@ class Agent(BaseAgent):
689
691
  step_count: Optional[int] = None,
690
692
  metadata: Optional[dict] = None,
691
693
  summarize_attempt_count: int = 0,
694
+ last_function_failed: bool = False,
692
695
  ) -> AgentStepResponse:
693
696
  """Runs a single step in the agent loop (generates at most one LLM call)"""
694
697
 
@@ -723,7 +726,17 @@ class Agent(BaseAgent):
723
726
  first_message=first_message,
724
727
  stream=stream,
725
728
  step_count=step_count,
729
+ last_function_failed=last_function_failed,
726
730
  )
731
+ if not response:
732
+ # EDGE CASE: Function call failed AND there's no tools left for agent to call -> return early
733
+ return AgentStepResponse(
734
+ messages=input_message_sequence,
735
+ heartbeat_request=False,
736
+ function_failed=False, # NOTE: this is different from other function fails. We force to return early
737
+ in_context_memory_warning=False,
738
+ usage=UsageStatistics(),
739
+ )
727
740
 
728
741
  # Step 3: check if LLM wanted to call a function
729
742
  # (if yes) Step 4: call the function
letta/client/client.py CHANGED
@@ -2950,18 +2950,11 @@ class LocalClient(AbstractClient):
2950
2950
  langchain_tool=langchain_tool,
2951
2951
  additional_imports_module_attr_map=additional_imports_module_attr_map,
2952
2952
  )
2953
- return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2954
-
2955
- def load_crewai_tool(self, crewai_tool: "CrewAIBaseTool", additional_imports_module_attr_map: dict[str, str] = None) -> Tool:
2956
- tool_create = ToolCreate.from_crewai(
2957
- crewai_tool=crewai_tool,
2958
- additional_imports_module_attr_map=additional_imports_module_attr_map,
2959
- )
2960
- return self.server.tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2953
+ return self.server.tool_manager.create_or_update_langchain_tool(tool_create=tool_create, actor=self.user)
2961
2954
 
2962
2955
  def load_composio_tool(self, action: "ActionType") -> Tool:
2963
2956
  tool_create = ToolCreate.from_composio(action_name=action.name)
2964
- return self.server.tool_manager.create_or_update_composio_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=self.user)
2957
+ return self.server.tool_manager.create_or_update_composio_tool(tool_create=tool_create, actor=self.user)
2965
2958
 
2966
2959
  def create_tool(
2967
2960
  self,
@@ -230,9 +230,7 @@ def generate_imported_tool_instantiation_call_str(obj: Any) -> Optional[str]:
230
230
 
231
231
 
232
232
  def is_base_model(obj: Any):
233
- from langchain_core.pydantic_v1 import BaseModel as LangChainBaseModel
234
-
235
- return isinstance(obj, BaseModel) or isinstance(obj, LangChainBaseModel)
233
+ return isinstance(obj, BaseModel)
236
234
 
237
235
 
238
236
  def generate_import_code(module_attr_map: Optional[dict]):
letta/local_llm/utils.py CHANGED
@@ -11,8 +11,11 @@ import letta.local_llm.llm_chat_completion_wrappers.configurable_wrapper as conf
11
11
  import letta.local_llm.llm_chat_completion_wrappers.dolphin as dolphin
12
12
  import letta.local_llm.llm_chat_completion_wrappers.llama3 as llama3
13
13
  import letta.local_llm.llm_chat_completion_wrappers.zephyr as zephyr
14
+ from letta.log import get_logger
14
15
  from letta.schemas.openai.chat_completion_request import Tool, ToolCall
15
16
 
17
+ logger = get_logger(__name__)
18
+
16
19
 
17
20
  def post_json_auth_request(uri, json_payload, auth_type, auth_key):
18
21
  """Send a POST request with a JSON payload and optional authentication"""
@@ -126,8 +129,11 @@ def num_tokens_from_functions(functions: List[dict], model: str = "gpt-4"):
126
129
  function_tokens += 2
127
130
  if isinstance(v["items"], dict) and "type" in v["items"]:
128
131
  function_tokens += len(encoding.encode(v["items"]["type"]))
132
+ elif field == "default":
133
+ function_tokens += 2
134
+ function_tokens += len(encoding.encode(str(v["default"])))
129
135
  else:
130
- warnings.warn(f"num_tokens_from_functions: Unsupported field {field} in function {function}")
136
+ logger.warning(f"num_tokens_from_functions: Unsupported field {field} in function {function}")
131
137
  function_tokens += 11
132
138
 
133
139
  num_tokens += function_tokens
letta/orm/agent.py CHANGED
@@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, List, Optional
4
4
  from sqlalchemy import JSON, Index, String
5
5
  from sqlalchemy.orm import Mapped, mapped_column, relationship
6
6
 
7
- from letta.constants import MULTI_AGENT_TOOLS
8
7
  from letta.orm.block import Block
9
8
  from letta.orm.custom_columns import EmbeddingConfigColumn, LLMConfigColumn, ToolRulesColumn
10
9
  from letta.orm.message import Message
@@ -121,12 +120,7 @@ class Agent(SqlalchemyBase, OrganizationMixin):
121
120
  # add default rule for having send_message be a terminal tool
122
121
  tool_rules = self.tool_rules
123
122
  if not tool_rules:
124
- tool_rules = [
125
- TerminalToolRule(tool_name="send_message"),
126
- ]
127
-
128
- for tool_name in MULTI_AGENT_TOOLS:
129
- tool_rules.append(TerminalToolRule(tool_name=tool_name))
123
+ tool_rules = [TerminalToolRule(tool_name="send_message"), TerminalToolRule(tool_name="send_message_to_agent_async")]
130
124
 
131
125
  state = {
132
126
  "id": self.id,
letta/orm/enums.py CHANGED
@@ -7,6 +7,7 @@ class ToolType(str, Enum):
7
7
  LETTA_MEMORY_CORE = "letta_memory_core"
8
8
  LETTA_MULTI_AGENT_CORE = "letta_multi_agent_core"
9
9
  EXTERNAL_COMPOSIO = "external_composio"
10
+ EXTERNAL_LANGCHAIN = "external_langchain"
10
11
 
11
12
 
12
13
  class JobType(str, Enum):
letta/schemas/agent.py CHANGED
@@ -143,6 +143,9 @@ class CreateAgent(BaseModel, validate_assignment=True): #
143
143
  None, description="The environment variables for tool execution specific to this agent."
144
144
  )
145
145
  memory_variables: Optional[Dict[str, str]] = Field(None, description="The variables that should be set for the agent.")
146
+ project_id: Optional[str] = Field(None, description="The id of the project the agent belongs to.")
147
+ template_id: Optional[str] = Field(None, description="The id of the template the agent belongs to.")
148
+ base_template_id: Optional[str] = Field(None, description="The base template id of the agent.")
146
149
 
147
150
  @field_validator("name")
148
151
  @classmethod
@@ -210,6 +213,9 @@ class UpdateAgent(BaseModel):
210
213
  tool_exec_environment_variables: Optional[Dict[str, str]] = Field(
211
214
  None, description="The environment variables for tool execution specific to this agent."
212
215
  )
216
+ project_id: Optional[str] = Field(None, description="The id of the project the agent belongs to.")
217
+ template_id: Optional[str] = Field(None, description="The id of the template the agent belongs to.")
218
+ base_template_id: Optional[str] = Field(None, description="The base template id of the agent.")
213
219
 
214
220
  class Config:
215
221
  extra = "ignore" # Ignores extra fields
@@ -279,7 +279,7 @@ class LMStudioOpenAIProvider(OpenAIProvider):
279
279
  from letta.llm_api.openai import openai_get_model_list
280
280
 
281
281
  # For LMStudio, we want to hit 'GET /api/v0/models' instead of 'GET /v1/models'
282
- MODEL_ENDPOINT_URL = f"{self.base_url}/api/v0"
282
+ MODEL_ENDPOINT_URL = f"{self.base_url.strip('/v1')}/api/v0"
283
283
  response = openai_get_model_list(MODEL_ENDPOINT_URL)
284
284
 
285
285
  """
letta/schemas/tool.py CHANGED
@@ -79,7 +79,7 @@ class Tool(BaseTool):
79
79
  self.json_schema = get_json_schema_from_module(module_name=LETTA_MULTI_AGENT_TOOL_MODULE_NAME, function_name=self.name)
80
80
  elif self.tool_type == ToolType.EXTERNAL_COMPOSIO:
81
81
  # If it is a composio tool, we generate both the source code and json schema on the fly here
82
- # TODO: This is brittle, need to think long term about how to improve this
82
+ # TODO: Deriving the composio action name is brittle, need to think long term about how to improve this
83
83
  try:
84
84
  composio_action = generate_composio_action_from_func_name(self.name)
85
85
  tool_create = ToolCreate.from_composio(composio_action)
@@ -47,6 +47,9 @@ def list_agents(
47
47
  after: Optional[str] = Query(None, description="Cursor for pagination"),
48
48
  limit: Optional[int] = Query(None, description="Limit for pagination"),
49
49
  query_text: Optional[str] = Query(None, description="Search agents by name"),
50
+ project_id: Optional[str] = Query(None, description="Search agents by project id"),
51
+ template_id: Optional[str] = Query(None, description="Search agents by template id"),
52
+ base_template_id: Optional[str] = Query(None, description="Search agents by base template id"),
50
53
  ):
51
54
  """
52
55
  List all agents associated with a given user.
@@ -58,16 +61,25 @@ def list_agents(
58
61
  kwargs = {
59
62
  key: value
60
63
  for key, value in {
61
- "tags": tags,
62
- "match_all_tags": match_all_tags,
63
64
  "name": name,
64
- "query_text": query_text,
65
+ "project_id": project_id,
66
+ "template_id": template_id,
67
+ "base_template_id": base_template_id,
65
68
  }.items()
66
69
  if value is not None
67
70
  }
68
71
 
69
72
  # Call list_agents with the dynamic kwargs
70
- agents = server.agent_manager.list_agents(actor=actor, before=before, after=after, limit=limit, **kwargs)
73
+ agents = server.agent_manager.list_agents(
74
+ actor=actor,
75
+ before=before,
76
+ after=after,
77
+ limit=limit,
78
+ query_text=query_text,
79
+ tags=tags,
80
+ match_all_tags=match_all_tags,
81
+ **kwargs,
82
+ )
71
83
  return agents
72
84
 
73
85
 
@@ -66,7 +66,7 @@ def list_tools(
66
66
  try:
67
67
  actor = server.user_manager.get_user_or_default(user_id=user_id)
68
68
  if name is not None:
69
- tool = server.tool_manager.get_tool_by_name(name=name, actor=actor)
69
+ tool = server.tool_manager.get_tool_by_name(tool_name=name, actor=actor)
70
70
  return [tool] if tool else []
71
71
  return server.tool_manager.list_tools(actor=actor, after=after, limit=limit)
72
72
  except Exception as e:
@@ -231,7 +231,7 @@ def add_composio_tool(
231
231
 
232
232
  try:
233
233
  tool_create = ToolCreate.from_composio(action_name=composio_action_name)
234
- return server.tool_manager.create_or_update_composio_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=actor)
234
+ return server.tool_manager.create_or_update_composio_tool(tool_create=tool_create, actor=actor)
235
235
  except EnumStringNotFound as e:
236
236
  raise HTTPException(
237
237
  status_code=400, # Bad Request
letta/server/server.py CHANGED
@@ -189,7 +189,7 @@ def db_error_handler():
189
189
 
190
190
 
191
191
  if settings.letta_pg_uri_no_default:
192
- print("Creating postgres engine", settings.letta_pg_uri)
192
+ print("Creating postgres engine")
193
193
  config.recall_storage_type = "postgres"
194
194
  config.recall_storage_uri = settings.letta_pg_uri_no_default
195
195
  config.archival_storage_type = "postgres"
@@ -1000,8 +1000,8 @@ class SyncServer(Server):
1000
1000
  return passage_count, document_count
1001
1001
 
1002
1002
  def list_data_source_passages(self, user_id: str, source_id: str) -> List[Passage]:
1003
- warnings.warn("list_data_source_passages is not yet implemented, returning empty list.", category=UserWarning)
1004
- return []
1003
+ # TODO: move this query into PassageManager
1004
+ return self.agent_manager.list_passages(actor=self.user_manager.get_user_or_default(user_id=user_id), source_id=source_id)
1005
1005
 
1006
1006
  def list_all_sources(self, actor: User) -> List[Source]:
1007
1007
  """List all sources (w/ extra metadata) belonging to a user"""
letta/server/startup.sh CHANGED
@@ -14,7 +14,7 @@ wait_for_postgres() {
14
14
 
15
15
  # Check if we're configured for external Postgres
16
16
  if [ -n "$LETTA_PG_URI" ]; then
17
- echo "External Postgres configuration detected, using $LETTA_PG_URI"
17
+ echo "External Postgres configuration detected, using env var LETTA_PG_URI"
18
18
  else
19
19
  echo "No external Postgres configuration detected, starting internal PostgreSQL..."
20
20
  # Start PostgreSQL using the base image's entrypoint script
@@ -120,6 +120,9 @@ class AgentManager:
120
120
  metadata=agent_create.metadata,
121
121
  tool_rules=agent_create.tool_rules,
122
122
  actor=actor,
123
+ project_id=agent_create.project_id,
124
+ template_id=agent_create.template_id,
125
+ base_template_id=agent_create.base_template_id,
123
126
  )
124
127
 
125
128
  # If there are provided environment variables, add them in
@@ -179,6 +182,9 @@ class AgentManager:
179
182
  description: Optional[str] = None,
180
183
  metadata: Optional[Dict] = None,
181
184
  tool_rules: Optional[List[PydanticToolRule]] = None,
185
+ project_id: Optional[str] = None,
186
+ template_id: Optional[str] = None,
187
+ base_template_id: Optional[str] = None,
182
188
  ) -> PydanticAgentState:
183
189
  """Create a new agent."""
184
190
  with self.session_maker() as session:
@@ -193,6 +199,9 @@ class AgentManager:
193
199
  "description": description,
194
200
  "metadata_": metadata,
195
201
  "tool_rules": tool_rules,
202
+ "project_id": project_id,
203
+ "template_id": template_id,
204
+ "base_template_id": base_template_id,
196
205
  }
197
206
 
198
207
  # Create the new agent using SqlalchemyBase.create
@@ -242,7 +251,19 @@ class AgentManager:
242
251
  agent = AgentModel.read(db_session=session, identifier=agent_id, actor=actor)
243
252
 
244
253
  # Update scalar fields directly
245
- scalar_fields = {"name", "system", "llm_config", "embedding_config", "message_ids", "tool_rules", "description", "metadata"}
254
+ scalar_fields = {
255
+ "name",
256
+ "system",
257
+ "llm_config",
258
+ "embedding_config",
259
+ "message_ids",
260
+ "tool_rules",
261
+ "description",
262
+ "metadata",
263
+ "project_id",
264
+ "template_id",
265
+ "base_template_id",
266
+ }
246
267
  for field in scalar_fields:
247
268
  value = getattr(agent_update, field, None)
248
269
  if value is not None:
@@ -1,6 +1,8 @@
1
1
  from datetime import datetime
2
2
  from typing import List, Optional
3
3
 
4
+ from openai import OpenAI
5
+
4
6
  from letta.embeddings import embedding_model, parse_and_chunk_text
5
7
  from letta.orm.errors import NoResultFound
6
8
  from letta.orm.passage import AgentPassage, SourcePassage
@@ -86,14 +88,31 @@ class PassageManager:
86
88
  """Insert passage(s) into archival memory"""
87
89
 
88
90
  embedding_chunk_size = agent_state.embedding_config.embedding_chunk_size
89
- embed_model = embedding_model(agent_state.embedding_config)
91
+
92
+ # TODO eventually migrate off of llama-index for embeddings?
93
+ # Already causing pain for OpenAI proxy endpoints like LM Studio...
94
+ if agent_state.embedding_config.embedding_endpoint_type != "openai":
95
+ embed_model = embedding_model(agent_state.embedding_config)
90
96
 
91
97
  passages = []
92
98
 
93
99
  try:
94
100
  # breakup string into passages
95
101
  for text in parse_and_chunk_text(text, embedding_chunk_size):
96
- embedding = embed_model.get_text_embedding(text)
102
+
103
+ if agent_state.embedding_config.embedding_endpoint_type != "openai":
104
+ embedding = embed_model.get_text_embedding(text)
105
+ else:
106
+ # TODO should have the settings passed in via the server call
107
+ from letta.settings import model_settings
108
+
109
+ # Simple OpenAI client code
110
+ client = OpenAI(
111
+ api_key=model_settings.openai_api_key, base_url=agent_state.embedding_config.embedding_endpoint, max_retries=0
112
+ )
113
+ response = client.embeddings.create(input=text, model=agent_state.embedding_config.embedding_model)
114
+ embedding = response.data[0].embedding
115
+
97
116
  if isinstance(embedding, dict):
98
117
  try:
99
118
  embedding = embedding["data"][0]["embedding"]
@@ -1,4 +1,4 @@
1
- import datetime
1
+ from datetime import datetime
2
2
  from typing import List, Literal, Optional
3
3
 
4
4
  from sqlalchemy import select
@@ -11,7 +11,7 @@ from letta.orm.enums import ToolType
11
11
  from letta.orm.errors import NoResultFound
12
12
  from letta.orm.tool import Tool as ToolModel
13
13
  from letta.schemas.tool import Tool as PydanticTool
14
- from letta.schemas.tool import ToolUpdate
14
+ from letta.schemas.tool import ToolCreate, ToolUpdate
15
15
  from letta.schemas.user import User as PydanticUser
16
16
  from letta.utils import enforce_types, printd
17
17
 
@@ -57,9 +57,16 @@ class ToolManager:
57
57
  return tool
58
58
 
59
59
  @enforce_types
60
- def create_or_update_composio_tool(self, pydantic_tool: PydanticTool, actor: PydanticUser) -> PydanticTool:
61
- pydantic_tool.tool_type = ToolType.EXTERNAL_COMPOSIO
62
- return self.create_or_update_tool(pydantic_tool, actor)
60
+ def create_or_update_composio_tool(self, tool_create: ToolCreate, actor: PydanticUser) -> PydanticTool:
61
+ return self.create_or_update_tool(
62
+ PydanticTool(tool_type=ToolType.EXTERNAL_COMPOSIO, name=tool_create.json_schema["name"], **tool_create.model_dump()), actor
63
+ )
64
+
65
+ @enforce_types
66
+ def create_or_update_langchain_tool(self, tool_create: ToolCreate, actor: PydanticUser) -> PydanticTool:
67
+ return self.create_or_update_tool(
68
+ PydanticTool(tool_type=ToolType.EXTERNAL_LANGCHAIN, name=tool_create.json_schema["name"], **tool_create.model_dump()), actor
69
+ )
63
70
 
64
71
  @enforce_types
65
72
  def create_tool(self, pydantic_tool: PydanticTool, actor: PydanticUser) -> PydanticTool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.6.20.dev20250204104033
3
+ Version: 0.6.21.dev20250205051348
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -1,6 +1,6 @@
1
- letta/__init__.py,sha256=EApmTks3-qbEB16_xaPs03NX3OVfVYtxNA3MTpE-oRs,919
1
+ letta/__init__.py,sha256=bjhntPdDAWCs4iINZfoEzpfEiEU32NSYtCmSvdcL3gk,919
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=3-YDxRLMKPrXnmvZ1qstG2MmH9FU9cUQ0cDYZbFQ9eM,56575
3
+ letta/agent.py,sha256=Ds5-t_Ge1ZJf8wbQ2LbpK9LHEs6XNu0xLow3iXoZpd8,56980
4
4
  letta/benchmark/benchmark.py,sha256=ebvnwfp3yezaXOQyGXkYCDYpsmre-b9hvNtnyx4xkG0,3701
5
5
  letta/benchmark/constants.py,sha256=aXc5gdpMGJT327VuxsT5FngbCK2J41PQYeICBO7g_RE,536
6
6
  letta/chat_only_agent.py,sha256=71Lf-df8y3nsE9IFKpEigaZaWHoWnXnhVChkp1L-83I,4760
@@ -8,7 +8,7 @@ letta/cli/cli.py,sha256=_uGKM-RvGLGf7y8iWjkLgLTxIw7uWrdCdL5ETUOCkUs,16472
8
8
  letta/cli/cli_config.py,sha256=2oo4vui1GXQarAD6Ru4SRzPvcW4eX2mCXOBusfYGvJw,8533
9
9
  letta/cli/cli_load.py,sha256=xFw-CuzjChcIptaqQ1XpDROENt0JSjyPeiQ0nmEeO1k,2706
10
10
  letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- letta/client/client.py,sha256=-h6V_PUunYouEmw_TI0BalEvQcQ9uIOfLBnDMrDSYQQ,138785
11
+ letta/client/client.py,sha256=Vzjq3a5dx9W5R96FvKkohHf2Yf0NV4XG0VWTH2h0624,138315
12
12
  letta/client/streaming.py,sha256=DzE86XJTg_0j9eC45Hrpy9vPt-Wfo1F-sIv_B7iNV6I,5509
13
13
  letta/client/utils.py,sha256=VCGV-op5ZSmurd4yw7Vhf93XDQ0BkyBT8qsuV7EqfiU,2859
14
14
  letta/config.py,sha256=JFGY4TWW0Wm5fTbZamOwWqk5G8Nn-TXyhgByGoAqy2c,12375
@@ -23,7 +23,7 @@ letta/functions/function_sets/base.py,sha256=bOiitkhzqYKwZBiRYrx29AlordiA5IrXw25
23
23
  letta/functions/function_sets/extras.py,sha256=Z9yEdBpQFtTjpxkgbtkWMA8GtDWC6ai2bdsRpnv0H_w,4837
24
24
  letta/functions/function_sets/multi_agent.py,sha256=2tu7A_eMwkfXldZWpfwwnq0OXPFTjROo-NjXvuhxOhc,5357
25
25
  letta/functions/functions.py,sha256=NyWLh7a-f4mXti5vM1oWDwXzfA58VmVVqL03O9vosKY,5672
26
- letta/functions/helpers.py,sha256=9mNkjzxDo72Xe7Cg5tgbHUT4VyfHc4dygLUONhmVroo,19815
26
+ letta/functions/helpers.py,sha256=5f4ua21fffqU-M24ha2xNGrqIhqLWh0-sTNFOHbu18Q,19700
27
27
  letta/functions/schema_generator.py,sha256=qosgp3p27QRTqOCPLrSkCGVdyQsyTTZunXQ_g-YaTkw,20138
28
28
  letta/helpers/__init__.py,sha256=p0luQ1Oe3Skc6sH4O58aHHA3Qbkyjifpuq0DZ1GAY0U,59
29
29
  letta/helpers/tool_rule_solver.py,sha256=VnJfqb5L1Lcipc_tBVGj0om60GKQkMkNLgg6X9VZl2c,6210
@@ -73,7 +73,7 @@ letta/local_llm/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
73
73
  letta/local_llm/settings/deterministic_mirostat.py,sha256=kgRikcxYHfIbPFydHW6W7IO9jmp6NeA7JNAhnI3DPsc,1221
74
74
  letta/local_llm/settings/settings.py,sha256=QBuuOLl3OFxN6koBGxFuXSllf4EryQuJCHB5_0IttFU,2962
75
75
  letta/local_llm/settings/simple.py,sha256=HAO2jBJ_hJCEsXWIJcD0sckR0tI0zs3x2CPdf6ORQLs,719
76
- letta/local_llm/utils.py,sha256=fWLAnd-1-HhSBZ0Nf1GOq0qTKhZfN9-UFG896NwT1rU,13485
76
+ letta/local_llm/utils.py,sha256=jvJIhAH0qOXpV5dCqA2jI_3GxcaV6VT-chP7OxihEOU,13735
77
77
  letta/local_llm/vllm/api.py,sha256=2kAGZjc_GH9ILJnVRq-45yfsfKELVfbC9VEl_cIC6vg,2590
78
78
  letta/local_llm/webui/api.py,sha256=kkxncdCFq1vjgvaHOoQ__j7rcDPgC1F64KcEm94Y6Rs,2639
79
79
  letta/local_llm/webui/legacy_api.py,sha256=k3H3y4qp2Fs-XmP24iSIEyvq6wjWFWBzklY3-wRAJNI,2335
@@ -87,13 +87,13 @@ letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
87
87
  letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
88
88
  letta/orm/__all__.py,sha256=2gh2MZTkA3Hw67VWVKK3JIStJOqTeLdpCvYSVYNeEDA,692
89
89
  letta/orm/__init__.py,sha256=wPokP-EvOri2LhKLjmYVtI_FWchaxgFvJeF_NAfqJIo,842
90
- letta/orm/agent.py,sha256=ucVnPMP7S9YuWcMPxG_CWtT5QH8P2TWSU-Cp1HLuXIU,7385
90
+ letta/orm/agent.py,sha256=sFUzzFf8SPJetHP50DuiRss7YmMFh_gEqcFr6tTmBKg,7245
91
91
  letta/orm/agents_tags.py,sha256=dYSnHz4IWBjyOiQ4RJomX3P0QN76JTlEZEw5eJM6Emg,925
92
92
  letta/orm/base.py,sha256=VjvxF9TwKF9Trf8BJkDgf7D6KrWWopOkUiF18J3IElk,3071
93
93
  letta/orm/block.py,sha256=EjH8lXexHtFIHJ8G-RjNo2oAH834x0Hbn4CER9S4U74,3880
94
94
  letta/orm/blocks_agents.py,sha256=W0dykl9OchAofSuAYZD5zNmhyMabPr9LTJrz-I3A0m4,983
95
95
  letta/orm/custom_columns.py,sha256=APR3ylle9hUutQoy8m-toTV53F1mpcQhEcnf32XTmQA,5447
96
- letta/orm/enums.py,sha256=HzX3eXhBH-PnpxhBWtWbkV4J6wrStlJaX_OVdZgAdLU,428
96
+ letta/orm/enums.py,sha256=g4qbX_6a1NQpCt3rScAySbwSbkwqGmnOEaNbkMmCWF4,474
97
97
  letta/orm/errors.py,sha256=Se0Guz-gqi-D36NUWSh7AP9zTVCSph9KgZh_trwng4o,734
98
98
  letta/orm/file.py,sha256=7_p7LxityP3NQZVURQYG0kgcZhEkVuMN0Fj4h9YOe1w,1780
99
99
  letta/orm/job.py,sha256=G2P-xUpTapD4lhU2FwMZET1b5QR4ju9WOB3uiTKD_mw,2157
@@ -139,7 +139,7 @@ letta/prompts/system/memgpt_modified_o1.txt,sha256=objnDgnxpF3-MmU28ZqZ7-TOG8UlH
139
139
  letta/prompts/system/memgpt_offline_memory.txt,sha256=rWEJeF-6aiinjkJM9hgLUYCmlEcC_HekYB1bjEUYq6M,2460
140
140
  letta/prompts/system/memgpt_offline_memory_chat.txt,sha256=ituh7gDuio7nC2UKFB7GpBq6crxb8bYedQfJ0ADoPgg,3949
141
141
  letta/pytest.ini,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
- letta/schemas/agent.py,sha256=M09-BY37tqxRkQ8sh16oq7Ay_TOJWoP63Cm6aJxp-SY,12091
142
+ letta/schemas/agent.py,sha256=9Utvls2nMlwixoWaBn6gf5VlgQTnTZxhexK1j2CuSD0,12707
143
143
  letta/schemas/block.py,sha256=FYYmRWjH4d4QHMUx_nmIXjv_qJna_l-Ip_4i51wDBPA,5942
144
144
  letta/schemas/embedding_config.py,sha256=RkLbUorFkMWr1tPkn6c2aHrnICjWTbhPY86tIncwXyA,3373
145
145
  letta/schemas/embedding_config_overrides.py,sha256=lkTa4y-EQ2RnaEKtKDM0sEAk7EwNa67REw8DGNNtGQY,84
@@ -163,12 +163,12 @@ letta/schemas/openai/embedding_response.py,sha256=WKIZpXab1Av7v6sxKG8feW3ZtpQUNo
163
163
  letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwndk,7970
164
164
  letta/schemas/organization.py,sha256=WWbUWVSp_VQRFwWN4fdHg1yObiV6x9rZnvIY8x5BPs0,746
165
165
  letta/schemas/passage.py,sha256=pdCLZgOn0gWK1gB6aFHLS0gfdWCBqLaiHDA0iQ12Zd8,3704
166
- letta/schemas/providers.py,sha256=Wd0d0jgv6z3X5t7cT1ZVoX_Qa85ecsm1gQzkOPgQFUo,34890
166
+ letta/schemas/providers.py,sha256=1Sc7gWI6n9RkR4kOY4g3xGLVo6VCSwpiJySp3Pm3MQw,34903
167
167
  letta/schemas/run.py,sha256=SRqPRziINIiPunjOhE_NlbnQYgxTvqmbauni_yfBQRA,2085
168
168
  letta/schemas/sandbox_config.py,sha256=Nz8K5brqe6jpf66KnTJ0-E7ZeFdPoBFGN-XOI35OeaY,5926
169
169
  letta/schemas/source.py,sha256=-BQVolcXA2ziCu2ztR6cbTdGUc8G7vGJy7rvpdf1hpg,2880
170
170
  letta/schemas/step.py,sha256=cCmDChQMndy7aMJGH0Z19VbzJkAeFTYuA0cJpzjW2g0,1928
171
- letta/schemas/tool.py,sha256=uv3WxTt9SzaoXzwTLNHT2wegWTcaBFQBnBvNJrxeYvs,11022
171
+ letta/schemas/tool.py,sha256=dDyzvLZ_gw9DdcFplGTHoH9aFQnF2ez-ApYLRtxAfys,11051
172
172
  letta/schemas/tool_rule.py,sha256=tS7ily6NJD8E4n7Hla38jMUe6OIdhdc1ckq0AiRpu5Y,1893
173
173
  letta/schemas/usage.py,sha256=8oYRH-JX0PfjIu2zkT5Uu3UWQ7Unnz_uHiO8hRGI4m0,912
174
174
  letta/schemas/user.py,sha256=V32Tgl6oqB3KznkxUz12y7agkQicjzW7VocSpj78i6Q,1526
@@ -187,7 +187,7 @@ letta/server/rest_api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
187
187
  letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
188
188
  letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=TjeiyEMkpWR3PBazUWs4W2XVwGVEW5NRTOFXWag62oU,6657
189
189
  letta/server/rest_api/routers/v1/__init__.py,sha256=tzD8Oh6ynPkg8ULcITWcwalLL81SIh6eztPqV9l7VGk,1162
190
- letta/server/rest_api/routers/v1/agents.py,sha256=-eRRQDyWyBnP6aj6z9rJTiaidqPls1TQB1xSqGdIYGA,25041
190
+ letta/server/rest_api/routers/v1/agents.py,sha256=AkoJWxn-cJEURGXBarJtasTJsldPe6vv9TFOiGpJRlY,25473
191
191
  letta/server/rest_api/routers/v1/blocks.py,sha256=oJYOpGUTd4AhKwVolVlZPIXO2EoOrBHkyi2PdrmbtmA,3888
192
192
  letta/server/rest_api/routers/v1/health.py,sha256=pKCuVESlVOhGIb4VC4K-H82eZqfghmT6kvj2iOkkKuc,401
193
193
  letta/server/rest_api/routers/v1/jobs.py,sha256=pKihW12hQdFwt6tHQXs94yOMv6xotlhBB3Vl7Q5ASKQ,2738
@@ -199,12 +199,12 @@ letta/server/rest_api/routers/v1/sandbox_configs.py,sha256=_6WcwgKrgRfnTEC_EgoN-
199
199
  letta/server/rest_api/routers/v1/sources.py,sha256=g7NKgbZkS7y1vlukJHZ_yoWrk3AxyoWKTVGszp0Ky18,8414
200
200
  letta/server/rest_api/routers/v1/steps.py,sha256=SuZmneaeSSAHjOMatMt_QILLzkNpkbwuVgKiMYr52cE,3038
201
201
  letta/server/rest_api/routers/v1/tags.py,sha256=45G0cmcP-ER0OO5OanT_fGtGQfl9ZjRKU97mFwtwyfo,878
202
- letta/server/rest_api/routers/v1/tools.py,sha256=BS4HsHZLv2n4MauLSrVNlch2TuijzlyEfqKc3fP2b94,12633
202
+ letta/server/rest_api/routers/v1/tools.py,sha256=Ft1wnS7RJT3TOfwSGMJ0_gfTpXnVArZUtPCXT3osI-0,12615
203
203
  letta/server/rest_api/routers/v1/users.py,sha256=G5DBHSkPfBgVHN2Wkm-rVYiLQAudwQczIq2Z3YLdbVo,2277
204
204
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
205
205
  letta/server/rest_api/utils.py,sha256=dsjkZzgo9Rk3fjUf1ajjiiql1eeO5DAzmXprttI7bJU,3993
206
- letta/server/server.py,sha256=GensF4fGmRP5-3hGxm6Zr6rQea2XyX-T2dIK277Shjk,59827
207
- letta/server/startup.sh,sha256=722uKJWB2C4q3vjn39De2zzPacaZNw_1fN1SpLGjKIo,1569
206
+ letta/server/server.py,sha256=8tRXPLta26ARQSThMDnKDAxTGx39j8Zw-41kgEgSpoQ,59850
207
+ letta/server/startup.sh,sha256=qEi6dQHJRzEzDIgnIODj-RYp-O1XstfFpc6cFLkUzVs,1576
208
208
  letta/server/static_files/assets/index-048c9598.js,sha256=mR16XppvselwKCcNgONs4L7kZEVa4OEERm4lNZYtLSk,146819
209
209
  letta/server/static_files/assets/index-0e31b727.css,sha256=SBbja96uiQVLDhDOroHgM6NSl7tS4lpJRCREgSS_hA8,7672
210
210
  letta/server/static_files/favicon.ico,sha256=DezhLdFSbM8o81wCOZcV3riq7tFUOGQD4h6-vr-HuU0,342
@@ -217,29 +217,29 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
217
217
  letta/server/ws_api/protocol.py,sha256=M_-gM5iuDBwa1cuN2IGNCG5GxMJwU2d3XW93XALv9s8,1821
218
218
  letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
219
219
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
220
- letta/services/agent_manager.py,sha256=I6F-nED9Q6O_FY2-O1gHDY-OaQIxbt3FsL88DENVTfE,50287
220
+ letta/services/agent_manager.py,sha256=FLb6Y3_aZSNGTIfUrKJbRTXTMHJBlf8OGH-Ahf0moY0,50958
221
221
  letta/services/block_manager.py,sha256=u56TXG46QDMbQZadDGCO7fY1vreJ69Xr_0MUF53xw4k,5519
222
222
  letta/services/helpers/agent_manager_helper.py,sha256=RH0MXLZASkP2LVbVNUfSYHrcBYZnVxFd9ejGjRK90Hw,11283
223
223
  letta/services/helpers/tool_execution_helper.py,sha256=q8uSiQcX6VH_iNg5VNloZgC2JkH9lIOXBKCXYPx2Yac,6097
224
224
  letta/services/job_manager.py,sha256=7awEHraoEmqlDQvcQTEkb_dcZsG9eIYGWYct8exzm2E,13117
225
225
  letta/services/message_manager.py,sha256=w6-B9Zz5z9UXcd6mKhinsaCINTsmxDsH9JJsV2_qlH4,8878
226
226
  letta/services/organization_manager.py,sha256=h3hrknBhA3YQt90OeBzFnqjYM9NWKnk8jDKzXGm4AUg,3392
227
- letta/services/passage_manager.py,sha256=INxeNiFAluAkzmCL5jeWWimA3ONUZcck8BuCa0UYajk,7714
227
+ letta/services/passage_manager.py,sha256=oyTFguEa5Z1JgciKHSmQkgC3kV4Q4fV-4NgUQxfxafU,8647
228
228
  letta/services/per_agent_lock_manager.py,sha256=porM0cKKANQ1FvcGXOO_qM7ARk5Fgi1HVEAhXsAg9-4,546
229
229
  letta/services/provider_manager.py,sha256=jEal0A0XWobWH5CVfmzPtcFhsflI-sanqyg26FqpDKk,3575
230
230
  letta/services/sandbox_config_manager.py,sha256=eWDNTscRG9Gt_Ixho3-daOOno_9KcebxeA9v_CbzYu0,13371
231
231
  letta/services/source_manager.py,sha256=0JLKIv405oS5wc6bY5k2bxxZpS9O-VwUGHVdGPbJ3e4,7676
232
- letta/services/step_manager.py,sha256=Zpbz6o-KgvGePiQkgd6lHagtpYo3H906vjDE0_hpZVo,4871
232
+ letta/services/step_manager.py,sha256=dvGFeobB7TjXBENG6pGrEr6tEil3Am7GyTzv2IjQifA,4885
233
233
  letta/services/tool_execution_sandbox.py,sha256=4XBYkCEBLG6GqijxgqeLIQQJ9zRbsJa8vZ4dZG04Pq8,22080
234
- letta/services/tool_manager.py,sha256=0zc7bFxGvl_wjs7CC4cyeUWFZAUQK_yVKsUjhrW3Vao,9181
234
+ letta/services/tool_manager.py,sha256=9Y15q0GqnADk-tnUeWDFFsDOt_ZjwsPU2oteDVtHAF4,9572
235
235
  letta/services/user_manager.py,sha256=1U8BQ_-MBkEW2wnSFV_OsTwBmRAZLN8uHLFjnDjK3hA,4308
236
236
  letta/settings.py,sha256=r9KUE1YU2VfLPsdjTRvv5OfyEhV6CqXwqkeeT6NMznI,6090
237
237
  letta/streaming_interface.py,sha256=lo2VAQRUJOdWTijwnXuKOC9uejqr2siUAEmZiQUXkj8,15710
238
238
  letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,11946
239
239
  letta/system.py,sha256=S_0cod77iEttkFd1bSh2wenLCKA8YL487AuVenIDUng,8425
240
240
  letta/utils.py,sha256=lgBDWKmrQrmJGPxcgamFC2aJyi6I0dX7bzLBt3YC6j0,34051
241
- letta_nightly-0.6.20.dev20250204104033.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
242
- letta_nightly-0.6.20.dev20250204104033.dist-info/METADATA,sha256=YYWyr6N3zBnbtsnEebVXBEp1D1rDg-7gwQZ-clL2Pv4,22156
243
- letta_nightly-0.6.20.dev20250204104033.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
244
- letta_nightly-0.6.20.dev20250204104033.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
245
- letta_nightly-0.6.20.dev20250204104033.dist-info/RECORD,,
241
+ letta_nightly-0.6.21.dev20250205051348.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
242
+ letta_nightly-0.6.21.dev20250205051348.dist-info/METADATA,sha256=frAZ25QAcs_U__UpdBap5HwnrO-07Bp7zjmEiYL-HHM,22156
243
+ letta_nightly-0.6.21.dev20250205051348.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
244
+ letta_nightly-0.6.21.dev20250205051348.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
245
+ letta_nightly-0.6.21.dev20250205051348.dist-info/RECORD,,