letta-nightly 0.6.5.dev20241218104144__py3-none-any.whl → 0.6.5.dev20241219104153__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/agent.py CHANGED
@@ -20,7 +20,7 @@ from letta.constants import (
20
20
  REQ_HEARTBEAT_MESSAGE,
21
21
  STRUCTURED_OUTPUT_MODELS,
22
22
  )
23
- from letta.errors import LLMError
23
+ from letta.errors import ContextWindowExceededError
24
24
  from letta.helpers import ToolRulesSolver
25
25
  from letta.interface import AgentInterface
26
26
  from letta.llm_api.helpers import is_context_overflow_error
@@ -1094,6 +1094,7 @@ class Agent(BaseAgent):
1094
1094
 
1095
1095
  # If we got a context alert, try trimming the messages length, then try again
1096
1096
  if is_context_overflow_error(e):
1097
+ printd(f"context window exceeded with limit {self.agent_state.llm_config.context_window}, running summarizer to trim messages")
1097
1098
  # A separate API call to run a summarizer
1098
1099
  self.summarize_messages_inplace()
1099
1100
 
@@ -1169,8 +1170,13 @@ class Agent(BaseAgent):
1169
1170
 
1170
1171
  # If at this point there's nothing to summarize, throw an error
1171
1172
  if len(candidate_messages_to_summarize) == 0:
1172
- raise LLMError(
1173
- f"Summarize error: tried to run summarize, but couldn't find enough messages to compress [len={len(self.messages)}, preserve_N={MESSAGE_SUMMARY_TRUNC_KEEP_N_LAST}]"
1173
+ raise ContextWindowExceededError(
1174
+ "Not enough messages to compress for summarization",
1175
+ details={
1176
+ "num_candidate_messages": len(candidate_messages_to_summarize),
1177
+ "num_total_messages": len(self.messages),
1178
+ "preserve_N": MESSAGE_SUMMARY_TRUNC_KEEP_N_LAST,
1179
+ },
1174
1180
  )
1175
1181
 
1176
1182
  # Walk down the message buffer (front-to-back) until we hit the target token count
@@ -1204,8 +1210,13 @@ class Agent(BaseAgent):
1204
1210
  message_sequence_to_summarize = self._messages[1:cutoff] # do NOT get rid of the system message
1205
1211
  if len(message_sequence_to_summarize) <= 1:
1206
1212
  # This prevents a potential infinite loop of summarizing the same message over and over
1207
- raise LLMError(
1208
- f"Summarize error: tried to run summarize, but couldn't find enough messages to compress [len={len(message_sequence_to_summarize)} <= 1]"
1213
+ raise ContextWindowExceededError(
1214
+ "Not enough messages to compress for summarization after determining cutoff",
1215
+ details={
1216
+ "num_candidate_messages": len(message_sequence_to_summarize),
1217
+ "num_total_messages": len(self.messages),
1218
+ "preserve_N": MESSAGE_SUMMARY_TRUNC_KEEP_N_LAST,
1219
+ },
1209
1220
  )
1210
1221
  else:
1211
1222
  printd(f"Attempting to summarize {len(message_sequence_to_summarize)} messages [1:{cutoff}] of {len(self._messages)}")
@@ -1218,6 +1229,7 @@ class Agent(BaseAgent):
1218
1229
  self.agent_state.llm_config.context_window = (
1219
1230
  LLM_MAX_TOKENS[self.model] if (self.model is not None and self.model in LLM_MAX_TOKENS) else LLM_MAX_TOKENS["DEFAULT"]
1220
1231
  )
1232
+
1221
1233
  summary = summarize_messages(agent_state=self.agent_state, message_sequence_to_summarize=message_sequence_to_summarize)
1222
1234
  printd(f"Got summary: {summary}")
1223
1235
 
letta/client/client.py CHANGED
@@ -233,7 +233,7 @@ class AbstractClient(object):
233
233
  def get_tool_id(self, name: str) -> Optional[str]:
234
234
  raise NotImplementedError
235
235
 
236
- def add_base_tools(self) -> List[Tool]:
236
+ def upsert_base_tools(self) -> List[Tool]:
237
237
  raise NotImplementedError
238
238
 
239
239
  def load_data(self, connector: DataConnector, source_name: str):
@@ -1466,7 +1466,7 @@ class RESTClient(AbstractClient):
1466
1466
  raise ValueError(f"Failed to get tool: {response.text}")
1467
1467
  return response.json()
1468
1468
 
1469
- def add_base_tools(self) -> List[Tool]:
1469
+ def upsert_base_tools(self) -> List[Tool]:
1470
1470
  response = requests.post(f"{self.base_url}/{self.api_prefix}/tools/add-base-tools/", headers=self.headers)
1471
1471
  if response.status_code != 200:
1472
1472
  raise ValueError(f"Failed to add base tools: {response.text}")
letta/errors.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from enum import Enum
2
3
  from typing import TYPE_CHECKING, List, Optional, Union
3
4
 
4
5
  # Avoid circular imports
@@ -6,9 +7,31 @@ if TYPE_CHECKING:
6
7
  from letta.schemas.message import Message
7
8
 
8
9
 
10
+ class ErrorCode(Enum):
11
+ """Enum for error codes used by client."""
12
+
13
+ INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
14
+ CONTEXT_WINDOW_EXCEEDED = "CONTEXT_WINDOW_EXCEEDED"
15
+ RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"
16
+
17
+
9
18
  class LettaError(Exception):
10
19
  """Base class for all Letta related errors."""
11
20
 
21
+ def __init__(self, message: str, code: Optional[ErrorCode] = None, details: dict = {}):
22
+ self.message = message
23
+ self.code = code
24
+ self.details = details
25
+ super().__init__(message)
26
+
27
+ def __str__(self) -> str:
28
+ if self.code:
29
+ return f"{self.code.value}: {self.message}"
30
+ return self.message
31
+
32
+ def __repr__(self) -> str:
33
+ return f"{self.__class__.__name__}(message='{self.message}', code='{self.code}', details={self.details})"
34
+
12
35
 
13
36
  class LettaToolCreateError(LettaError):
14
37
  """Error raised when a tool cannot be created."""
@@ -16,10 +39,7 @@ class LettaToolCreateError(LettaError):
16
39
  default_error_message = "Error creating tool."
17
40
 
18
41
  def __init__(self, message=None):
19
- if message is None:
20
- message = self.default_error_message
21
- self.message = message
22
- super().__init__(self.message)
42
+ super().__init__(message=message or self.default_error_message)
23
43
 
24
44
 
25
45
  class LettaConfigurationError(LettaError):
@@ -27,23 +47,17 @@ class LettaConfigurationError(LettaError):
27
47
 
28
48
  def __init__(self, message: str, missing_fields: Optional[List[str]] = None):
29
49
  self.missing_fields = missing_fields or []
30
- super().__init__(message)
50
+ super().__init__(message=message, details={"missing_fields": self.missing_fields})
31
51
 
32
52
 
33
53
  class LettaAgentNotFoundError(LettaError):
34
54
  """Error raised when an agent is not found."""
35
-
36
- def __init__(self, message: str):
37
- self.message = message
38
- super().__init__(self.message)
55
+ pass
39
56
 
40
57
 
41
58
  class LettaUserNotFoundError(LettaError):
42
59
  """Error raised when a user is not found."""
43
-
44
- def __init__(self, message: str):
45
- self.message = message
46
- super().__init__(self.message)
60
+ pass
47
61
 
48
62
 
49
63
  class LLMError(LettaError):
@@ -54,24 +68,45 @@ class LLMJSONParsingError(LettaError):
54
68
  """Exception raised for errors in the JSON parsing process."""
55
69
 
56
70
  def __init__(self, message="Error parsing JSON generated by LLM"):
57
- self.message = message
58
- super().__init__(self.message)
71
+ super().__init__(message=message)
59
72
 
60
73
 
61
74
  class LocalLLMError(LettaError):
62
75
  """Generic catch-all error for local LLM problems"""
63
76
 
64
77
  def __init__(self, message="Encountered an error while running local LLM"):
65
- self.message = message
66
- super().__init__(self.message)
78
+ super().__init__(message=message)
67
79
 
68
80
 
69
81
  class LocalLLMConnectionError(LettaError):
70
82
  """Error for when local LLM cannot be reached with provided IP/port"""
71
83
 
72
84
  def __init__(self, message="Could not connect to local LLM"):
73
- self.message = message
74
- super().__init__(self.message)
85
+ super().__init__(message=message)
86
+
87
+
88
+ class ContextWindowExceededError(LettaError):
89
+ """Error raised when the context window is exceeded but further summarization fails."""
90
+
91
+ def __init__(self, message: str, details: dict = {}):
92
+ error_message = f"{message} ({details})"
93
+ super().__init__(
94
+ message=error_message,
95
+ code=ErrorCode.CONTEXT_WINDOW_EXCEEDED,
96
+ details=details,
97
+ )
98
+
99
+
100
+ class RateLimitExceededError(LettaError):
101
+ """Error raised when the llm rate limiter throttles api requests."""
102
+
103
+ def __init__(self, message: str, max_retries: int):
104
+ error_message = f"{message} ({max_retries})"
105
+ super().__init__(
106
+ message=error_message,
107
+ code=ErrorCode.RATE_LIMIT_EXCEEDED,
108
+ details={"max_retries": max_retries},
109
+ )
75
110
 
76
111
 
77
112
  class LettaMessageError(LettaError):
@@ -61,60 +61,6 @@ def conversation_search(self: "Agent", query: str, page: Optional[int] = 0) -> O
61
61
  return results_str
62
62
 
63
63
 
64
- def conversation_search_date(self: "Agent", start_date: str, end_date: str, page: Optional[int] = 0) -> Optional[str]:
65
- """
66
- Search prior conversation history using a date range.
67
-
68
- Args:
69
- start_date (str): The start of the date range to search, in the format 'YYYY-MM-DD'.
70
- end_date (str): The end of the date range to search, in the format 'YYYY-MM-DD'.
71
- page (int): Allows you to page through results. Only use on a follow-up query. Defaults to 0 (first page).
72
-
73
- Returns:
74
- str: Query result string
75
- """
76
- import math
77
- from datetime import datetime
78
-
79
- from letta.constants import RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE
80
- from letta.utils import json_dumps
81
-
82
- if page is None or (isinstance(page, str) and page.lower().strip() == "none"):
83
- page = 0
84
- try:
85
- page = int(page)
86
- if page < 0:
87
- raise ValueError
88
- except:
89
- raise ValueError(f"'page' argument must be an integer")
90
-
91
- # Convert date strings to datetime objects
92
- try:
93
- start_datetime = datetime.strptime(start_date, "%Y-%m-%d").replace(hour=0, minute=0, second=0, microsecond=0)
94
- end_datetime = datetime.strptime(end_date, "%Y-%m-%d").replace(hour=23, minute=59, second=59, microsecond=999999)
95
- except ValueError:
96
- raise ValueError("Dates must be in the format 'YYYY-MM-DD'")
97
-
98
- count = RETRIEVAL_QUERY_DEFAULT_PAGE_SIZE
99
- results = self.message_manager.list_user_messages_for_agent(
100
- # TODO: add paging by page number. currently cursor only works with strings.
101
- agent_id=self.agent_state.id,
102
- actor=self.user,
103
- start_date=start_datetime,
104
- end_date=end_datetime,
105
- limit=count,
106
- )
107
- total = len(results)
108
- num_pages = math.ceil(total / count) - 1 # 0 index
109
- if len(results) == 0:
110
- results_str = f"No results found."
111
- else:
112
- results_pref = f"Showing {len(results)} of {total} results (page {page}/{num_pages}):"
113
- results_formatted = [f"timestamp: {d['timestamp']}, {d['message']['role']} - {d['message']['content']}" for d in results]
114
- results_str = f"{results_pref} {json_dumps(results_formatted)}"
115
- return results_str
116
-
117
-
118
64
  def archival_memory_insert(self: "Agent", content: str) -> Optional[str]:
119
65
  """
120
66
  Add to archival memory. Make sure to phrase the memory contents such that it can be easily queried later.
@@ -5,7 +5,7 @@ from typing import List, Optional, Union
5
5
  import requests
6
6
 
7
7
  from letta.constants import CLI_WARNING_PREFIX
8
- from letta.errors import LettaConfigurationError
8
+ from letta.errors import LettaConfigurationError, RateLimitExceededError
9
9
  from letta.llm_api.anthropic import anthropic_chat_completions_request
10
10
  from letta.llm_api.azure_openai import azure_openai_chat_completions_request
11
11
  from letta.llm_api.google_ai import (
@@ -80,7 +80,7 @@ def retry_with_exponential_backoff(
80
80
 
81
81
  # Check if max retries has been reached
82
82
  if num_retries > max_retries:
83
- raise Exception(f"Maximum number of retries ({max_retries}) exceeded.")
83
+ raise RateLimitExceededError("Maximum number of retries exceeded", max_retries=max_retries)
84
84
 
85
85
  # Increment the delay
86
86
  delay *= exponential_base * (1 + jitter * random.random())
letta/orm/message.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
+ from sqlalchemy import Index
3
4
  from sqlalchemy.orm import Mapped, mapped_column, relationship
4
5
 
5
6
  from letta.orm.custom_columns import ToolCallColumn
@@ -13,7 +14,7 @@ class Message(SqlalchemyBase, OrganizationMixin, AgentMixin):
13
14
  """Defines data model for storing Message objects"""
14
15
 
15
16
  __tablename__ = "messages"
16
- __table_args__ = {"extend_existing": True}
17
+ __table_args__ = (Index("ix_messages_agent_created_at", "agent_id", "created_at"),)
17
18
  __pydantic_model__ = PydanticMessage
18
19
 
19
20
  id: Mapped[str] = mapped_column(primary_key=True, doc="Unique message identifier")
letta/orm/passage.py CHANGED
@@ -1,26 +1,26 @@
1
1
  from typing import TYPE_CHECKING
2
- from sqlalchemy import Column, JSON, Index
3
- from sqlalchemy.orm import Mapped, mapped_column, relationship, declared_attr
4
2
 
5
- from letta.orm.mixins import FileMixin, OrganizationMixin
3
+ from sqlalchemy import JSON, Column, Index
4
+ from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship
5
+
6
+ from letta.config import LettaConfig
7
+ from letta.constants import MAX_EMBEDDING_DIM
6
8
  from letta.orm.custom_columns import CommonVector, EmbeddingConfigColumn
7
- from letta.orm.sqlalchemy_base import SqlalchemyBase
8
9
  from letta.orm.mixins import AgentMixin, FileMixin, OrganizationMixin, SourceMixin
10
+ from letta.orm.sqlalchemy_base import SqlalchemyBase
9
11
  from letta.schemas.passage import Passage as PydanticPassage
10
12
  from letta.settings import settings
11
13
 
12
- from letta.config import LettaConfig
13
- from letta.constants import MAX_EMBEDDING_DIM
14
-
15
14
  config = LettaConfig()
16
15
 
17
16
  if TYPE_CHECKING:
18
- from letta.orm.organization import Organization
19
17
  from letta.orm.agent import Agent
18
+ from letta.orm.organization import Organization
20
19
 
21
20
 
22
21
  class BasePassage(SqlalchemyBase, OrganizationMixin):
23
22
  """Base class for all passage types with common fields"""
23
+
24
24
  __abstract__ = True
25
25
  __pydantic_model__ = PydanticPassage
26
26
 
@@ -45,17 +45,15 @@ class BasePassage(SqlalchemyBase, OrganizationMixin):
45
45
  @declared_attr
46
46
  def __table_args__(cls):
47
47
  if settings.letta_pg_uri_no_default:
48
- return (
49
- Index(f'{cls.__tablename__}_org_idx', 'organization_id'),
50
- {"extend_existing": True}
51
- )
48
+ return (Index(f"{cls.__tablename__}_org_idx", "organization_id"), {"extend_existing": True})
52
49
  return ({"extend_existing": True},)
53
50
 
54
51
 
55
52
  class SourcePassage(BasePassage, FileMixin, SourceMixin):
56
53
  """Passages derived from external files/sources"""
54
+
57
55
  __tablename__ = "source_passages"
58
-
56
+
59
57
  @declared_attr
60
58
  def file(cls) -> Mapped["FileMetadata"]:
61
59
  """Relationship to file"""
@@ -64,7 +62,7 @@ class SourcePassage(BasePassage, FileMixin, SourceMixin):
64
62
  @declared_attr
65
63
  def organization(cls) -> Mapped["Organization"]:
66
64
  return relationship("Organization", back_populates="source_passages", lazy="selectin")
67
-
65
+
68
66
  @declared_attr
69
67
  def source(cls) -> Mapped["Source"]:
70
68
  """Relationship to source"""
@@ -73,8 +71,9 @@ class SourcePassage(BasePassage, FileMixin, SourceMixin):
73
71
 
74
72
  class AgentPassage(BasePassage, AgentMixin):
75
73
  """Passages created by agents as archival memories"""
74
+
76
75
  __tablename__ = "agent_passages"
77
-
76
+
78
77
  @declared_attr
79
78
  def organization(cls) -> Mapped["Organization"]:
80
79
  return relationship("Organization", back_populates="agent_passages", lazy="selectin")
@@ -17,6 +17,7 @@ from fastapi.responses import JSONResponse, StreamingResponse
17
17
  from pydantic import Field
18
18
 
19
19
  from letta.constants import DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG
20
+ from letta.log import get_logger
20
21
  from letta.orm.errors import NoResultFound
21
22
  from letta.schemas.agent import AgentState, CreateAgent, UpdateAgent
22
23
  from letta.schemas.block import ( # , BlockLabelUpdate, BlockLimitUpdate
@@ -54,6 +55,8 @@ from letta.server.server import SyncServer
54
55
 
55
56
  router = APIRouter(prefix="/agents", tags=["agents"])
56
57
 
58
+ logger = get_logger(__name__)
59
+
57
60
 
58
61
  # TODO: This should be paginated
59
62
  @router.get("/", response_model=List[AgentState], operation_id="list_agents")
@@ -453,6 +456,13 @@ def get_agent_messages(
453
456
  """
454
457
  actor = server.user_manager.get_user_or_default(user_id=user_id)
455
458
 
459
+ # TODO: Temporary debugging logs for debugging very slow endpoint
460
+ import uuid
461
+
462
+ temp_rand_uuid = uuid.uuid4()
463
+
464
+ logger.info(f"[{temp_rand_uuid}] RECEIVED GET /messages for agent_id={agent_id} before={before} limit={limit}")
465
+
456
466
  return server.get_agent_recall_cursor(
457
467
  user_id=actor.id,
458
468
  agent_id=agent_id,
@@ -462,6 +472,7 @@ def get_agent_messages(
462
472
  return_message_object=msg_object,
463
473
  assistant_message_tool_name=assistant_message_tool_name,
464
474
  assistant_message_tool_kwarg=assistant_message_tool_kwarg,
475
+ temp_rand_uuid=temp_rand_uuid,
465
476
  )
466
477
 
467
478
 
@@ -152,30 +152,15 @@ def update_tool(
152
152
 
153
153
 
154
154
  @router.post("/add-base-tools", response_model=List[Tool], operation_id="add_base_tools")
155
- def add_base_tools(
155
+ def upsert_base_tools(
156
156
  server: SyncServer = Depends(get_letta_server),
157
157
  user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
158
158
  ):
159
159
  """
160
- Add base tools
160
+ Upsert base tools
161
161
  """
162
162
  actor = server.user_manager.get_user_or_default(user_id=user_id)
163
- return server.tool_manager.add_base_tools(actor=actor)
164
-
165
-
166
- # NOTE: can re-enable if needed
167
- # @router.post("/{tool_id}/run", response_model=FunctionReturn, operation_id="run_tool")
168
- # def run_tool(
169
- # server: SyncServer = Depends(get_letta_server),
170
- # request: ToolRun = Body(...),
171
- # user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
172
- # ):
173
- # """
174
- # Run an existing tool on provided arguments
175
- # """
176
- # actor = server.user_manager.get_user_or_default(user_id=user_id)
177
-
178
- # return server.run_tool(tool_id=request.tool_id, tool_args=request.tool_args, user_id=actor.id)
163
+ return server.tool_manager.upsert_base_tools(actor=actor)
179
164
 
180
165
 
181
166
  @router.post("/run", response_model=FunctionReturn, operation_id="run_tool_from_source")
@@ -8,6 +8,7 @@ from typing import AsyncGenerator, Optional, Union
8
8
  from fastapi import Header
9
9
  from pydantic import BaseModel
10
10
 
11
+ from letta.errors import ContextWindowExceededError, RateLimitExceededError
11
12
  from letta.schemas.usage import LettaUsageStatistics
12
13
  from letta.server.rest_api.interface import StreamingServerInterface
13
14
  from letta.server.server import SyncServer
@@ -61,34 +62,21 @@ async def sse_async_generator(
61
62
  if not isinstance(usage, LettaUsageStatistics):
62
63
  raise ValueError(f"Expected LettaUsageStatistics, got {type(usage)}")
63
64
  yield sse_formatter({"usage": usage.model_dump()})
64
- except Exception as e:
65
- import traceback
66
-
67
- traceback.print_exc()
68
- warnings.warn(f"SSE stream generator failed: {e}")
69
65
 
70
- # Log the error, since the exception handler upstack (in FastAPI) won't catch it, because this may be a 200 response
71
- # Print the stack trace
72
- if (os.getenv("SENTRY_DSN") is not None) and (os.getenv("SENTRY_DSN") != ""):
73
- import sentry_sdk
66
+ except ContextWindowExceededError as e:
67
+ log_error_to_sentry(e)
68
+ yield sse_formatter({"error": f"Stream failed: {e}", "code": str(e.code.value) if e.code else None})
74
69
 
75
- sentry_sdk.capture_exception(e)
70
+ except RateLimitExceededError as e:
71
+ log_error_to_sentry(e)
72
+ yield sse_formatter({"error": f"Stream failed: {e}", "code": str(e.code.value) if e.code else None})
76
73
 
74
+ except Exception as e:
75
+ log_error_to_sentry(e)
77
76
  yield sse_formatter({"error": f"Stream failed (internal error occured)"})
78
77
 
79
78
  except Exception as e:
80
- import traceback
81
-
82
- traceback.print_exc()
83
- warnings.warn(f"SSE stream generator failed: {e}")
84
-
85
- # Log the error, since the exception handler upstack (in FastAPI) won't catch it, because this may be a 200 response
86
- # Print the stack trace
87
- if (os.getenv("SENTRY_DSN") is not None) and (os.getenv("SENTRY_DSN") != ""):
88
- import sentry_sdk
89
-
90
- sentry_sdk.capture_exception(e)
91
-
79
+ log_error_to_sentry(e)
92
80
  yield sse_formatter({"error": "Stream failed (decoder encountered an error)"})
93
81
 
94
82
  finally:
@@ -113,3 +101,16 @@ def get_user_id(user_id: Optional[str] = Header(None, alias="user_id")) -> Optio
113
101
 
114
102
  def get_current_interface() -> StreamingServerInterface:
115
103
  return StreamingServerInterface
104
+
105
+ def log_error_to_sentry(e):
106
+ import traceback
107
+
108
+ traceback.print_exc()
109
+ warnings.warn(f"SSE stream generator failed: {e}")
110
+
111
+ # Log the error, since the exception handler upstack (in FastAPI) won't catch it, because this may be a 200 response
112
+ # Print the stack trace
113
+ if (os.getenv("SENTRY_DSN") is not None) and (os.getenv("SENTRY_DSN") != ""):
114
+ import sentry_sdk
115
+
116
+ sentry_sdk.capture_exception(e)
letta/server/server.py CHANGED
@@ -301,7 +301,7 @@ class SyncServer(Server):
301
301
  self.default_org = self.organization_manager.create_default_organization()
302
302
  self.default_user = self.user_manager.create_default_user()
303
303
  self.block_manager.add_default_blocks(actor=self.default_user)
304
- self.tool_manager.add_base_tools(actor=self.default_user)
304
+ self.tool_manager.upsert_base_tools(actor=self.default_user)
305
305
 
306
306
  # If there is a default org/user
307
307
  # This logic may have to change in the future
@@ -979,16 +979,32 @@ class SyncServer(Server):
979
979
  return_message_object: bool = True,
980
980
  assistant_message_tool_name: str = constants.DEFAULT_MESSAGE_TOOL,
981
981
  assistant_message_tool_kwarg: str = constants.DEFAULT_MESSAGE_TOOL_KWARG,
982
+ temp_rand_uuid: Optional[str] = None,
982
983
  ) -> Union[List[Message], List[LettaMessage]]:
983
984
  # TODO: Thread actor directly through this function, since the top level caller most likely already retrieved the user
985
+ import datetime
986
+
987
+ start_time = datetime.datetime.utcnow()
988
+
989
+ logger.info(
990
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Fetching actor for user_id={user_id} (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
991
+ )
984
992
  actor = self.user_manager.get_user_or_default(user_id=user_id)
985
993
 
986
- # Get the agent object (loaded in memory)
994
+ logger.info(
995
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Loading agent object for agent_id={agent_id} (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
996
+ )
987
997
  letta_agent = self.load_agent(agent_id=agent_id, actor=actor)
988
998
 
989
- # iterate over records
999
+ logger.info(
1000
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Resolving start_date and end_date for filtering messages (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1001
+ )
990
1002
  start_date = self.message_manager.get_message_by_id(after, actor=actor).created_at if after else None
991
1003
  end_date = self.message_manager.get_message_by_id(before, actor=actor).created_at if before else None
1004
+
1005
+ logger.info(
1006
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Fetching messages for agent_id={agent_id}, start_date={start_date}, end_date={end_date}, limit={limit}, reverse={reverse} (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1007
+ )
992
1008
  records = letta_agent.message_manager.list_messages_for_agent(
993
1009
  agent_id=agent_id,
994
1010
  actor=actor,
@@ -998,10 +1014,15 @@ class SyncServer(Server):
998
1014
  ascending=not reverse,
999
1015
  )
1000
1016
 
1017
+ logger.info(
1018
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Validating message types (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1019
+ )
1001
1020
  assert all(isinstance(m, Message) for m in records)
1002
1021
 
1003
1022
  if not return_message_object:
1004
- # If we're GETing messages in reverse, we need to reverse the inner list (generated by to_letta_message)
1023
+ logger.info(
1024
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Converting messages to LettaMessage objects (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1025
+ )
1005
1026
  records = [
1006
1027
  msg
1007
1028
  for m in records
@@ -1012,8 +1033,14 @@ class SyncServer(Server):
1012
1033
  ]
1013
1034
 
1014
1035
  if reverse:
1036
+ logger.info(
1037
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Reversing message order (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1038
+ )
1015
1039
  records = records[::-1]
1016
1040
 
1041
+ logger.info(
1042
+ f"[{temp_rand_uuid}] {datetime.datetime.utcnow()} - Returning {len(records)} messages (Elapsed: {(datetime.datetime.utcnow() - start_time).total_seconds()}s)"
1043
+ )
1017
1044
  return records
1018
1045
 
1019
1046
  def get_server_config(self, include_defaults: bool = False) -> dict:
@@ -1,18 +1,18 @@
1
- from typing import Dict, List, Optional
2
1
  from datetime import datetime
3
- import numpy as np
2
+ from typing import Dict, List, Optional
4
3
 
5
- from sqlalchemy import select, union_all, literal, func, Select
4
+ import numpy as np
5
+ from sqlalchemy import Select, func, literal, select, union_all
6
6
 
7
7
  from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS, MAX_EMBEDDING_DIM
8
8
  from letta.embeddings import embedding_model
9
9
  from letta.log import get_logger
10
10
  from letta.orm import Agent as AgentModel
11
+ from letta.orm import AgentPassage
11
12
  from letta.orm import Block as BlockModel
12
13
  from letta.orm import Source as SourceModel
14
+ from letta.orm import SourcePassage, SourcesAgents
13
15
  from letta.orm import Tool as ToolModel
14
- from letta.orm import AgentPassage, SourcePassage
15
- from letta.orm import SourcesAgents
16
16
  from letta.orm.errors import NoResultFound
17
17
  from letta.orm.sqlite_functions import adapt_array
18
18
  from letta.schemas.agent import AgentState as PydanticAgentState
@@ -77,6 +77,8 @@ class AgentManager:
77
77
  tool_names.extend(BASE_TOOLS + BASE_MEMORY_TOOLS)
78
78
  if agent_create.tools:
79
79
  tool_names.extend(agent_create.tools)
80
+ # Remove duplicates
81
+ tool_names = list(set(tool_names))
80
82
 
81
83
  tool_ids = agent_create.tool_ids or []
82
84
  for tool_name in tool_names:
@@ -431,7 +433,7 @@ class AgentManager:
431
433
  agent_only: bool = False,
432
434
  ) -> Select:
433
435
  """Helper function to build the base passage query with all filters applied.
434
-
436
+
435
437
  Returns the query before any limit or count operations are applied.
436
438
  """
437
439
  embedded_text = None
@@ -448,21 +450,14 @@ class AgentManager:
448
450
  if not agent_only: # Include source passages
449
451
  if agent_id is not None:
450
452
  source_passages = (
451
- select(
452
- SourcePassage,
453
- literal(None).label('agent_id')
454
- )
453
+ select(SourcePassage, literal(None).label("agent_id"))
455
454
  .join(SourcesAgents, SourcesAgents.source_id == SourcePassage.source_id)
456
455
  .where(SourcesAgents.agent_id == agent_id)
457
456
  .where(SourcePassage.organization_id == actor.organization_id)
458
457
  )
459
458
  else:
460
- source_passages = (
461
- select(
462
- SourcePassage,
463
- literal(None).label('agent_id')
464
- )
465
- .where(SourcePassage.organization_id == actor.organization_id)
459
+ source_passages = select(SourcePassage, literal(None).label("agent_id")).where(
460
+ SourcePassage.organization_id == actor.organization_id
466
461
  )
467
462
 
468
463
  if source_id:
@@ -486,9 +481,9 @@ class AgentManager:
486
481
  AgentPassage._created_by_id,
487
482
  AgentPassage._last_updated_by_id,
488
483
  AgentPassage.organization_id,
489
- literal(None).label('file_id'),
490
- literal(None).label('source_id'),
491
- AgentPassage.agent_id
484
+ literal(None).label("file_id"),
485
+ literal(None).label("source_id"),
486
+ AgentPassage.agent_id,
492
487
  )
493
488
  .where(AgentPassage.agent_id == agent_id)
494
489
  .where(AgentPassage.organization_id == actor.organization_id)
@@ -496,11 +491,11 @@ class AgentManager:
496
491
 
497
492
  # Combine queries
498
493
  if source_passages is not None and agent_passages is not None:
499
- combined_query = union_all(source_passages, agent_passages).cte('combined_passages')
494
+ combined_query = union_all(source_passages, agent_passages).cte("combined_passages")
500
495
  elif agent_passages is not None:
501
- combined_query = agent_passages.cte('combined_passages')
496
+ combined_query = agent_passages.cte("combined_passages")
502
497
  elif source_passages is not None:
503
- combined_query = source_passages.cte('combined_passages')
498
+ combined_query = source_passages.cte("combined_passages")
504
499
  else:
505
500
  raise ValueError("No passages found")
506
501
 
@@ -521,9 +516,7 @@ class AgentManager:
521
516
  if embedded_text:
522
517
  if settings.letta_pg_uri_no_default:
523
518
  # PostgreSQL with pgvector
524
- main_query = main_query.order_by(
525
- combined_query.c.embedding.cosine_distance(embedded_text).asc()
526
- )
519
+ main_query = main_query.order_by(combined_query.c.embedding.cosine_distance(embedded_text).asc())
527
520
  else:
528
521
  # SQLite with custom vector type
529
522
  query_embedding_binary = adapt_array(embedded_text)
@@ -531,13 +524,13 @@ class AgentManager:
531
524
  main_query = main_query.order_by(
532
525
  func.cosine_distance(combined_query.c.embedding, query_embedding_binary).asc(),
533
526
  combined_query.c.created_at.asc(),
534
- combined_query.c.id.asc()
527
+ combined_query.c.id.asc(),
535
528
  )
536
529
  else:
537
530
  main_query = main_query.order_by(
538
531
  func.cosine_distance(combined_query.c.embedding, query_embedding_binary).asc(),
539
532
  combined_query.c.created_at.desc(),
540
- combined_query.c.id.asc()
533
+ combined_query.c.id.asc(),
541
534
  )
542
535
  else:
543
536
  if query_text:
@@ -545,18 +538,12 @@ class AgentManager:
545
538
 
546
539
  # Handle cursor-based pagination
547
540
  if cursor:
548
- cursor_query = select(combined_query.c.created_at).where(
549
- combined_query.c.id == cursor
550
- ).scalar_subquery()
551
-
541
+ cursor_query = select(combined_query.c.created_at).where(combined_query.c.id == cursor).scalar_subquery()
542
+
552
543
  if ascending:
553
- main_query = main_query.where(
554
- combined_query.c.created_at > cursor_query
555
- )
544
+ main_query = main_query.where(combined_query.c.created_at > cursor_query)
556
545
  else:
557
- main_query = main_query.where(
558
- combined_query.c.created_at < cursor_query
559
- )
546
+ main_query = main_query.where(combined_query.c.created_at < cursor_query)
560
547
 
561
548
  # Add ordering if not already ordered by similarity
562
549
  if not embed_query:
@@ -588,7 +575,7 @@ class AgentManager:
588
575
  embed_query: bool = False,
589
576
  ascending: bool = True,
590
577
  embedding_config: Optional[EmbeddingConfig] = None,
591
- agent_only: bool = False
578
+ agent_only: bool = False,
592
579
  ) -> List[PydanticPassage]:
593
580
  """Lists all passages attached to an agent."""
594
581
  with self.session_maker() as session:
@@ -617,19 +604,18 @@ class AgentManager:
617
604
  passages = []
618
605
  for row in results:
619
606
  data = dict(row._mapping)
620
- if data['agent_id'] is not None:
607
+ if data["agent_id"] is not None:
621
608
  # This is an AgentPassage - remove source fields
622
- data.pop('source_id', None)
623
- data.pop('file_id', None)
609
+ data.pop("source_id", None)
610
+ data.pop("file_id", None)
624
611
  passage = AgentPassage(**data)
625
612
  else:
626
613
  # This is a SourcePassage - remove agent field
627
- data.pop('agent_id', None)
614
+ data.pop("agent_id", None)
628
615
  passage = SourcePassage(**data)
629
616
  passages.append(passage)
630
-
631
- return [p.to_pydantic() for p in passages]
632
617
 
618
+ return [p.to_pydantic() for p in passages]
633
619
 
634
620
  @enforce_types
635
621
  def passage_size(
@@ -645,7 +631,7 @@ class AgentManager:
645
631
  embed_query: bool = False,
646
632
  ascending: bool = True,
647
633
  embedding_config: Optional[EmbeddingConfig] = None,
648
- agent_only: bool = False
634
+ agent_only: bool = False,
649
635
  ) -> int:
650
636
  """Returns the count of passages matching the given criteria."""
651
637
  with self.session_maker() as session:
@@ -663,7 +649,7 @@ class AgentManager:
663
649
  embedding_config=embedding_config,
664
650
  agent_only=agent_only,
665
651
  )
666
-
652
+
667
653
  # Convert to count query
668
654
  count_query = select(func.count()).select_from(main_query.subquery())
669
655
  return session.scalar(count_query) or 0
@@ -3,6 +3,7 @@ import inspect
3
3
  import warnings
4
4
  from typing import List, Optional
5
5
 
6
+ from letta.constants import BASE_MEMORY_TOOLS, BASE_TOOLS
6
7
  from letta.functions.functions import derive_openai_json_schema, load_function_set
7
8
 
8
9
  # TODO: Remove this once we translate all of these to the ORM
@@ -20,7 +21,6 @@ class ToolManager:
20
21
  BASE_TOOL_NAMES = [
21
22
  "send_message",
22
23
  "conversation_search",
23
- "conversation_search_date",
24
24
  "archival_memory_insert",
25
25
  "archival_memory_search",
26
26
  ]
@@ -133,7 +133,7 @@ class ToolManager:
133
133
  raise ValueError(f"Tool with id {tool_id} not found.")
134
134
 
135
135
  @enforce_types
136
- def add_base_tools(self, actor: PydanticUser) -> List[PydanticTool]:
136
+ def upsert_base_tools(self, actor: PydanticUser) -> List[PydanticTool]:
137
137
  """Add default tools in base.py"""
138
138
  module_name = "base"
139
139
  full_module_name = f"letta.functions.function_sets.{module_name}"
@@ -154,7 +154,7 @@ class ToolManager:
154
154
  # create tool in db
155
155
  tools = []
156
156
  for name, schema in functions_to_schema.items():
157
- if name in self.BASE_TOOL_NAMES + self.BASE_MEMORY_TOOL_NAMES:
157
+ if name in BASE_TOOLS + BASE_MEMORY_TOOLS:
158
158
  # print([str(inspect.getsource(line)) for line in schema["imports"]])
159
159
  source_code = inspect.getsource(schema["python_function"])
160
160
  tags = [module_name]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.6.5.dev20241218104144
3
+ Version: 0.6.5.dev20241219104153
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -23,8 +23,8 @@ Requires-Dist: alembic (>=1.13.3,<2.0.0)
23
23
  Requires-Dist: autoflake (>=2.3.0,<3.0.0) ; extra == "dev" or extra == "all"
24
24
  Requires-Dist: black[jupyter] (>=24.2.0,<25.0.0) ; extra == "dev" or extra == "all"
25
25
  Requires-Dist: brotli (>=1.1.0,<2.0.0)
26
- Requires-Dist: composio-core (>=0.5.51,<0.6.0)
27
- Requires-Dist: composio-langchain (>=0.5.28,<0.6.0)
26
+ Requires-Dist: composio-core (>=0.6.3,<0.7.0)
27
+ Requires-Dist: composio-langchain (>=0.6.3,<0.7.0)
28
28
  Requires-Dist: datasets (>=2.14.6,<3.0.0) ; extra == "dev" or extra == "all"
29
29
  Requires-Dist: demjson3 (>=3.0.6,<4.0.0)
30
30
  Requires-Dist: docker (>=7.1.0,<8.0.0) ; extra == "external-tools" or extra == "all"
@@ -1,6 +1,6 @@
1
1
  letta/__init__.py,sha256=zY_5By7t8Jgl0Nejq2i4yOTJQg0lzJNlwdmp8sB-W9Y,1014
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=I4qbwYEkq9ZXYb_ToRRdGfNnMIkeI715vXRziQu731Y,77721
3
+ letta/agent.py,sha256=qFWmRmpjBtIA-fQ3zhStLeHb69vvz4Hf1tygvAOrJqw,78266
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=3wBCzddcfF6IMbPdDtTZFze5-HJSYxHd8w6jkkSwzsw,4628
@@ -8,7 +8,7 @@ letta/cli/cli.py,sha256=N6jCmysldhAGTQPkGDmRVMAIID7GlgECXqarcMV5h3M,16502
8
8
  letta/cli/cli_config.py,sha256=tB0Wgz3O9j6KiCsU1HWfsKmhNM9RqLsAxzxEDFQFGnM,8565
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=VG2I6YwAOUwENuqpgzU2PZh5QHl6Web09OpIL3Tz8eA,126667
11
+ letta/client/client.py,sha256=hQDOqeWIeHNvvWTiO6QgBcuzHQ54V-G0DjtbJ-ycL9Y,126673
12
12
  letta/client/streaming.py,sha256=Hh5pjlyrdCuO2V75ZCxSSOCPd3BmHdKFGaIUJC6fBp0,4775
13
13
  letta/client/utils.py,sha256=OJlAKWrldc4I6M1WpcTWNtPJ4wfxlzlZqWLfCozkFtI,2872
14
14
  letta/config.py,sha256=JFGY4TWW0Wm5fTbZamOwWqk5G8Nn-TXyhgByGoAqy2c,12375
@@ -17,9 +17,9 @@ letta/credentials.py,sha256=D9mlcPsdDWlIIXQQD8wSPE9M_QvsRrb0p3LB5i9OF5Q,5806
17
17
  letta/data_sources/connectors.py,sha256=Bwgf6mW55rDrdX69dY3bLzQW9Uuk_o9w4skwbx1NioY,7039
18
18
  letta/data_sources/connectors_helper.py,sha256=2TQjCt74fCgT5sw1AP8PalDEk06jPBbhrPG4HVr-WLs,3371
19
19
  letta/embeddings.py,sha256=r1jWuZplYZl8GzfObBZxPdKWg2O0Msd8D164qSoCPIM,8855
20
- letta/errors.py,sha256=Voy_BP0W_M816-vWudKLBlgydRufPPA-Q2PNd-SvZYc,3897
20
+ letta/errors.py,sha256=Suh7081WzyH-LcAaJGs8tQ6TQ6b4ATd5cmUyBCa5aoA,5139
21
21
  letta/functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- letta/functions/function_sets/base.py,sha256=OcjkPBfurAGWSXfvG8i108c5cpA9lnykC9COXXmzbM8,8173
22
+ letta/functions/function_sets/base.py,sha256=bOiitkhzqYKwZBiRYrx29AlordiA5IrXw25eVSRK8BY,5984
23
23
  letta/functions/function_sets/extras.py,sha256=Jik3UiDqYTm4Lam1XPTvuVjvgUHwIAhopsnbmVhGMBg,4732
24
24
  letta/functions/functions.py,sha256=evH6GKnIJwVVre1Xre2gaSIqREv4eNM4DiWOhn8PMqg,3299
25
25
  letta/functions/helpers.py,sha256=fJo4gPvWpkvR7jn0HucRorz4VlpYVqmYsiX1RetnnSY,8569
@@ -37,7 +37,7 @@ letta/llm_api/azure_openai_constants.py,sha256=oXtKrgBFHf744gyt5l1thILXgyi8NDNUr
37
37
  letta/llm_api/cohere.py,sha256=vDRd-SUGp1t_JUIdwC3RkIhwMl0OY7n-tAU9uPORYkY,14826
38
38
  letta/llm_api/google_ai.py,sha256=xKz9JDZs3m6yzSfcgCAAUD_rjI20BBIINoiSvlcnOw0,17621
39
39
  letta/llm_api/helpers.py,sha256=F8xZDZgDojWX5v-0vakyeUQyCyBr1HmzmsITRdOsmVg,13457
40
- letta/llm_api/llm_api_tools.py,sha256=MadPiVSc_JYwf5dwpeCc5lRBN3rBmsops5EQy2HW-gw,17687
40
+ letta/llm_api/llm_api_tools.py,sha256=Qb1NsOOLmgMzMZ_A1J0v2EoQPm444Z2kZ9d-5j-FL6c,17731
41
41
  letta/llm_api/mistral.py,sha256=fHdfD9ug-rQIk2qn8tRKay1U6w9maF11ryhKi91FfXM,1593
42
42
  letta/llm_api/openai.py,sha256=4GEGROTv4vLawSgODnAHCI-DeIWDqrhuxtKrqYzHvso,24160
43
43
  letta/local_llm/README.md,sha256=hFJyw5B0TU2jrh9nb0zGZMgdH-Ei1dSRfhvPQG_NSoU,168
@@ -96,10 +96,10 @@ letta/orm/enums.py,sha256=KfHcFt_fR6GUmSlmfsa-TetvmuRxGESNve8MStRYW64,145
96
96
  letta/orm/errors.py,sha256=Se0Guz-gqi-D36NUWSh7AP9zTVCSph9KgZh_trwng4o,734
97
97
  letta/orm/file.py,sha256=0qfqmBFizwtYriCz_qrshjxw3A9lMaRFKtwsZecviyo,1765
98
98
  letta/orm/job.py,sha256=If-qSTJW4t5h-6Jolw3tS3-xMZEaPIbXe3S0GMf_FXI,1102
99
- letta/orm/message.py,sha256=yWf46-adgYGqhxjn_QREW19jvVjYU0eD11uwlVSrdT4,1490
99
+ letta/orm/message.py,sha256=Ui3mMZTAObP7RvmNnqCFudw8Pkt8IAPTlIjCSCsVDFA,1560
100
100
  letta/orm/mixins.py,sha256=fBT4WjKDfDgGkkOHD8lFAaofDKElTLOx3oM1d6yQHSk,1620
101
101
  letta/orm/organization.py,sha256=PSu9pHBT_YYdZo7Z7AZBBM4veOzD7x2H2NYN4SjD82E,2519
102
- letta/orm/passage.py,sha256=fsETrGtmiGI45N5Rc2pMLy-9Mzggn6FZ-fRwI-0x-Hg,3249
102
+ letta/orm/passage.py,sha256=tm5YhUozLR9hN7odGCqCniTl-3GDiFNz3LWAxullaGA,3132
103
103
  letta/orm/sandbox_config.py,sha256=PCMHE-eJPzBT-90OYtXjEMRF4f9JB8AJIGETE7bu-f0,2870
104
104
  letta/orm/source.py,sha256=SlX08BnoJYMh34cGTuPbo2kraCuXSy25xGdNWb7d7DQ,1782
105
105
  letta/orm/sources_agents.py,sha256=q5Wf5TFNI9KH6cGW93roNhvFD3n39UE2bYQhnSJwlME,433
@@ -180,7 +180,7 @@ letta/server/rest_api/routers/openai/assistants/schemas.py,sha256=d3LdBLUI-mMUCP
180
180
  letta/server/rest_api/routers/openai/chat_completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
181
181
  letta/server/rest_api/routers/openai/chat_completions/chat_completions.py,sha256=Yvhcr1FVk1JltXg8PRwkJf8TD1fO4ARDAVsMmHY5xzc,4849
182
182
  letta/server/rest_api/routers/v1/__init__.py,sha256=RZc0fIHNN4BGretjU6_TGK7q49RyV4jfYNudoiK_sUo,762
183
- letta/server/rest_api/routers/v1/agents.py,sha256=ryzV4R0Tfhkk0MDuSQFg1DXuHOpLgtYsajx6toAlK3g,30295
183
+ letta/server/rest_api/routers/v1/agents.py,sha256=JIeHoe-C1_6PaSSYWuj5GPbHBEiWLqsQJzb_HCebvHw,30637
184
184
  letta/server/rest_api/routers/v1/blocks.py,sha256=IJ2pppwNooaUjIwyBALnKL4sJ8idW8cVJlY-VH_J9HY,4803
185
185
  letta/server/rest_api/routers/v1/health.py,sha256=pKCuVESlVOhGIb4VC4K-H82eZqfghmT6kvj2iOkkKuc,401
186
186
  letta/server/rest_api/routers/v1/jobs.py,sha256=-tEyuIxlXZfPREeMks-sRzHwhKE2xxgzbXeEbBAS2Q8,2730
@@ -188,11 +188,11 @@ letta/server/rest_api/routers/v1/llms.py,sha256=TcyvSx6MEM3je5F4DysL7ligmssL_pFl
188
188
  letta/server/rest_api/routers/v1/organizations.py,sha256=tyqVzXTpMtk3sKxI3Iz4aS6RhbGEbXDzFBB_CpW18v4,2080
189
189
  letta/server/rest_api/routers/v1/sandbox_configs.py,sha256=pG3X3bYbmsq90kRc-06qfnM6yalvYEpVVEQnTuZVm0o,5134
190
190
  letta/server/rest_api/routers/v1/sources.py,sha256=bLvxyYBOLx1VD5YPuoCBrQrua0AruzUzvCMIiekjVQg,9974
191
- letta/server/rest_api/routers/v1/tools.py,sha256=g6LKwz6aamNwMrjMqUSYDu8X0FjKAzCtOKJRAwcuZEk,11762
191
+ letta/server/rest_api/routers/v1/tools.py,sha256=y6Dx8VDcq0CShDbMqv47y1dgrT-P6bMAiIQE4CIw9Yo,11170
192
192
  letta/server/rest_api/routers/v1/users.py,sha256=EBQe9IfCG3kzHpKmotz4yVGZioXz3SCSRy5yEhJK8hU,2293
193
193
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
194
- letta/server/rest_api/utils.py,sha256=5GO52-3EehRFZPDkvk4pUoHa0GZXvbPuQsmX298gbJg,3905
195
- letta/server/server.py,sha256=xL1hZ3pS7Imc8CKahTXVkzL-gKjW0pgzJz_jpMrBb7I,60291
194
+ letta/server/rest_api/utils.py,sha256=3wF-KavVVKzcdxcTQ7zzKtQQ1a7jRN3YuKXttrI5S9Y,3963
195
+ letta/server/server.py,sha256=eBLkVhbmPrlNWMI38fI15PjTdLuv9Uc3yQTPxF0JlPg,61973
196
196
  letta/server/startup.sh,sha256=722uKJWB2C4q3vjn39De2zzPacaZNw_1fN1SpLGjKIo,1569
197
197
  letta/server/static_files/assets/index-048c9598.js,sha256=mR16XppvselwKCcNgONs4L7kZEVa4OEERm4lNZYtLSk,146819
198
198
  letta/server/static_files/assets/index-0e31b727.css,sha256=DjG3J3RSFLcinzoisOYYLiyTAIe5Uaxge3HE-DmQIsU,7688
@@ -206,7 +206,7 @@ letta/server/ws_api/interface.py,sha256=TWl9vkcMCnLsUtgsuENZ-ku2oMDA-OUTzLh_yNRo
206
206
  letta/server/ws_api/protocol.py,sha256=M_-gM5iuDBwa1cuN2IGNCG5GxMJwU2d3XW93XALv9s8,1821
207
207
  letta/server/ws_api/server.py,sha256=cBSzf-V4zT1bL_0i54OTI3cMXhTIIxqjSRF8pYjk7fg,5835
208
208
  letta/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
209
- letta/services/agent_manager.py,sha256=5XODToGznZjdH4J3y-j-ToaP1rsbxQ82YxldEAO6kVU,30678
209
+ letta/services/agent_manager.py,sha256=GQyLp6ZgBCXozkkbfeY81_sFbf-UHVU_ulaTvFO9dCM,30338
210
210
  letta/services/block_manager.py,sha256=HUj9HKW2LvAsENEsgCO3Pf3orvSy6XOimev38VKmRZ8,4929
211
211
  letta/services/helpers/agent_manager_helper.py,sha256=4AoJJI3ELDZrfhx38vc2OwgQflb7mkdppucln0MkgYg,3457
212
212
  letta/services/job_manager.py,sha256=FrkSXloke48CZKuzlYdysxM5gKWoTu7FRigPrs_YW4A,3645
@@ -217,7 +217,7 @@ letta/services/per_agent_lock_manager.py,sha256=porM0cKKANQ1FvcGXOO_qM7ARk5Fgi1H
217
217
  letta/services/sandbox_config_manager.py,sha256=USTXwEEWexKRZjng4NepP4_eetrxCJ5n16cl2AHJ_VM,13220
218
218
  letta/services/source_manager.py,sha256=ZtLQikeJjwAq49f0d4WxUzyUN3LZBqWCZI4a-AzEMWQ,7643
219
219
  letta/services/tool_execution_sandbox.py,sha256=NtjSXdm86_lbTeW2gF08tyf2KSoxBP0Bxq7qSNudrGE,21567
220
- letta/services/tool_manager.py,sha256=lfrfWyxiFUWcEf-nATHs7r76XWutMYbOPyePs543ZOo,7681
220
+ letta/services/tool_manager.py,sha256=eKhvGYNBq8MOwfuk-GyqiTdEcxRn4srYvTJqj-HgTKA,7686
221
221
  letta/services/tool_sandbox_env/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
222
222
  letta/services/user_manager.py,sha256=oqLF9C4mGbN0TaGj7wMpb2RH2bUg6OJJcdyaWv370rQ,4272
223
223
  letta/settings.py,sha256=sC4gS8Po1NyPmSMTk-TTxrKEoJUi8qgou1K3-BqtNA4,3979
@@ -225,8 +225,8 @@ letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,
225
225
  letta/streaming_utils.py,sha256=329fsvj1ZN0r0LpQtmMPZ2vSxkDBIUUwvGHZFkjm2I8,11745
226
226
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
227
227
  letta/utils.py,sha256=AlGH2fADkU5VkGISj9-hwMSZVKQUiI1whWTvXCKOiYw,33338
228
- letta_nightly-0.6.5.dev20241218104144.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
229
- letta_nightly-0.6.5.dev20241218104144.dist-info/METADATA,sha256=rLBP3ch7RRrjmKRH3yjCMMug0qs-aWqDbhMs8I0BcqE,21695
230
- letta_nightly-0.6.5.dev20241218104144.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
231
- letta_nightly-0.6.5.dev20241218104144.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
232
- letta_nightly-0.6.5.dev20241218104144.dist-info/RECORD,,
228
+ letta_nightly-0.6.5.dev20241219104153.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
229
+ letta_nightly-0.6.5.dev20241219104153.dist-info/METADATA,sha256=T6o-VctnMHTFvzBlj19iFGVpp7dF3IzSf-4pOjbYB4Y,21693
230
+ letta_nightly-0.6.5.dev20241219104153.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
231
+ letta_nightly-0.6.5.dev20241219104153.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
232
+ letta_nightly-0.6.5.dev20241219104153.dist-info/RECORD,,