letta-nightly 0.6.36.dev20250305104137__py3-none-any.whl → 0.6.37.dev20250306104215__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.36"
1
+ __version__ = "0.6.37"
2
2
 
3
3
  # import clients
4
4
  from letta.client.client import LocalClient, RESTClient, create_client
letta/agent.py CHANGED
@@ -39,7 +39,7 @@ from letta.schemas.block import BlockUpdate
39
39
  from letta.schemas.embedding_config import EmbeddingConfig
40
40
  from letta.schemas.enums import MessageRole
41
41
  from letta.schemas.memory import ContextWindowOverview, Memory
42
- from letta.schemas.message import Message
42
+ from letta.schemas.message import Message, ToolReturn
43
43
  from letta.schemas.openai.chat_completion_response import ChatCompletionResponse
44
44
  from letta.schemas.openai.chat_completion_response import Message as ChatCompletionMessage
45
45
  from letta.schemas.openai.chat_completion_response import UsageStatistics
@@ -277,6 +277,7 @@ class Agent(BaseAgent):
277
277
  function_args: dict,
278
278
  function_response: str,
279
279
  messages: List[Message],
280
+ tool_returns: Optional[List[ToolReturn]] = None,
280
281
  include_function_failed_message: bool = False,
281
282
  ) -> List[Message]:
282
283
  """
@@ -298,6 +299,7 @@ class Agent(BaseAgent):
298
299
  "content": function_response,
299
300
  "tool_call_id": tool_call_id,
300
301
  },
302
+ tool_returns=tool_returns,
301
303
  )
302
304
  messages.append(new_message)
303
305
  self.interface.function_message(f"Error: {error_msg}", msg_obj=new_message)
@@ -561,8 +563,17 @@ class Agent(BaseAgent):
561
563
  )
562
564
 
563
565
  if sandbox_run_result and sandbox_run_result.status == "error":
566
+ tool_return = ToolReturn(
567
+ status=sandbox_run_result.status, stdout=sandbox_run_result.stdout, stderr=sandbox_run_result.stderr
568
+ )
564
569
  messages = self._handle_function_error_response(
565
- function_response, tool_call_id, function_name, function_args, function_response, messages
570
+ function_response,
571
+ tool_call_id,
572
+ function_name,
573
+ function_args,
574
+ function_response,
575
+ messages,
576
+ [tool_return],
566
577
  )
567
578
  return messages, False, True # force a heartbeat to allow agent to handle error
568
579
 
@@ -591,20 +602,52 @@ class Agent(BaseAgent):
591
602
  error_msg_user = f"{error_msg}\n{traceback.format_exc()}"
592
603
  self.logger.error(error_msg_user)
593
604
  messages = self._handle_function_error_response(
594
- error_msg, tool_call_id, function_name, function_args, function_response, messages, include_function_failed_message=True
605
+ error_msg,
606
+ tool_call_id,
607
+ function_name,
608
+ function_args,
609
+ function_response,
610
+ messages,
611
+ [ToolReturn(status="error", stderr=[error_msg_user])],
612
+ include_function_failed_message=True,
595
613
  )
596
614
  return messages, False, True # force a heartbeat to allow agent to handle error
597
615
 
598
616
  # Step 4: check if function response is an error
599
617
  if function_response_string.startswith(ERROR_MESSAGE_PREFIX):
600
618
  error_msg = function_response_string
619
+ tool_return = (
620
+ ToolReturn(
621
+ status=sandbox_run_result.status,
622
+ stdout=sandbox_run_result.stdout,
623
+ stderr=sandbox_run_result.stderr,
624
+ )
625
+ if sandbox_run_result
626
+ else None
627
+ )
601
628
  messages = self._handle_function_error_response(
602
- error_msg, tool_call_id, function_name, function_args, function_response, messages, include_function_failed_message=True
629
+ error_msg,
630
+ tool_call_id,
631
+ function_name,
632
+ function_args,
633
+ function_response,
634
+ messages,
635
+ [tool_return] if tool_return else None,
636
+ include_function_failed_message=True,
603
637
  )
604
638
  return messages, False, True # force a heartbeat to allow agent to handle error
605
639
 
606
640
  # If no failures happened along the way: ...
607
641
  # Step 5: send the info on the function call and function response to GPT
642
+ tool_return = (
643
+ ToolReturn(
644
+ status=sandbox_run_result.status,
645
+ stdout=sandbox_run_result.stdout,
646
+ stderr=sandbox_run_result.stderr,
647
+ )
648
+ if sandbox_run_result
649
+ else None
650
+ )
608
651
  messages.append(
609
652
  Message.dict_to_message(
610
653
  agent_id=self.agent_state.id,
@@ -616,6 +659,7 @@ class Agent(BaseAgent):
616
659
  "content": function_response,
617
660
  "tool_call_id": tool_call_id,
618
661
  },
662
+ tool_returns=[tool_return] if tool_return else None,
619
663
  )
620
664
  ) # extend conversation with function response
621
665
  self.interface.function_message(f"Ran {function_name}({function_args})", msg_obj=messages[-1])
letta/cli/cli_load.py CHANGED
@@ -20,7 +20,7 @@ from letta.data_sources.connectors import DirectoryConnector
20
20
  app = typer.Typer()
21
21
 
22
22
 
23
- default_extensions = ".txt,.md,.pdf"
23
+ default_extensions = "txt,md,pdf"
24
24
 
25
25
 
26
26
  @app.command("directory")
@@ -73,7 +73,7 @@ def get_filenames_in_dir(
73
73
  ext = file_path.suffix.lstrip(".")
74
74
  # If required_exts is empty, match any file
75
75
  if not required_exts or ext in required_exts:
76
- files.append(file_path)
76
+ files.append(str(file_path))
77
77
 
78
78
  return files
79
79
 
@@ -9,6 +9,7 @@ from sqlalchemy import Dialect
9
9
  from letta.schemas.embedding_config import EmbeddingConfig
10
10
  from letta.schemas.enums import ToolRuleType
11
11
  from letta.schemas.llm_config import LLMConfig
12
+ from letta.schemas.message import ToolReturn
12
13
  from letta.schemas.tool_rule import ChildToolRule, ConditionalToolRule, ContinueToolRule, InitToolRule, TerminalToolRule, ToolRule
13
14
 
14
15
  # --------------------------
@@ -127,6 +128,41 @@ def deserialize_tool_calls(data: Optional[List[Dict]]) -> List[OpenAIToolCall]:
127
128
  return calls
128
129
 
129
130
 
131
+ # --------------------------
132
+ # ToolReturn Serialization
133
+ # --------------------------
134
+
135
+
136
+ def serialize_tool_returns(tool_returns: Optional[List[Union[ToolReturn, dict]]]) -> List[Dict]:
137
+ """Convert a list of ToolReturn objects into JSON-serializable format."""
138
+ if not tool_returns:
139
+ return []
140
+
141
+ serialized_tool_returns = []
142
+ for tool_return in tool_returns:
143
+ if isinstance(tool_return, ToolReturn):
144
+ serialized_tool_returns.append(tool_return.model_dump())
145
+ elif isinstance(tool_return, dict):
146
+ serialized_tool_returns.append(tool_return) # Already a dictionary, leave it as-is
147
+ else:
148
+ raise TypeError(f"Unexpected tool return type: {type(tool_return)}")
149
+
150
+ return serialized_tool_returns
151
+
152
+
153
+ def deserialize_tool_returns(data: Optional[List[Dict]]) -> List[ToolReturn]:
154
+ """Convert a JSON list back into ToolReturn objects."""
155
+ if not data:
156
+ return []
157
+
158
+ tool_returns = []
159
+ for item in data:
160
+ tool_return = ToolReturn(**item)
161
+ tool_returns.append(tool_return)
162
+
163
+ return tool_returns
164
+
165
+
130
166
  # --------------------------
131
167
  # Vector Serialization
132
168
  # --------------------------
@@ -5,11 +5,13 @@ from letta.helpers.converters import (
5
5
  deserialize_embedding_config,
6
6
  deserialize_llm_config,
7
7
  deserialize_tool_calls,
8
+ deserialize_tool_returns,
8
9
  deserialize_tool_rules,
9
10
  deserialize_vector,
10
11
  serialize_embedding_config,
11
12
  serialize_llm_config,
12
13
  serialize_tool_calls,
14
+ serialize_tool_returns,
13
15
  serialize_tool_rules,
14
16
  serialize_vector,
15
17
  )
@@ -67,6 +69,19 @@ class ToolCallColumn(TypeDecorator):
67
69
  return deserialize_tool_calls(value)
68
70
 
69
71
 
72
+ class ToolReturnColumn(TypeDecorator):
73
+ """Custom SQLAlchemy column type for storing the return value of a tool call as JSON."""
74
+
75
+ impl = JSON
76
+ cache_ok = True
77
+
78
+ def process_bind_param(self, value, dialect):
79
+ return serialize_tool_returns(value)
80
+
81
+ def process_result_value(self, value, dialect):
82
+ return deserialize_tool_returns(value)
83
+
84
+
70
85
  class CommonVector(TypeDecorator):
71
86
  """Custom SQLAlchemy column type for storing vectors in SQLite."""
72
87
 
letta/orm/message.py CHANGED
@@ -1,14 +1,15 @@
1
- from typing import Optional
1
+ from typing import List, Optional
2
2
 
3
3
  from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall as OpenAIToolCall
4
4
  from sqlalchemy import ForeignKey, Index
5
5
  from sqlalchemy.orm import Mapped, mapped_column, relationship
6
6
 
7
- from letta.orm.custom_columns import ToolCallColumn
7
+ from letta.orm.custom_columns import ToolCallColumn, ToolReturnColumn
8
8
  from letta.orm.mixins import AgentMixin, OrganizationMixin
9
9
  from letta.orm.sqlalchemy_base import SqlalchemyBase
10
10
  from letta.schemas.message import Message as PydanticMessage
11
11
  from letta.schemas.message import TextContent as PydanticTextContent
12
+ from letta.schemas.message import ToolReturn
12
13
 
13
14
 
14
15
  class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
@@ -26,11 +27,15 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
26
27
  text: Mapped[Optional[str]] = mapped_column(nullable=True, doc="Message content")
27
28
  model: Mapped[Optional[str]] = mapped_column(nullable=True, doc="LLM model used")
28
29
  name: Mapped[Optional[str]] = mapped_column(nullable=True, doc="Name for multi-agent scenarios")
29
- tool_calls: Mapped[OpenAIToolCall] = mapped_column(ToolCallColumn, doc="Tool call information")
30
+ tool_calls: Mapped[List[OpenAIToolCall]] = mapped_column(ToolCallColumn, doc="Tool call information")
30
31
  tool_call_id: Mapped[Optional[str]] = mapped_column(nullable=True, doc="ID of the tool call")
31
32
  step_id: Mapped[Optional[str]] = mapped_column(
32
33
  ForeignKey("steps.id", ondelete="SET NULL"), nullable=True, doc="ID of the step that this message belongs to"
33
34
  )
35
+ otid: Mapped[Optional[str]] = mapped_column(nullable=True, doc="The offline threading ID associated with this message")
36
+ tool_returns: Mapped[List[ToolReturn]] = mapped_column(
37
+ ToolReturnColumn, nullable=True, doc="Tool execution return information for prior tool calls"
38
+ )
34
39
 
35
40
  # Relationships
36
41
  agent: Mapped["Agent"] = relationship("Agent", back_populates="messages", lazy="selectin")
letta/schemas/agent.py CHANGED
@@ -175,11 +175,6 @@ class CreateAgent(BaseModel, validate_assignment=True): #
175
175
  # don't check if not provided
176
176
  return name
177
177
 
178
- # TODO: this check should also be added to other model (e.g. User.name)
179
- # Length check
180
- if not (1 <= len(name) <= 50):
181
- raise ValueError("Name length must be between 1 and 50 characters.")
182
-
183
178
  # Regex for allowed characters (alphanumeric, spaces, hyphens, underscores)
184
179
  if not re.match("^[A-Za-z0-9 _-]+$", name):
185
180
  raise ValueError("Name contains invalid characters.")
letta/schemas/message.py CHANGED
@@ -124,9 +124,11 @@ class Message(BaseMessage):
124
124
  agent_id: Optional[str] = Field(None, description="The unique identifier of the agent.")
125
125
  model: Optional[str] = Field(None, description="The model used to make the function call.")
126
126
  name: Optional[str] = Field(None, description="The name of the participant.")
127
- tool_calls: Optional[List[OpenAIToolCall,]] = Field(None, description="The list of tool calls requested.")
127
+ tool_calls: Optional[List[OpenAIToolCall]] = Field(None, description="The list of tool calls requested.")
128
128
  tool_call_id: Optional[str] = Field(None, description="The id of the tool call.")
129
129
  step_id: Optional[str] = Field(None, description="The id of the step that this message was created in.")
130
+ otid: Optional[str] = Field(None, description="The offline threading id associated with this message")
131
+ tool_returns: Optional[List[ToolReturn]] = Field(None, description="Tool execution return information for prior tool calls")
130
132
 
131
133
  # This overrides the optional base orm schema, created_at MUST exist on all messages objects
132
134
  created_at: datetime = Field(default_factory=get_utc_time, description="The timestamp when the object was created.")
@@ -299,8 +301,10 @@ class Message(BaseMessage):
299
301
  id=self.id,
300
302
  date=self.created_at,
301
303
  tool_return=self.text,
302
- status=status_enum,
304
+ status=self.tool_returns[0].status if self.tool_returns else status_enum,
303
305
  tool_call_id=self.tool_call_id,
306
+ stdout=self.tool_returns[0].stdout if self.tool_returns else None,
307
+ stderr=self.tool_returns[0].stderr if self.tool_returns else None,
304
308
  )
305
309
  )
306
310
  elif self.role == MessageRole.user:
@@ -338,6 +342,7 @@ class Message(BaseMessage):
338
342
  allow_functions_style: bool = False, # allow deprecated functions style?
339
343
  created_at: Optional[datetime] = None,
340
344
  id: Optional[str] = None,
345
+ tool_returns: Optional[List[ToolReturn]] = None,
341
346
  ):
342
347
  """Convert a ChatCompletion message object into a Message object (synced to DB)"""
343
348
  if not created_at:
@@ -366,6 +371,7 @@ class Message(BaseMessage):
366
371
  tool_call_id=openai_message_dict["tool_call_id"] if "tool_call_id" in openai_message_dict else None,
367
372
  created_at=created_at,
368
373
  id=str(id),
374
+ tool_returns=tool_returns,
369
375
  )
370
376
  else:
371
377
  return Message(
@@ -378,6 +384,7 @@ class Message(BaseMessage):
378
384
  tool_calls=openai_message_dict["tool_calls"] if "tool_calls" in openai_message_dict else None,
379
385
  tool_call_id=openai_message_dict["tool_call_id"] if "tool_call_id" in openai_message_dict else None,
380
386
  created_at=created_at,
387
+ tool_returns=tool_returns,
381
388
  )
382
389
 
383
390
  elif "function_call" in openai_message_dict and openai_message_dict["function_call"] is not None:
@@ -411,6 +418,7 @@ class Message(BaseMessage):
411
418
  tool_call_id=None, # NOTE: None, since this field is only non-null for role=='tool'
412
419
  created_at=created_at,
413
420
  id=str(id),
421
+ tool_returns=tool_returns,
414
422
  )
415
423
  else:
416
424
  return Message(
@@ -423,6 +431,7 @@ class Message(BaseMessage):
423
431
  tool_calls=tool_calls,
424
432
  tool_call_id=None, # NOTE: None, since this field is only non-null for role=='tool'
425
433
  created_at=created_at,
434
+ tool_returns=tool_returns,
426
435
  )
427
436
 
428
437
  else:
@@ -456,6 +465,7 @@ class Message(BaseMessage):
456
465
  tool_call_id=openai_message_dict["tool_call_id"] if "tool_call_id" in openai_message_dict else None,
457
466
  created_at=created_at,
458
467
  id=str(id),
468
+ tool_returns=tool_returns,
459
469
  )
460
470
  else:
461
471
  return Message(
@@ -468,6 +478,7 @@ class Message(BaseMessage):
468
478
  tool_calls=tool_calls,
469
479
  tool_call_id=openai_message_dict["tool_call_id"] if "tool_call_id" in openai_message_dict else None,
470
480
  created_at=created_at,
481
+ tool_returns=tool_returns,
471
482
  )
472
483
 
473
484
  def to_openai_dict_search_results(self, max_tool_id_length: int = TOOL_CALL_ID_MAX_LEN) -> dict:
@@ -844,3 +855,9 @@ class Message(BaseMessage):
844
855
  raise ValueError(self.role)
845
856
 
846
857
  return cohere_message
858
+
859
+
860
+ class ToolReturn(BaseModel):
861
+ status: Literal["success", "error"] = Field(..., description="The status of the tool call")
862
+ stdout: Optional[List[str]] = Field(None, description="Captured stdout (e.g. prints, logs) from the tool invocation")
863
+ stderr: Optional[List[str]] = Field(None, description="Captured stderr from the tool invocation")
@@ -5,10 +5,12 @@ from marshmallow import fields, post_dump
5
5
  from letta.orm import Agent
6
6
  from letta.schemas.agent import AgentState as PydanticAgentState
7
7
  from letta.schemas.user import User
8
+ from letta.serialize_schemas.agent_environment_variable import SerializedAgentEnvironmentVariableSchema
8
9
  from letta.serialize_schemas.base import BaseSchema
9
10
  from letta.serialize_schemas.block import SerializedBlockSchema
10
11
  from letta.serialize_schemas.custom_fields import EmbeddingConfigField, LLMConfigField, ToolRulesField
11
12
  from letta.serialize_schemas.message import SerializedMessageSchema
13
+ from letta.serialize_schemas.tag import SerializedAgentTagSchema
12
14
  from letta.serialize_schemas.tool import SerializedToolSchema
13
15
  from letta.server.db import SessionLocal
14
16
 
@@ -28,6 +30,8 @@ class SerializedAgentSchema(BaseSchema):
28
30
  messages = fields.List(fields.Nested(SerializedMessageSchema))
29
31
  core_memory = fields.List(fields.Nested(SerializedBlockSchema))
30
32
  tools = fields.List(fields.Nested(SerializedToolSchema))
33
+ tool_exec_environment_variables = fields.List(fields.Nested(SerializedAgentEnvironmentVariableSchema))
34
+ tags = fields.List(fields.Nested(SerializedAgentTagSchema))
31
35
 
32
36
  def __init__(self, *args, session: SessionLocal, actor: User, **kwargs):
33
37
  super().__init__(*args, actor=actor, **kwargs)
@@ -66,4 +70,4 @@ class SerializedAgentSchema(BaseSchema):
66
70
  class Meta(BaseSchema.Meta):
67
71
  model = Agent
68
72
  # TODO: Serialize these as well...
69
- exclude = BaseSchema.Meta.exclude + ("sources", "tags", "source_passages", "agent_passages")
73
+ exclude = BaseSchema.Meta.exclude + ("sources", "source_passages", "agent_passages")
@@ -0,0 +1,21 @@
1
+ import uuid
2
+ from typing import Optional
3
+
4
+ from letta.orm.sandbox_config import AgentEnvironmentVariable
5
+ from letta.serialize_schemas.base import BaseSchema
6
+
7
+
8
+ class SerializedAgentEnvironmentVariableSchema(BaseSchema):
9
+ """
10
+ Marshmallow schema for serializing/deserializing AgentEnvironmentVariable objects.
11
+ """
12
+
13
+ __pydantic_model__ = None
14
+
15
+ def generate_id(self) -> Optional[str]:
16
+ # TODO: This is brittle and duplicated in orm/sandbox_config.py
17
+ return f"agent-env-{uuid.uuid4()}"
18
+
19
+ class Meta(BaseSchema.Meta):
20
+ model = AgentEnvironmentVariable
21
+ exclude = BaseSchema.Meta.exclude + ("agent",)
@@ -22,9 +22,20 @@ class BaseSchema(SQLAlchemyAutoSchema):
22
22
  super().__init__(*args, **kwargs)
23
23
  self.actor = actor
24
24
 
25
+ def generate_id(self) -> Optional[str]:
26
+ if self.__pydantic_model__:
27
+ return self.__pydantic_model__.generate_id()
28
+
29
+ return None
30
+
25
31
  @post_dump
26
- def sanitize_ids(self, data: Dict, **kwargs):
27
- data["id"] = self.__pydantic_model__.generate_id()
32
+ def sanitize_ids(self, data: Dict, **kwargs) -> Dict:
33
+ if self.Meta.model:
34
+ mapper = inspect(self.Meta.model)
35
+ if "id" in mapper.columns:
36
+ generated_id = self.generate_id()
37
+ if generated_id:
38
+ data["id"] = generated_id
28
39
 
29
40
  for sensitive_id in BaseSchema.sensitive_ids.union(BaseSchema.sensitive_relationships):
30
41
  if sensitive_id in data:
@@ -33,7 +44,7 @@ class BaseSchema(SQLAlchemyAutoSchema):
33
44
  return data
34
45
 
35
46
  @pre_load
36
- def regenerate_ids(self, data: Dict, **kwargs):
47
+ def regenerate_ids(self, data: Dict, **kwargs) -> Dict:
37
48
  if self.Meta.model:
38
49
  mapper = inspect(self.Meta.model)
39
50
  for sensitive_id in BaseSchema.sensitive_ids:
@@ -0,0 +1,18 @@
1
+ from marshmallow import fields
2
+
3
+ from letta.orm.agents_tags import AgentsTags
4
+ from letta.serialize_schemas.base import BaseSchema
5
+
6
+
7
+ class SerializedAgentTagSchema(BaseSchema):
8
+ """
9
+ Marshmallow schema for serializing/deserializing Agent Tags.
10
+ """
11
+
12
+ __pydantic_model__ = None
13
+
14
+ tag = fields.String(required=True)
15
+
16
+ class Meta(BaseSchema.Meta):
17
+ model = AgentsTags
18
+ exclude = BaseSchema.Meta.exclude + ("agent",)
@@ -235,12 +235,14 @@ def create_application() -> "FastAPI":
235
235
  endpoint = os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
236
236
  if endpoint:
237
237
  print(f"▶ Using OTLP tracing with endpoint: {endpoint}")
238
+ env_name_suffix = os.getenv("ENV_NAME")
239
+ service_name = f"letta-server-{env_name_suffix.lower()}" if env_name_suffix else "letta-server"
238
240
  from letta.tracing import setup_tracing
239
241
 
240
242
  setup_tracing(
241
243
  endpoint=endpoint,
242
244
  app=app,
243
- service_name="memgpt-server",
245
+ service_name=service_name,
244
246
  )
245
247
 
246
248
  for route in v1_routes:
@@ -1169,8 +1169,10 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
1169
1169
  id=msg_obj.id,
1170
1170
  date=msg_obj.created_at,
1171
1171
  tool_return=msg,
1172
- status="success",
1172
+ status=msg_obj.tool_returns[0].status if msg_obj.tool_returns else "success",
1173
1173
  tool_call_id=msg_obj.tool_call_id,
1174
+ stdout=msg_obj.tool_returns[0].stdout if msg_obj.tool_returns else None,
1175
+ stderr=msg_obj.tool_returns[0].stderr if msg_obj.tool_returns else None,
1174
1176
  )
1175
1177
 
1176
1178
  elif msg.startswith("Error: "):
@@ -1181,8 +1183,10 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
1181
1183
  id=msg_obj.id,
1182
1184
  date=msg_obj.created_at,
1183
1185
  tool_return=msg,
1184
- status="error",
1186
+ status=msg_obj.tool_returns[0].status if msg_obj.tool_returns else "error",
1185
1187
  tool_call_id=msg_obj.tool_call_id,
1188
+ stdout=msg_obj.tool_returns[0].stdout if msg_obj.tool_returns else None,
1189
+ stderr=msg_obj.tool_returns[0].stderr if msg_obj.tool_returns else None,
1186
1190
  )
1187
1191
 
1188
1192
  else:
@@ -1,10 +1,13 @@
1
+ import json
1
2
  import traceback
2
3
  from datetime import datetime
3
4
  from typing import Annotated, List, Optional
4
5
 
5
- from fastapi import APIRouter, BackgroundTasks, Body, Depends, Header, HTTPException, Query, status
6
+ from fastapi import APIRouter, BackgroundTasks, Body, Depends, File, Header, HTTPException, Query, UploadFile, status
6
7
  from fastapi.responses import JSONResponse
8
+ from marshmallow import ValidationError
7
9
  from pydantic import Field
10
+ from sqlalchemy.exc import IntegrityError, OperationalError
8
11
 
9
12
  from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG
10
13
  from letta.log import get_logger
@@ -88,6 +91,65 @@ def list_agents(
88
91
  return agents
89
92
 
90
93
 
94
+ @router.get("/{agent_id}/download", operation_id="download_agent_serialized")
95
+ def download_agent_serialized(
96
+ agent_id: str,
97
+ server: "SyncServer" = Depends(get_letta_server),
98
+ actor_id: Optional[str] = Header(None, alias="user_id"),
99
+ ):
100
+ """
101
+ Download the serialized JSON representation of an agent.
102
+ """
103
+ actor = server.user_manager.get_user_or_default(user_id=actor_id)
104
+
105
+ try:
106
+ serialized_agent = server.agent_manager.serialize(agent_id=agent_id, actor=actor)
107
+ return JSONResponse(content=serialized_agent, media_type="application/json")
108
+ except NoResultFound:
109
+ raise HTTPException(status_code=404, detail=f"Agent with id={agent_id} not found for user_id={actor.id}.")
110
+
111
+
112
+ @router.post("/upload", response_model=AgentState, operation_id="upload_agent_serialized")
113
+ async def upload_agent_serialized(
114
+ file: UploadFile = File(...),
115
+ server: "SyncServer" = Depends(get_letta_server),
116
+ actor_id: Optional[str] = Header(None, alias="user_id"),
117
+ append_copy_suffix: bool = Query(True, description='If set to True, appends "_copy" to the end of the agent name.'),
118
+ override_existing_tools: bool = Query(
119
+ True,
120
+ description="If set to True, existing tools can get their source code overwritten by the uploaded tool definitions. Note that Letta core tools can never be updated externally.",
121
+ ),
122
+ ):
123
+ """
124
+ Upload a serialized agent JSON file and recreate the agent in the system.
125
+ """
126
+ actor = server.user_manager.get_user_or_default(user_id=actor_id)
127
+
128
+ try:
129
+ serialized_data = await file.read()
130
+ agent_json = json.loads(serialized_data)
131
+ new_agent = server.agent_manager.deserialize(
132
+ serialized_agent=agent_json, actor=actor, append_copy_suffix=append_copy_suffix, override_existing_tools=override_existing_tools
133
+ )
134
+ return new_agent
135
+
136
+ except json.JSONDecodeError:
137
+ raise HTTPException(status_code=400, detail="Corrupted agent file format.")
138
+
139
+ except ValidationError as e:
140
+ raise HTTPException(status_code=422, detail=f"Invalid agent schema: {str(e)}")
141
+
142
+ except IntegrityError as e:
143
+ raise HTTPException(status_code=409, detail=f"Database integrity error: {str(e)}")
144
+
145
+ except OperationalError as e:
146
+ raise HTTPException(status_code=503, detail=f"Database connection error. Please try again later: {str(e)}")
147
+
148
+ except Exception:
149
+ traceback.print_exc()
150
+ raise HTTPException(status_code=500, detail="An unexpected error occurred while uploading the agent.")
151
+
152
+
91
153
  @router.get("/{agent_id}/context", response_model=ContextWindowOverview, operation_id="retrieve_agent_context_window")
92
154
  def retrieve_agent_context_window(
93
155
  agent_id: str,
@@ -15,6 +15,7 @@ from letta.orm import Identity as IdentityModel
15
15
  from letta.orm import Source as SourceModel
16
16
  from letta.orm import SourcePassage, SourcesAgents
17
17
  from letta.orm import Tool as ToolModel
18
+ from letta.orm.enums import ToolType
18
19
  from letta.orm.errors import NoResultFound
19
20
  from letta.orm.sandbox_config import AgentEnvironmentVariable as AgentEnvironmentVariableModel
20
21
  from letta.orm.sqlite_functions import adapt_array
@@ -399,13 +400,15 @@ class AgentManager:
399
400
  return schema.dump(agent)
400
401
 
401
402
  @enforce_types
402
- def deserialize(self, serialized_agent: dict, actor: PydanticUser, mark_as_copy: bool = True) -> PydanticAgentState:
403
+ def deserialize(
404
+ self, serialized_agent: dict, actor: PydanticUser, append_copy_suffix: bool = True, override_existing_tools: bool = True
405
+ ) -> PydanticAgentState:
403
406
  tool_data_list = serialized_agent.pop("tools", [])
404
407
 
405
408
  with self.session_maker() as session:
406
409
  schema = SerializedAgentSchema(session=session, actor=actor)
407
410
  agent = schema.load(serialized_agent, session=session)
408
- if mark_as_copy:
411
+ if append_copy_suffix:
409
412
  agent.name += "_copy"
410
413
  agent.create(session, actor=actor)
411
414
  pydantic_agent = agent.to_pydantic()
@@ -413,7 +416,20 @@ class AgentManager:
413
416
  # Need to do this separately as there's some fancy upsert logic that SqlAlchemy cannot handle
414
417
  for tool_data in tool_data_list:
415
418
  pydantic_tool = SerializedToolSchema(actor=actor).load(tool_data, transient=True).to_pydantic()
416
- pydantic_tool = self.tool_manager.create_or_update_tool(pydantic_tool, actor=actor)
419
+
420
+ existing_pydantic_tool = self.tool_manager.get_tool_by_name(pydantic_tool.name, actor=actor)
421
+ # If the tool exists
422
+ # AND EITHER:
423
+ # 1) override_existing_tools is set to False
424
+ # 2) existing_pydantic_tool is NOT any type of Letta core tool
425
+ if existing_pydantic_tool and (
426
+ existing_pydantic_tool.tool_type in {ToolType.LETTA_CORE, ToolType.LETTA_MULTI_AGENT_CORE, ToolType.LETTA_MEMORY_CORE}
427
+ or not override_existing_tools
428
+ ):
429
+ pydantic_tool = existing_pydantic_tool
430
+ else:
431
+ pydantic_tool = self.tool_manager.create_or_update_tool(pydantic_tool, actor=actor)
432
+
417
433
  pydantic_agent = self.attach_tool(agent_id=pydantic_agent.id, tool_id=pydantic_tool.id, actor=actor)
418
434
 
419
435
  return pydantic_agent
@@ -461,6 +477,8 @@ class AgentManager:
461
477
  value=value,
462
478
  agent_id=agent_id,
463
479
  organization_id=actor.organization_id,
480
+ created_by_id=actor.id,
481
+ last_updated_by_id=actor.id,
464
482
  )
465
483
  )
466
484
 
letta/settings.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from pathlib import Path
2
3
  from typing import Optional
3
4
 
@@ -116,13 +117,20 @@ class ModelSettings(BaseSettings):
116
117
  disable_schema_generation: bool = False
117
118
 
118
119
 
119
- cors_origins = [
120
- "http://letta.localhost",
121
- "http://localhost:8283",
122
- "http://localhost:8083",
123
- "http://localhost:3000",
124
- "http://localhost:4200",
125
- ]
120
+ env_cors_origins = os.getenv("ACCEPTABLE_ORIGINS")
121
+
122
+ cors_origins = (
123
+ [
124
+ "http://letta.localhost",
125
+ "http://localhost:8283",
126
+ "http://localhost:8083",
127
+ "http://localhost:3000",
128
+ "http://localhost:4200",
129
+ ]
130
+ + [env_cors_origins]
131
+ if env_cors_origins
132
+ else []
133
+ )
126
134
 
127
135
  # read pg_uri from ~/.letta/pg_uri or set to none, this is to support Letta Desktop
128
136
  default_pg_uri = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.6.36.dev20250305104137
3
+ Version: 0.6.37.dev20250306104215
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -25,7 +25,7 @@ Requires-Dist: alembic (>=1.13.3,<2.0.0)
25
25
  Requires-Dist: anthropic (>=0.43.0,<0.44.0)
26
26
  Requires-Dist: autoflake (>=2.3.0,<3.0.0) ; extra == "dev" or extra == "all"
27
27
  Requires-Dist: black[jupyter] (>=24.2.0,<25.0.0) ; extra == "dev" or extra == "all"
28
- Requires-Dist: boto3 (>=1.36.24,<2.0.0) ; extra == "bedrock" or extra == "all"
28
+ Requires-Dist: boto3 (>=1.36.24,<2.0.0) ; extra == "bedrock"
29
29
  Requires-Dist: brotli (>=1.1.0,<2.0.0)
30
30
  Requires-Dist: colorama (>=0.4.6,<0.5.0)
31
31
  Requires-Dist: composio-core (>=0.7.2,<0.8.0)
@@ -39,7 +39,7 @@ Requires-Dist: docx2txt (>=0.8,<0.9)
39
39
  Requires-Dist: e2b-code-interpreter (>=1.0.3,<2.0.0) ; extra == "cloud-tool-sandbox"
40
40
  Requires-Dist: faker (>=36.1.0,<37.0.0)
41
41
  Requires-Dist: fastapi (>=0.115.6,<0.116.0) ; extra == "server" or extra == "all"
42
- Requires-Dist: google-genai (>=1.1.0,<2.0.0) ; extra == "google" or extra == "all"
42
+ Requires-Dist: google-genai (>=1.1.0,<2.0.0) ; extra == "google"
43
43
  Requires-Dist: grpcio (>=1.68.1,<2.0.0)
44
44
  Requires-Dist: grpcio-tools (>=1.68.1,<2.0.0)
45
45
  Requires-Dist: html2text (>=2020.1.16,<2021.0.0)
@@ -49,7 +49,7 @@ Requires-Dist: isort (>=5.13.2,<6.0.0) ; extra == "dev" or extra == "all"
49
49
  Requires-Dist: jinja2 (>=3.1.5,<4.0.0)
50
50
  Requires-Dist: langchain (>=0.3.7,<0.4.0) ; extra == "external-tools" or extra == "all"
51
51
  Requires-Dist: langchain-community (>=0.3.7,<0.4.0) ; extra == "external-tools" or extra == "all"
52
- Requires-Dist: letta_client (>=0.1.23,<0.2.0)
52
+ Requires-Dist: letta_client (>=0.1.54,<0.2.0)
53
53
  Requires-Dist: llama-index (>=0.12.2,<0.13.0)
54
54
  Requires-Dist: llama-index-embeddings-openai (>=0.3.1,<0.4.0)
55
55
  Requires-Dist: locust (>=2.31.5,<3.0.0) ; extra == "dev" or extra == "all"
@@ -89,7 +89,7 @@ Requires-Dist: sqlalchemy-json (>=0.7.0,<0.8.0)
89
89
  Requires-Dist: sqlalchemy-utils (>=0.41.2,<0.42.0)
90
90
  Requires-Dist: sqlmodel (>=0.0.16,<0.0.17)
91
91
  Requires-Dist: tqdm (>=4.66.1,<5.0.0)
92
- Requires-Dist: typer (>=0.12,<1.0)
92
+ Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
93
93
  Requires-Dist: uvicorn (>=0.24.0.post1,<0.25.0) ; extra == "server" or extra == "all"
94
94
  Requires-Dist: wikipedia (>=1.4.0,<2.0.0) ; extra == "external-tools" or extra == "tests" or extra == "all"
95
95
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
- letta/__init__.py,sha256=3laFXdFab_elN6Et2nVUFuVgXonpAuR0CntCKlRqyPI,918
1
+ letta/__init__.py,sha256=dygpre5_iDLqGclnvhkq6BPMUZ8uZ8GWBua27pnkvGM,918
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=VljWPlG9bh22cF0o0s9lGw0Qi5Ua2O2JLSOiFtH1SfE,62823
3
+ letta/agent.py,sha256=IcLnwnJNeVFJhpU3mJg-609sg_eWBZP8OwfCvEqcN-k,64398
4
4
  letta/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  letta/agents/base_agent.py,sha256=8IMB7UK4ft-Wi-ZYjX7akqQUhk_cSswRgepqeZyvCMs,1550
6
6
  letta/agents/ephemeral_agent.py,sha256=XOQ5rj3cn7i7Imr_aFaL8DbH7oAizrHbmUy8QZuz9yk,2704
@@ -9,7 +9,7 @@ letta/benchmark/benchmark.py,sha256=ebvnwfp3yezaXOQyGXkYCDYpsmre-b9hvNtnyx4xkG0,
9
9
  letta/benchmark/constants.py,sha256=aXc5gdpMGJT327VuxsT5FngbCK2J41PQYeICBO7g_RE,536
10
10
  letta/cli/cli.py,sha256=zJz78-qDUz-depb7VQWkg87RBKiETQU4h9DI6ukQBa8,16477
11
11
  letta/cli/cli_config.py,sha256=MNMhIAAjXiAy2gX_gAtqiY0Ya6VNbzXJWjIcRVEZa-k,8597
12
- letta/cli/cli_load.py,sha256=xFw-CuzjChcIptaqQ1XpDROENt0JSjyPeiQ0nmEeO1k,2706
12
+ letta/cli/cli_load.py,sha256=vER0PwpHnsCZtCHcR2YjEXM-VVuO9jhfQibdo3gI3S0,2703
13
13
  letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  letta/client/client.py,sha256=G2X1QBXOQORzVwViRNax2EPSYwzdxw_L2RiTFS-SS9Q,139150
15
15
  letta/client/streaming.py,sha256=lN9vamc07sfQlRbFif327GvURLUPhx-4AC_oUOPvs6w,4543
@@ -17,7 +17,7 @@ letta/client/utils.py,sha256=VCGV-op5ZSmurd4yw7Vhf93XDQ0BkyBT8qsuV7EqfiU,2859
17
17
  letta/config.py,sha256=JFGY4TWW0Wm5fTbZamOwWqk5G8Nn-TXyhgByGoAqy2c,12375
18
18
  letta/constants.py,sha256=MvctT-wAIq4CKlYn3XerWAYsqiJj7ydJqqCCuwJLQa0,7675
19
19
  letta/data_sources/connectors.py,sha256=R2AssXpqS7wN6VI8AfxvqaZs5S1ZACc4E_FewmR9iZI,7022
20
- letta/data_sources/connectors_helper.py,sha256=2TQjCt74fCgT5sw1AP8PalDEk06jPBbhrPG4HVr-WLs,3371
20
+ letta/data_sources/connectors_helper.py,sha256=oQpVlc-BjSz9sTZ7sp4PsJSXJbBKpZPi3Dam03CURTQ,3376
21
21
  letta/embeddings.py,sha256=zqlfbN3aCgSOlNd9M2NW9zrwx4WwQzketb8oa5BzzE8,10831
22
22
  letta/errors.py,sha256=6fQXg2unP-2fo3R7db0ayKKWlD2XMusOPNi9TgJplCg,5558
23
23
  letta/functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -31,7 +31,7 @@ letta/functions/interface.py,sha256=s_PPp5WDvGH_y9KUpMlORkdC141ITczFk3wsevrrUD8,
31
31
  letta/functions/schema_generator.py,sha256=dLM8pQ9UP1vP9cnKHgA-UhGllMNvyEIjsO-r47RP9LM,20765
32
32
  letta/helpers/__init__.py,sha256=p0luQ1Oe3Skc6sH4O58aHHA3Qbkyjifpuq0DZ1GAY0U,59
33
33
  letta/helpers/composio_helpers.py,sha256=6CWV483vE3N-keQlblexxBiQHxorMAgQuvbok4adGO4,949
34
- letta/helpers/converters.py,sha256=p-_9bzq_DXerQE9abHcQ8xxdQlvu9nwbmMzdefFNCuQ,5541
34
+ letta/helpers/converters.py,sha256=yM0slUU4YMW6K260JT88xnCxK2jB4RstSmaOezsT_Ik,6679
35
35
  letta/helpers/datetime_helpers.py,sha256=7U5ZJkE0cLki4sG8ukIHZSAoFfQpLWQu2kFekETy6Zg,2633
36
36
  letta/helpers/json_helpers.py,sha256=PWZ5HhSqGXO4e563dM_8M72q7ScirjXQ4Rv1ckohaV8,396
37
37
  letta/helpers/tool_execution_helper.py,sha256=bskCscuz2nqoUboFcYA7sQGeikdEyqiYnNpO4gLQTdc,7469
@@ -107,7 +107,7 @@ letta/orm/agents_tags.py,sha256=dYSnHz4IWBjyOiQ4RJomX3P0QN76JTlEZEw5eJM6Emg,925
107
107
  letta/orm/base.py,sha256=VjvxF9TwKF9Trf8BJkDgf7D6KrWWopOkUiF18J3IElk,3071
108
108
  letta/orm/block.py,sha256=EjH8lXexHtFIHJ8G-RjNo2oAH834x0Hbn4CER9S4U74,3880
109
109
  letta/orm/blocks_agents.py,sha256=W0dykl9OchAofSuAYZD5zNmhyMabPr9LTJrz-I3A0m4,983
110
- letta/orm/custom_columns.py,sha256=NnTpOhSdnlFb45AkQg8RZQU2aMm7hxjZc_ubleza_nw,2142
110
+ letta/orm/custom_columns.py,sha256=UW_qhIJGYlPn7TMMeTILYKXibdZ2CKVlhXDX5UmnNxA,2567
111
111
  letta/orm/enums.py,sha256=g4qbX_6a1NQpCt3rScAySbwSbkwqGmnOEaNbkMmCWF4,474
112
112
  letta/orm/errors.py,sha256=Se0Guz-gqi-D36NUWSh7AP9zTVCSph9KgZh_trwng4o,734
113
113
  letta/orm/file.py,sha256=7_p7LxityP3NQZVURQYG0kgcZhEkVuMN0Fj4h9YOe1w,1780
@@ -115,7 +115,7 @@ letta/orm/identities_agents.py,sha256=cfIQ6UsbwmjxtGVmFt1ArK2zbKr9k6VWoELuISDnLS
115
115
  letta/orm/identity.py,sha256=jGg7A2hjlH9qWGOJDy7zsFugdshfiPyNRLDK68wNm70,2475
116
116
  letta/orm/job.py,sha256=G2P-xUpTapD4lhU2FwMZET1b5QR4ju9WOB3uiTKD_mw,2157
117
117
  letta/orm/job_messages.py,sha256=SgwaYPYwwAC3dBtl9Xye_TWUl9H_-m95S95TTcfPyOg,1245
118
- letta/orm/message.py,sha256=qY5lhSq5JDOSGKnEnLryKr2bHYLr60pk2LqYBxGeONQ,2718
118
+ letta/orm/message.py,sha256=Q3Tx8MqImgmyxiGnfxrgkosZdhWeOiHP9XUOM4Y9KFk,3085
119
119
  letta/orm/mixins.py,sha256=9c79Kfr-Z1hL-SDYKeoptx_yMTbBwJJBo9nrKEzSDAc,1622
120
120
  letta/orm/organization.py,sha256=ciF2ffCBMddfOQ9O1gHv_cFMTPUlSQZNDYYXFgH_z1E,3070
121
121
  letta/orm/passage.py,sha256=tQi-fZZFBFVz0KZxd0foKPkAOaempgiYOHHK6lJ98gw,3332
@@ -157,7 +157,7 @@ letta/prompts/system/memgpt_modified_o1.txt,sha256=objnDgnxpF3-MmU28ZqZ7-TOG8UlH
157
157
  letta/prompts/system/memgpt_offline_memory.txt,sha256=rWEJeF-6aiinjkJM9hgLUYCmlEcC_HekYB1bjEUYq6M,2460
158
158
  letta/prompts/system/memgpt_offline_memory_chat.txt,sha256=ituh7gDuio7nC2UKFB7GpBq6crxb8bYedQfJ0ADoPgg,3949
159
159
  letta/pytest.ini,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
160
- letta/schemas/agent.py,sha256=6wYcMCDi920UdyUwBm_-xt-cS2Q4G3vfJXYeB9r3BT8,14347
160
+ letta/schemas/agent.py,sha256=4pTHrTV_FYyJuujtSglRsJQ2ouF0FRYWNmvObbLeL2g,14123
161
161
  letta/schemas/block.py,sha256=FYYmRWjH4d4QHMUx_nmIXjv_qJna_l-Ip_4i51wDBPA,5942
162
162
  letta/schemas/embedding_config.py,sha256=ufboqW9ctSBJdhwzJRbrGtTzOTwSKfT0LY0mowpr6fs,3398
163
163
  letta/schemas/embedding_config_overrides.py,sha256=lkTa4y-EQ2RnaEKtKDM0sEAk7EwNa67REw8DGNNtGQY,84
@@ -174,7 +174,7 @@ letta/schemas/letta_response.py,sha256=pq-SxXQy5yZo1-DiAwV2mMURlUvz1Uu7HHR_tB1hM
174
174
  letta/schemas/llm_config.py,sha256=bqq4LGE9layPcnnkzd_8d2SB8o1x8XdDzfd2ZkYQwcY,5611
175
175
  letta/schemas/llm_config_overrides.py,sha256=-oRglCTcajF6UAK3RAa0FLWVuKODPI1v403fDIWMAtA,1815
176
176
  letta/schemas/memory.py,sha256=GOYDfPKzbWftUWO9Hv4KW7xAi1EIQmC8zpP7qvEkVHw,10245
177
- letta/schemas/message.py,sha256=3-0zd-WuasJ1rZPDSqwJ7mRex399icPgpsVk19T7CoQ,37687
177
+ letta/schemas/message.py,sha256=dJHjvF8K-tHZMXvvPu51AKeo48L6YSo_HulORqFLSpM,38840
178
178
  letta/schemas/openai/chat_completion_request.py,sha256=3tALmbBV2pv1CcqzNLBxxIPOQ8Z85woucT7FN0fuDic,3402
179
179
  letta/schemas/openai/chat_completion_response.py,sha256=koEb_NTiz5YsAAX81Z0cSqSFX4a6MdD2lhoXtxF_rw4,4100
180
180
  letta/schemas/openai/chat_completions.py,sha256=l0e9sT9boTD5VBU5YtJ0s7qUtCfFGB2K-gQLeEZ2LHU,3599
@@ -192,29 +192,31 @@ letta/schemas/tool_rule.py,sha256=2YQZba4fXS3u4j8pIk7BDujfq8rnxSVMwJSyaVgApH4,21
192
192
  letta/schemas/usage.py,sha256=8oYRH-JX0PfjIu2zkT5Uu3UWQ7Unnz_uHiO8hRGI4m0,912
193
193
  letta/schemas/user.py,sha256=V32Tgl6oqB3KznkxUz12y7agkQicjzW7VocSpj78i6Q,1526
194
194
  letta/serialize_schemas/__init__.py,sha256=mflGEZvM3NuMG9Q6dccEdVk73BUHoX-v7hmfk025Gmc,64
195
- letta/serialize_schemas/agent.py,sha256=XQiLmve761rqktUNNjMYNyuxn1xHq0wtJeZVD9UV11c,2817
196
- letta/serialize_schemas/base.py,sha256=gH3LyWn7EbiDSJ5T6vUhTf6dilTSfM_TJMIsfQ1Wu_E,1682
195
+ letta/serialize_schemas/agent.py,sha256=AyQJYjQ5Af8GpccpjD32dRwAScJ__Uv5R0-Sx3KIj0k,3149
196
+ letta/serialize_schemas/agent_environment_variable.py,sha256=IDcivP-xB1j6Fn0onSM4T1l4CSlXhOLWNmKHG2psOoU,641
197
+ letta/serialize_schemas/base.py,sha256=X5cs1U4P0Vjem4SKNCYBiSAIboThJdM2Oyphu_Nl9Ag,2040
197
198
  letta/serialize_schemas/block.py,sha256=BeZ2FZO59IWUHiPMSNKlZMi9dH9Gbys0rJpsoCFBjdg,420
198
199
  letta/serialize_schemas/custom_fields.py,sha256=HPonsK5DIU9EezXmQG0vAq2CWZVucld6Fqm3-VcJBg8,2101
199
200
  letta/serialize_schemas/message.py,sha256=Tdj_ixjjqM2j29gqxLgrdWpTgh5mR3YF5wTbPTnSsHY,920
201
+ letta/serialize_schemas/tag.py,sha256=wbCTERsLqN5_bitTMwIuYjt-yZ4XmAewOwEN6ObtbNM,443
200
202
  letta/serialize_schemas/tool.py,sha256=x13LlwOvbAwnS7Jf5M1nOQCUnDhm4oTFY_mYJu3aJhM,392
201
203
  letta/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
202
204
  letta/server/constants.py,sha256=yAdGbLkzlOU_dLTx0lKDmAnj0ZgRXCEaIcPJWO69eaE,92
203
205
  letta/server/db.py,sha256=cA1MHpMCTTC1MX7VWppJ-cKq1XW93Vws_vTV0-bKmTE,3642
204
206
  letta/server/generate_openapi_schema.sh,sha256=0OtBhkC1g6CobVmNEd_m2B6sTdppjbJLXaM95icejvE,371
205
207
  letta/server/rest_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
206
- letta/server/rest_api/app.py,sha256=Di1FRxlgb056Bj9lncsGAc_HX9Cs8OntV9puT6463bs,12157
208
+ letta/server/rest_api/app.py,sha256=mQJf3KrthYrBTnzoomGIl3aLAhYVMPxnGaZJuoejUsM,12306
207
209
  letta/server/rest_api/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
208
210
  letta/server/rest_api/auth/index.py,sha256=fQBGyVylGSRfEMLQ17cZzrHd5Y1xiVylvPqH5Rl-lXQ,1378
209
211
  letta/server/rest_api/auth_token.py,sha256=725EFEIiNj4dh70hrSd94UysmFD8vcJLrTRfNHkzxDo,774
210
212
  letta/server/rest_api/chat_completions_interface.py,sha256=htY1v3eyP6OmoDkBYog2fPZX_2cHsNKkxUAAAvuUbiE,10862
211
- letta/server/rest_api/interface.py,sha256=jAt7azrk27sNKNCZHgoIzYDIUbEgJ8hsC3Ef7OevH7U,57605
213
+ letta/server/rest_api/interface.py,sha256=kjywwm4QFa8gVQRL1_X_0bKqeAqF-ncelGGxBmVJyvw,58089
212
214
  letta/server/rest_api/optimistic_json_parser.py,sha256=1z4d9unmxMb0ou7owJ62uUQoNjNYf21FmaNdg0ZcqUU,6567
213
215
  letta/server/rest_api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
214
216
  letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
215
217
  letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=yJo_oiv75QsD324lC0uR2B9su9WG1FxabQhAJKS--6s,5530
216
218
  letta/server/rest_api/routers/v1/__init__.py,sha256=Zi2th-okqT_RWAjB8MYGHX8CpHt1OSRyO5V8SJEp6UU,1361
217
- letta/server/rest_api/routers/v1/agents.py,sha256=I5V06th-lpUZ2fX2wtlYLD8SC2W1AkEIHRR4lr098YM,26658
219
+ letta/server/rest_api/routers/v1/agents.py,sha256=eA_gHr2fj2gpRaimwla9fNI1IXzhkgMQlxx37KQFIoM,29335
218
220
  letta/server/rest_api/routers/v1/blocks.py,sha256=0j7JX2BQzk31RyhvPZeEb-zh9ImXsVU4_8y5XMiR_WA,3900
219
221
  letta/server/rest_api/routers/v1/health.py,sha256=MoOjkydhGcJXTiuJrKIB0etVXiRMdTa51S8RQ8-50DQ,399
220
222
  letta/server/rest_api/routers/v1/identities.py,sha256=gRTcOXxbpiTzQzzVA_I1Sc9aUSKhW6YcQcJb__HxOX4,5957
@@ -246,7 +248,7 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
246
248
  letta/server/ws_api/protocol.py,sha256=5mDgpfNZn_kNwHnpt5Dsuw8gdNH298sgxTGed3etzYg,1836
247
249
  letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
248
250
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
- letta/services/agent_manager.py,sha256=VksHRO4kTYbymJhds9im5w2g9kfpIcCjG9X8ON9eWG0,57077
251
+ letta/services/agent_manager.py,sha256=ATqXxVKd-yQyD9rIaUd1JZVn-yBES-7-J4bngiUtak8,57903
250
252
  letta/services/block_manager.py,sha256=PkbiO59VimLyK6PolWR5O29uHxksCH6pP6K4Vkov3NA,5573
251
253
  letta/services/helpers/agent_manager_helper.py,sha256=uvQsmk2ZCAvG4AuI9r3Cs3IDe3Ra8X-qP3B3RbBVMtg,11179
252
254
  letta/services/helpers/tool_execution_helper.py,sha256=lLoebs1kZKjw62y1PxHbIDkHq_heJN2ZT0gKje-R8oo,6941
@@ -266,14 +268,14 @@ letta/services/summarizer/summarizer.py,sha256=u3WNTPAK0hSyUS8Jb6NDz-BWIZ7bEQj9R
266
268
  letta/services/tool_execution_sandbox.py,sha256=6AB3rFS34PzoyE9dxtUmuaUUWvKrwdE083NuBRa1eC0,22969
267
269
  letta/services/tool_manager.py,sha256=HJIDtXmBjoaAfHtuVDBi-j-L4akHelqjkPkeWlLfQo8,9555
268
270
  letta/services/user_manager.py,sha256=ScHbdJK9kNF8QXjsd3ZWGEL87n_Uyp3YwfKetOJmpHs,4304
269
- letta/settings.py,sha256=bDJUHGzcGs04LIvxnISwg27M_kSCRXntcj2cNcgXftc,6685
271
+ letta/settings.py,sha256=Z7Zlwk8bmk4RFl_8KMQMQIk_g8k6_ysDA2KvS0n8bjk,6840
270
272
  letta/streaming_interface.py,sha256=1vuAckIxo1p1UsXtDzE8LTUve5RoTZRdXUe-WBIYDWU,15818
271
273
  letta/streaming_utils.py,sha256=jLqFTVhUL76FeOuYk8TaRQHmPTf3HSRc2EoJwxJNK6U,11946
272
274
  letta/system.py,sha256=dnOrS2FlRMwijQnOvfrky0Lg8wEw-FUq2zzfAJOUSKA,8477
273
275
  letta/tracing.py,sha256=h_-c2lIKHmA7yCLOvgaHijMabmRC__FAl2rZtVKufUM,8017
274
276
  letta/utils.py,sha256=AdHrQ2OQ3V4XhJ1LtYwbLUO71j2IJY37cIUxXPgaaRY,32125
275
- letta_nightly-0.6.36.dev20250305104137.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
276
- letta_nightly-0.6.36.dev20250305104137.dist-info/METADATA,sha256=2Yf7W7UN5kl0LA74pEGoLyH8B_Qg9qTNJCX8fuvsakY,22627
277
- letta_nightly-0.6.36.dev20250305104137.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
278
- letta_nightly-0.6.36.dev20250305104137.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
279
- letta_nightly-0.6.36.dev20250305104137.dist-info/RECORD,,
277
+ letta_nightly-0.6.37.dev20250306104215.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
278
+ letta_nightly-0.6.37.dev20250306104215.dist-info/METADATA,sha256=UrT-o_jT-ERZNlw2UDgRpcuxxJH_gKdtJZDqn4Hmb1Q,22600
279
+ letta_nightly-0.6.37.dev20250306104215.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
280
+ letta_nightly-0.6.37.dev20250306104215.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
281
+ letta_nightly-0.6.37.dev20250306104215.dist-info/RECORD,,