letta-nightly 0.6.5.dev20241218213641__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 +17 -5
- letta/client/client.py +2 -2
- letta/errors.py +54 -19
- letta/functions/function_sets/base.py +0 -54
- letta/llm_api/llm_api_tools.py +2 -2
- letta/orm/message.py +2 -1
- letta/orm/passage.py +14 -15
- letta/server/rest_api/routers/v1/agents.py +11 -0
- letta/server/rest_api/routers/v1/tools.py +3 -18
- letta/server/rest_api/utils.py +23 -22
- letta/server/server.py +31 -4
- letta/services/agent_manager.py +32 -46
- letta/services/tool_manager.py +3 -3
- {letta_nightly-0.6.5.dev20241218213641.dist-info → letta_nightly-0.6.5.dev20241219104153.dist-info}/METADATA +1 -1
- {letta_nightly-0.6.5.dev20241218213641.dist-info → letta_nightly-0.6.5.dev20241219104153.dist-info}/RECORD +18 -18
- {letta_nightly-0.6.5.dev20241218213641.dist-info → letta_nightly-0.6.5.dev20241219104153.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.5.dev20241218213641.dist-info → letta_nightly-0.6.5.dev20241219104153.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.5.dev20241218213641.dist-info → letta_nightly-0.6.5.dev20241219104153.dist-info}/entry_points.txt +0 -0
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
|
|
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
|
|
1173
|
-
|
|
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
|
|
1208
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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.
|
letta/llm_api/llm_api_tools.py
CHANGED
|
@@ -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
|
|
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__ =
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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")
|
letta/server/rest_api/utils.py
CHANGED
|
@@ -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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
letta/services/agent_manager.py
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
from typing import Dict, List, Optional
|
|
2
1
|
from datetime import datetime
|
|
3
|
-
import
|
|
2
|
+
from typing import Dict, List, Optional
|
|
4
3
|
|
|
5
|
-
|
|
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
|
-
|
|
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(
|
|
490
|
-
literal(None).label(
|
|
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(
|
|
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(
|
|
496
|
+
combined_query = agent_passages.cte("combined_passages")
|
|
502
497
|
elif source_passages is not None:
|
|
503
|
-
combined_query = source_passages.cte(
|
|
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
|
-
|
|
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[
|
|
607
|
+
if data["agent_id"] is not None:
|
|
621
608
|
# This is an AgentPassage - remove source fields
|
|
622
|
-
data.pop(
|
|
623
|
-
data.pop(
|
|
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(
|
|
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
|
letta/services/tool_manager.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
letta/__init__.py,sha256=zY_5By7t8Jgl0Nejq2i4yOTJQg0lzJNlwdmp8sB-W9Y,1014
|
|
2
2
|
letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
|
|
3
|
-
letta/agent.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
195
|
-
letta/server/server.py,sha256=
|
|
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=
|
|
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=
|
|
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.
|
|
229
|
-
letta_nightly-0.6.5.
|
|
230
|
-
letta_nightly-0.6.5.
|
|
231
|
-
letta_nightly-0.6.5.
|
|
232
|
-
letta_nightly-0.6.5.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|