letta-nightly 0.6.2.dev20241210104242__py3-none-any.whl → 0.6.3.dev20241211050151__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of letta-nightly might be problematic. Click here for more details.
- letta/__init__.py +1 -1
- letta/agent.py +32 -43
- letta/agent_store/db.py +12 -54
- letta/agent_store/storage.py +10 -9
- letta/cli/cli.py +1 -0
- letta/client/client.py +3 -2
- letta/config.py +2 -2
- letta/data_sources/connectors.py +4 -3
- letta/embeddings.py +29 -9
- letta/functions/function_sets/base.py +36 -11
- letta/metadata.py +13 -2
- letta/o1_agent.py +2 -3
- letta/offline_memory_agent.py +2 -1
- letta/orm/__init__.py +1 -0
- letta/orm/file.py +1 -0
- letta/orm/mixins.py +12 -2
- letta/orm/organization.py +3 -0
- letta/orm/passage.py +72 -0
- letta/orm/sqlalchemy_base.py +36 -7
- letta/orm/sqlite_functions.py +140 -0
- letta/orm/user.py +1 -1
- letta/schemas/agent.py +4 -3
- letta/schemas/letta_message.py +5 -1
- letta/schemas/letta_request.py +3 -3
- letta/schemas/passage.py +6 -4
- letta/schemas/sandbox_config.py +1 -0
- letta/schemas/tool_rule.py +0 -3
- letta/server/rest_api/app.py +34 -12
- letta/server/rest_api/routers/v1/agents.py +19 -6
- letta/server/server.py +182 -43
- letta/server/static_files/assets/{index-4848e3d7.js → index-048c9598.js} +1 -1
- letta/server/static_files/assets/{index-43ab4d62.css → index-0e31b727.css} +1 -1
- letta/server/static_files/index.html +2 -2
- letta/services/passage_manager.py +225 -0
- letta/services/source_manager.py +2 -1
- letta/services/tool_execution_sandbox.py +18 -6
- letta/settings.py +2 -0
- letta_nightly-0.6.3.dev20241211050151.dist-info/METADATA +375 -0
- {letta_nightly-0.6.2.dev20241210104242.dist-info → letta_nightly-0.6.3.dev20241211050151.dist-info}/RECORD +42 -40
- letta/agent_store/chroma.py +0 -297
- letta_nightly-0.6.2.dev20241210104242.dist-info/METADATA +0 -212
- {letta_nightly-0.6.2.dev20241210104242.dist-info → letta_nightly-0.6.3.dev20241211050151.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.2.dev20241210104242.dist-info → letta_nightly-0.6.3.dev20241211050151.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.2.dev20241210104242.dist-info → letta_nightly-0.6.3.dev20241211050151.dist-info}/entry_points.txt +0 -0
letta/server/server.py
CHANGED
|
@@ -16,7 +16,6 @@ import letta.constants as constants
|
|
|
16
16
|
import letta.server.utils as server_utils
|
|
17
17
|
import letta.system as system
|
|
18
18
|
from letta.agent import Agent, save_agent
|
|
19
|
-
from letta.agent_store.db import attach_base
|
|
20
19
|
from letta.agent_store.storage import StorageConnector, TableType
|
|
21
20
|
from letta.chat_only_agent import ChatOnlyAgent
|
|
22
21
|
from letta.credentials import LettaCredentials
|
|
@@ -70,17 +69,18 @@ from letta.schemas.memory import (
|
|
|
70
69
|
)
|
|
71
70
|
from letta.schemas.message import Message, MessageCreate, MessageRole, MessageUpdate
|
|
72
71
|
from letta.schemas.organization import Organization
|
|
73
|
-
from letta.schemas.passage import Passage
|
|
72
|
+
from letta.schemas.passage import Passage as PydanticPassage
|
|
74
73
|
from letta.schemas.source import Source
|
|
75
74
|
from letta.schemas.tool import Tool, ToolCreate
|
|
76
75
|
from letta.schemas.usage import LettaUsageStatistics
|
|
77
|
-
from letta.schemas.user import User
|
|
76
|
+
from letta.schemas.user import User as PydanticUser
|
|
78
77
|
from letta.services.agents_tags_manager import AgentsTagsManager
|
|
79
78
|
from letta.services.block_manager import BlockManager
|
|
80
79
|
from letta.services.blocks_agents_manager import BlocksAgentsManager
|
|
81
80
|
from letta.services.job_manager import JobManager
|
|
82
81
|
from letta.services.message_manager import MessageManager
|
|
83
82
|
from letta.services.organization_manager import OrganizationManager
|
|
83
|
+
from letta.services.passage_manager import PassageManager
|
|
84
84
|
from letta.services.per_agent_lock_manager import PerAgentLockManager
|
|
85
85
|
from letta.services.sandbox_config_manager import SandboxConfigManager
|
|
86
86
|
from letta.services.source_manager import SourceManager
|
|
@@ -125,7 +125,7 @@ class Server(object):
|
|
|
125
125
|
def create_agent(
|
|
126
126
|
self,
|
|
127
127
|
request: CreateAgent,
|
|
128
|
-
actor:
|
|
128
|
+
actor: PydanticUser,
|
|
129
129
|
# interface
|
|
130
130
|
interface: Union[AgentInterface, None] = None,
|
|
131
131
|
) -> AgentState:
|
|
@@ -156,6 +156,11 @@ class Server(object):
|
|
|
156
156
|
raise NotImplementedError
|
|
157
157
|
|
|
158
158
|
|
|
159
|
+
from contextlib import contextmanager
|
|
160
|
+
|
|
161
|
+
from rich.console import Console
|
|
162
|
+
from rich.panel import Panel
|
|
163
|
+
from rich.text import Text
|
|
159
164
|
from sqlalchemy import create_engine
|
|
160
165
|
from sqlalchemy.orm import sessionmaker
|
|
161
166
|
|
|
@@ -166,7 +171,36 @@ from letta.settings import model_settings, settings, tool_settings
|
|
|
166
171
|
|
|
167
172
|
config = LettaConfig.load()
|
|
168
173
|
|
|
169
|
-
|
|
174
|
+
|
|
175
|
+
def print_sqlite_schema_error():
|
|
176
|
+
"""Print a formatted error message for SQLite schema issues"""
|
|
177
|
+
console = Console()
|
|
178
|
+
error_text = Text()
|
|
179
|
+
error_text.append("Existing SQLite DB schema is invalid, and schema migrations are not supported for SQLite. ", style="bold red")
|
|
180
|
+
error_text.append("To have migrations supported between Letta versions, please run Letta with Docker (", style="white")
|
|
181
|
+
error_text.append("https://docs.letta.com/server/docker", style="blue underline")
|
|
182
|
+
error_text.append(") or use Postgres by setting ", style="white")
|
|
183
|
+
error_text.append("LETTA_PG_URI", style="yellow")
|
|
184
|
+
error_text.append(".\n\n", style="white")
|
|
185
|
+
error_text.append("If you wish to keep using SQLite, you can reset your database by removing the DB file with ", style="white")
|
|
186
|
+
error_text.append("rm ~/.letta/sqlite.db", style="yellow")
|
|
187
|
+
error_text.append(" or downgrade to your previous version of Letta.", style="white")
|
|
188
|
+
|
|
189
|
+
console.print(Panel(error_text, border_style="red"))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@contextmanager
|
|
193
|
+
def db_error_handler():
|
|
194
|
+
"""Context manager for handling database errors"""
|
|
195
|
+
try:
|
|
196
|
+
yield
|
|
197
|
+
except Exception as e:
|
|
198
|
+
# Handle other SQLAlchemy errors
|
|
199
|
+
print(e)
|
|
200
|
+
print_sqlite_schema_error()
|
|
201
|
+
# raise ValueError(f"SQLite DB error: {str(e)}")
|
|
202
|
+
exit(1)
|
|
203
|
+
|
|
170
204
|
|
|
171
205
|
if settings.letta_pg_uri_no_default:
|
|
172
206
|
config.recall_storage_type = "postgres"
|
|
@@ -180,6 +214,30 @@ else:
|
|
|
180
214
|
# TODO: don't rely on config storage
|
|
181
215
|
engine = create_engine("sqlite:///" + os.path.join(config.recall_storage_path, "sqlite.db"))
|
|
182
216
|
|
|
217
|
+
# Store the original connect method
|
|
218
|
+
original_connect = engine.connect
|
|
219
|
+
|
|
220
|
+
def wrapped_connect(*args, **kwargs):
|
|
221
|
+
with db_error_handler():
|
|
222
|
+
# Get the connection
|
|
223
|
+
connection = original_connect(*args, **kwargs)
|
|
224
|
+
|
|
225
|
+
# Store the original execution method
|
|
226
|
+
original_execute = connection.execute
|
|
227
|
+
|
|
228
|
+
# Wrap the execute method of the connection
|
|
229
|
+
def wrapped_execute(*args, **kwargs):
|
|
230
|
+
with db_error_handler():
|
|
231
|
+
return original_execute(*args, **kwargs)
|
|
232
|
+
|
|
233
|
+
# Replace the connection's execute method
|
|
234
|
+
connection.execute = wrapped_execute
|
|
235
|
+
|
|
236
|
+
return connection
|
|
237
|
+
|
|
238
|
+
# Replace the engine's connect method
|
|
239
|
+
engine.connect = wrapped_connect
|
|
240
|
+
|
|
183
241
|
Base.metadata.create_all(bind=engine)
|
|
184
242
|
|
|
185
243
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
@@ -245,6 +303,7 @@ class SyncServer(Server):
|
|
|
245
303
|
|
|
246
304
|
# Managers that interface with data models
|
|
247
305
|
self.organization_manager = OrganizationManager()
|
|
306
|
+
self.passage_manager = PassageManager()
|
|
248
307
|
self.user_manager = UserManager()
|
|
249
308
|
self.tool_manager = ToolManager()
|
|
250
309
|
self.block_manager = BlockManager()
|
|
@@ -379,6 +438,10 @@ class SyncServer(Server):
|
|
|
379
438
|
interface = interface or self.default_interface_factory()
|
|
380
439
|
if agent_state.agent_type == AgentType.memgpt_agent:
|
|
381
440
|
agent = Agent(agent_state=agent_state, interface=interface, user=actor, initial_message_sequence=initial_message_sequence)
|
|
441
|
+
elif agent_state.agent_type == AgentType.offline_memory_agent:
|
|
442
|
+
agent = OfflineMemoryAgent(
|
|
443
|
+
agent_state=agent_state, interface=interface, user=actor, initial_message_sequence=initial_message_sequence
|
|
444
|
+
)
|
|
382
445
|
else:
|
|
383
446
|
assert initial_message_sequence is None, f"Initial message sequence is not supported for O1Agents"
|
|
384
447
|
agent = O1Agent(agent_state=agent_state, interface=interface, user=actor)
|
|
@@ -496,7 +559,12 @@ class SyncServer(Server):
|
|
|
496
559
|
|
|
497
560
|
# attach data to agent from source
|
|
498
561
|
source_connector = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
|
|
499
|
-
letta_agent.attach_source(
|
|
562
|
+
letta_agent.attach_source(
|
|
563
|
+
user=self.user_manager.get_user_by_id(user_id=user_id),
|
|
564
|
+
source_id=data_source,
|
|
565
|
+
source_manager=letta_agent.source_manager,
|
|
566
|
+
ms=self.ms,
|
|
567
|
+
)
|
|
500
568
|
|
|
501
569
|
elif command.lower() == "dump" or command.lower().startswith("dump "):
|
|
502
570
|
# Check if there's an additional argument that's an integer
|
|
@@ -511,7 +579,7 @@ class SyncServer(Server):
|
|
|
511
579
|
letta_agent.interface.print_messages_raw(letta_agent.messages)
|
|
512
580
|
|
|
513
581
|
elif command.lower() == "memory":
|
|
514
|
-
ret_str = f"\nDumping memory contents:\n" + f"\n{str(letta_agent.agent_state.memory)}" + f"\n{str(letta_agent.
|
|
582
|
+
ret_str = f"\nDumping memory contents:\n" + f"\n{str(letta_agent.agent_state.memory)}" + f"\n{str(letta_agent.passage_manager)}"
|
|
515
583
|
return ret_str
|
|
516
584
|
|
|
517
585
|
elif command.lower() == "pop" or command.lower().startswith("pop "):
|
|
@@ -767,7 +835,7 @@ class SyncServer(Server):
|
|
|
767
835
|
def create_agent(
|
|
768
836
|
self,
|
|
769
837
|
request: CreateAgent,
|
|
770
|
-
actor:
|
|
838
|
+
actor: PydanticUser,
|
|
771
839
|
# interface
|
|
772
840
|
interface: Union[AgentInterface, None] = None,
|
|
773
841
|
) -> AgentState:
|
|
@@ -824,6 +892,12 @@ class SyncServer(Server):
|
|
|
824
892
|
if not user:
|
|
825
893
|
raise ValueError(f"cannot find user with associated client id: {user_id}")
|
|
826
894
|
|
|
895
|
+
if request.llm_config is None:
|
|
896
|
+
raise ValueError("llm_config is required")
|
|
897
|
+
|
|
898
|
+
if request.embedding_config is None:
|
|
899
|
+
raise ValueError("embedding_config is required")
|
|
900
|
+
|
|
827
901
|
# created and persist the agent state in the DB
|
|
828
902
|
agent_state = PersistedAgentState(
|
|
829
903
|
name=request.name,
|
|
@@ -843,7 +917,7 @@ class SyncServer(Server):
|
|
|
843
917
|
self.ms.create_agent(agent_state)
|
|
844
918
|
|
|
845
919
|
# create the agent object
|
|
846
|
-
if request.initial_message_sequence:
|
|
920
|
+
if request.initial_message_sequence is not None:
|
|
847
921
|
# init_messages = [Message(user_id=user_id, agent_id=agent_state.id, role=message.role, text=message.text) for message in request.initial_message_sequence]
|
|
848
922
|
init_messages = []
|
|
849
923
|
for message in request.initial_message_sequence:
|
|
@@ -913,6 +987,7 @@ class SyncServer(Server):
|
|
|
913
987
|
|
|
914
988
|
# get `Tool` objects
|
|
915
989
|
tools = [self.tool_manager.get_tool_by_name(tool_name=tool_name, actor=user) for tool_name in agent_state.tool_names]
|
|
990
|
+
tools = [tool for tool in tools if tool is not None]
|
|
916
991
|
|
|
917
992
|
# get `Source` objects
|
|
918
993
|
sources = self.list_attached_sources(agent_id=agent_id)
|
|
@@ -926,7 +1001,7 @@ class SyncServer(Server):
|
|
|
926
1001
|
def update_agent(
|
|
927
1002
|
self,
|
|
928
1003
|
request: UpdateAgentState,
|
|
929
|
-
actor:
|
|
1004
|
+
actor: PydanticUser,
|
|
930
1005
|
) -> AgentState:
|
|
931
1006
|
"""Update the agents core memory block, return the new state"""
|
|
932
1007
|
try:
|
|
@@ -1143,7 +1218,7 @@ class SyncServer(Server):
|
|
|
1143
1218
|
|
|
1144
1219
|
def get_archival_memory_summary(self, agent_id: str) -> ArchivalMemorySummary:
|
|
1145
1220
|
agent = self.load_agent(agent_id=agent_id)
|
|
1146
|
-
return ArchivalMemorySummary(size=
|
|
1221
|
+
return ArchivalMemorySummary(size=agent.passage_manager.size(actor=self.default_user))
|
|
1147
1222
|
|
|
1148
1223
|
def get_recall_memory_summary(self, agent_id: str) -> RecallMemorySummary:
|
|
1149
1224
|
agent = self.load_agent(agent_id=agent_id)
|
|
@@ -1168,7 +1243,56 @@ class SyncServer(Server):
|
|
|
1168
1243
|
message = agent.message_manager.get_message_by_id(id=message_id, actor=self.default_user)
|
|
1169
1244
|
return message
|
|
1170
1245
|
|
|
1171
|
-
def
|
|
1246
|
+
def get_agent_messages(
|
|
1247
|
+
self,
|
|
1248
|
+
agent_id: str,
|
|
1249
|
+
start: int,
|
|
1250
|
+
count: int,
|
|
1251
|
+
) -> Union[List[Message], List[LettaMessage]]:
|
|
1252
|
+
"""Paginated query of all messages in agent message queue"""
|
|
1253
|
+
# Get the agent object (loaded in memory)
|
|
1254
|
+
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1255
|
+
|
|
1256
|
+
if start < 0 or count < 0:
|
|
1257
|
+
raise ValueError("Start and count values should be non-negative")
|
|
1258
|
+
|
|
1259
|
+
if start + count < len(letta_agent._messages): # messages can be returned from whats in memory
|
|
1260
|
+
# Reverse the list to make it in reverse chronological order
|
|
1261
|
+
reversed_messages = letta_agent._messages[::-1]
|
|
1262
|
+
# Check if start is within the range of the list
|
|
1263
|
+
if start >= len(reversed_messages):
|
|
1264
|
+
raise IndexError("Start index is out of range")
|
|
1265
|
+
|
|
1266
|
+
# Calculate the end index, ensuring it does not exceed the list length
|
|
1267
|
+
end_index = min(start + count, len(reversed_messages))
|
|
1268
|
+
|
|
1269
|
+
# Slice the list for pagination
|
|
1270
|
+
messages = reversed_messages[start:end_index]
|
|
1271
|
+
|
|
1272
|
+
else:
|
|
1273
|
+
# need to access persistence manager for additional messages
|
|
1274
|
+
|
|
1275
|
+
# get messages using message manager
|
|
1276
|
+
page = letta_agent.message_manager.list_user_messages_for_agent(
|
|
1277
|
+
agent_id=agent_id,
|
|
1278
|
+
actor=self.default_user,
|
|
1279
|
+
cursor=start,
|
|
1280
|
+
limit=count,
|
|
1281
|
+
)
|
|
1282
|
+
|
|
1283
|
+
messages = page
|
|
1284
|
+
assert all(isinstance(m, Message) for m in messages)
|
|
1285
|
+
|
|
1286
|
+
## Convert to json
|
|
1287
|
+
## Add a tag indicating in-context or not
|
|
1288
|
+
# json_messages = [record.to_json() for record in messages]
|
|
1289
|
+
# in_context_message_ids = [str(m.id) for m in letta_agent._messages]
|
|
1290
|
+
# for d in json_messages:
|
|
1291
|
+
# d["in_context"] = True if str(d["id"]) in in_context_message_ids else False
|
|
1292
|
+
|
|
1293
|
+
return messages
|
|
1294
|
+
|
|
1295
|
+
def get_agent_archival(self, user_id: str, agent_id: str, cursor: Optional[str] = None, limit: int = 50) -> List[PydanticPassage]:
|
|
1172
1296
|
"""Paginated query of all messages in agent archival memory"""
|
|
1173
1297
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1174
1298
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
@@ -1179,22 +1303,22 @@ class SyncServer(Server):
|
|
|
1179
1303
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1180
1304
|
|
|
1181
1305
|
# iterate over records
|
|
1182
|
-
|
|
1306
|
+
records = letta_agent.passage_manager.list_passages(
|
|
1307
|
+
actor=self.default_user,
|
|
1308
|
+
agent_id=agent_id,
|
|
1309
|
+
cursor=cursor,
|
|
1310
|
+
limit=limit,
|
|
1311
|
+
)
|
|
1183
1312
|
|
|
1184
|
-
|
|
1185
|
-
page = next(db_iterator, [])
|
|
1186
|
-
return page
|
|
1313
|
+
return records
|
|
1187
1314
|
|
|
1188
1315
|
def get_agent_archival_cursor(
|
|
1189
1316
|
self,
|
|
1190
1317
|
user_id: str,
|
|
1191
1318
|
agent_id: str,
|
|
1192
|
-
|
|
1193
|
-
before: Optional[str] = None,
|
|
1319
|
+
cursor: Optional[str] = None,
|
|
1194
1320
|
limit: Optional[int] = 100,
|
|
1195
|
-
|
|
1196
|
-
reverse: Optional[bool] = False,
|
|
1197
|
-
) -> List[Passage]:
|
|
1321
|
+
) -> List[PydanticPassage]:
|
|
1198
1322
|
if self.user_manager.get_user_by_id(user_id=user_id) is None:
|
|
1199
1323
|
raise LettaUserNotFoundError(f"User user_id={user_id} does not exist")
|
|
1200
1324
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
@@ -1203,14 +1327,18 @@ class SyncServer(Server):
|
|
|
1203
1327
|
# Get the agent object (loaded in memory)
|
|
1204
1328
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1205
1329
|
|
|
1206
|
-
# iterate over
|
|
1207
|
-
|
|
1208
|
-
|
|
1330
|
+
# iterate over records
|
|
1331
|
+
records = letta_agent.passage_manager.list_passages(
|
|
1332
|
+
actor=self.default_user,
|
|
1333
|
+
agent_id=agent_id,
|
|
1334
|
+
cursor=cursor,
|
|
1335
|
+
limit=limit,
|
|
1209
1336
|
)
|
|
1210
1337
|
return records
|
|
1211
1338
|
|
|
1212
|
-
def insert_archival_memory(self, user_id: str, agent_id: str, memory_contents: str) -> List[
|
|
1213
|
-
|
|
1339
|
+
def insert_archival_memory(self, user_id: str, agent_id: str, memory_contents: str) -> List[PydanticPassage]:
|
|
1340
|
+
actor = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1341
|
+
if actor is None:
|
|
1214
1342
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
1215
1343
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1216
1344
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
@@ -1219,17 +1347,20 @@ class SyncServer(Server):
|
|
|
1219
1347
|
letta_agent = self.load_agent(agent_id=agent_id)
|
|
1220
1348
|
|
|
1221
1349
|
# Insert into archival memory
|
|
1222
|
-
passage_ids =
|
|
1350
|
+
passage_ids = self.passage_manager.insert_passage(
|
|
1351
|
+
agent_state=letta_agent.agent_state, agent_id=agent_id, text=memory_contents, actor=actor, return_ids=True
|
|
1352
|
+
)
|
|
1223
1353
|
|
|
1224
1354
|
# Update the agent
|
|
1225
1355
|
# TODO: should this update the system prompt?
|
|
1226
1356
|
save_agent(letta_agent, self.ms)
|
|
1227
1357
|
|
|
1228
1358
|
# TODO: this is gross, fix
|
|
1229
|
-
return [
|
|
1359
|
+
return [self.passage_manager.get_passage_by_id(passage_id=passage_id, actor=actor) for passage_id in passage_ids]
|
|
1230
1360
|
|
|
1231
1361
|
def delete_archival_memory(self, user_id: str, agent_id: str, memory_id: str):
|
|
1232
|
-
|
|
1362
|
+
actor = self.user_manager.get_user_by_id(user_id=user_id)
|
|
1363
|
+
if actor is None:
|
|
1233
1364
|
raise ValueError(f"User user_id={user_id} does not exist")
|
|
1234
1365
|
if self.ms.get_agent(agent_id=agent_id, user_id=user_id) is None:
|
|
1235
1366
|
raise ValueError(f"Agent agent_id={agent_id} does not exist")
|
|
@@ -1241,7 +1372,7 @@ class SyncServer(Server):
|
|
|
1241
1372
|
|
|
1242
1373
|
# Delete by ID
|
|
1243
1374
|
# TODO check if it exists first, and throw error if not
|
|
1244
|
-
letta_agent.
|
|
1375
|
+
letta_agent.passage_manager.delete_passage_by_id(passage_id=memory_id, actor=actor)
|
|
1245
1376
|
|
|
1246
1377
|
# TODO: return archival memory
|
|
1247
1378
|
|
|
@@ -1387,6 +1518,12 @@ class SyncServer(Server):
|
|
|
1387
1518
|
except NoResultFound:
|
|
1388
1519
|
logger.error(f"Agent with id {agent_state.id} has nonexistent user {agent_state.user_id}")
|
|
1389
1520
|
|
|
1521
|
+
# delete all passages associated with this agent
|
|
1522
|
+
# TODO: REMOVE THIS ONCE WE MIGRATE AGENTMODEL TO ORM
|
|
1523
|
+
passages = self.passage_manager.list_passages(actor=actor, agent_id=agent_state.id)
|
|
1524
|
+
for passage in passages:
|
|
1525
|
+
self.passage_manager.delete_passage_by_id(passage.id, actor=actor)
|
|
1526
|
+
|
|
1390
1527
|
# First, if the agent is in the in-memory cache we should remove it
|
|
1391
1528
|
# List of {'user_id': user_id, 'agent_id': agent_id, 'agent': agent_obj} dicts
|
|
1392
1529
|
try:
|
|
@@ -1429,7 +1566,7 @@ class SyncServer(Server):
|
|
|
1429
1566
|
self.ms.delete_api_key(api_key=api_key)
|
|
1430
1567
|
return api_key_obj
|
|
1431
1568
|
|
|
1432
|
-
def delete_source(self, source_id: str, actor:
|
|
1569
|
+
def delete_source(self, source_id: str, actor: PydanticUser):
|
|
1433
1570
|
"""Delete a data source"""
|
|
1434
1571
|
self.source_manager.delete_source(source_id=source_id, actor=actor)
|
|
1435
1572
|
|
|
@@ -1439,7 +1576,7 @@ class SyncServer(Server):
|
|
|
1439
1576
|
|
|
1440
1577
|
# TODO: delete data from agent passage stores (?)
|
|
1441
1578
|
|
|
1442
|
-
def load_file_to_source(self, source_id: str, file_path: str, job_id: str, actor:
|
|
1579
|
+
def load_file_to_source(self, source_id: str, file_path: str, job_id: str, actor: PydanticUser) -> Job:
|
|
1443
1580
|
|
|
1444
1581
|
# update job
|
|
1445
1582
|
job = self.job_manager.get_job_by_id(job_id, actor=actor)
|
|
@@ -1466,6 +1603,7 @@ class SyncServer(Server):
|
|
|
1466
1603
|
user_id: str,
|
|
1467
1604
|
connector: DataConnector,
|
|
1468
1605
|
source_name: str,
|
|
1606
|
+
agent_id: Optional[str] = None,
|
|
1469
1607
|
) -> Tuple[int, int]:
|
|
1470
1608
|
"""Load data from a DataConnector into a source for a specified user_id"""
|
|
1471
1609
|
# TODO: this should be implemented as a batch job or at least async, since it may take a long time
|
|
@@ -1480,14 +1618,13 @@ class SyncServer(Server):
|
|
|
1480
1618
|
passage_store = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
|
|
1481
1619
|
|
|
1482
1620
|
# load data into the document store
|
|
1483
|
-
passage_count, document_count = load_data(connector, source, passage_store, self.source_manager, actor=user)
|
|
1621
|
+
passage_count, document_count = load_data(connector, source, passage_store, self.source_manager, actor=user, agent_id=agent_id)
|
|
1484
1622
|
return passage_count, document_count
|
|
1485
1623
|
|
|
1486
1624
|
def attach_source_to_agent(
|
|
1487
1625
|
self,
|
|
1488
1626
|
user_id: str,
|
|
1489
1627
|
agent_id: str,
|
|
1490
|
-
# source_id: str,
|
|
1491
1628
|
source_id: Optional[str] = None,
|
|
1492
1629
|
source_name: Optional[str] = None,
|
|
1493
1630
|
) -> Source:
|
|
@@ -1499,15 +1636,14 @@ class SyncServer(Server):
|
|
|
1499
1636
|
data_source = self.source_manager.get_source_by_name(source_name=source_name, actor=user)
|
|
1500
1637
|
else:
|
|
1501
1638
|
raise ValueError(f"Need to provide at least source_id or source_name to find the source.")
|
|
1502
|
-
|
|
1503
|
-
source_connector = StorageConnector.get_storage_connector(TableType.PASSAGES, self.config, user_id=user_id)
|
|
1639
|
+
|
|
1504
1640
|
assert data_source, f"Data source with id={source_id} or name={source_name} does not exist"
|
|
1505
1641
|
|
|
1506
1642
|
# load agent
|
|
1507
1643
|
agent = self.load_agent(agent_id=agent_id)
|
|
1508
1644
|
|
|
1509
1645
|
# attach source to agent
|
|
1510
|
-
agent.attach_source(data_source.id,
|
|
1646
|
+
agent.attach_source(user=user, source_id=data_source.id, source_manager=self.source_manager, ms=self.ms)
|
|
1511
1647
|
|
|
1512
1648
|
return data_source
|
|
1513
1649
|
|
|
@@ -1530,8 +1666,7 @@ class SyncServer(Server):
|
|
|
1530
1666
|
|
|
1531
1667
|
# delete all Passage objects with source_id==source_id from agent's archival memory
|
|
1532
1668
|
agent = self.load_agent(agent_id=agent_id)
|
|
1533
|
-
|
|
1534
|
-
archival_memory.storage.delete({"source_id": source_id})
|
|
1669
|
+
agent.passage_manager.delete_passages(actor=user, limit=100, source_id=source_id)
|
|
1535
1670
|
|
|
1536
1671
|
# delete agent-source mapping
|
|
1537
1672
|
self.ms.detach_source(agent_id=agent_id, source_id=source_id)
|
|
@@ -1545,11 +1680,11 @@ class SyncServer(Server):
|
|
|
1545
1680
|
|
|
1546
1681
|
return [self.source_manager.get_source_by_id(source_id=id) for id in source_ids]
|
|
1547
1682
|
|
|
1548
|
-
def list_data_source_passages(self, user_id: str, source_id: str) -> List[
|
|
1683
|
+
def list_data_source_passages(self, user_id: str, source_id: str) -> List[PydanticPassage]:
|
|
1549
1684
|
warnings.warn("list_data_source_passages is not yet implemented, returning empty list.", category=UserWarning)
|
|
1550
1685
|
return []
|
|
1551
1686
|
|
|
1552
|
-
def list_all_sources(self, actor:
|
|
1687
|
+
def list_all_sources(self, actor: PydanticUser) -> List[Source]:
|
|
1553
1688
|
"""List all sources (w/ extra metadata) belonging to a user"""
|
|
1554
1689
|
|
|
1555
1690
|
sources = self.source_manager.list_sources(actor=actor)
|
|
@@ -1589,7 +1724,7 @@ class SyncServer(Server):
|
|
|
1589
1724
|
|
|
1590
1725
|
return sources_with_metadata
|
|
1591
1726
|
|
|
1592
|
-
def add_default_external_tools(self, actor:
|
|
1727
|
+
def add_default_external_tools(self, actor: PydanticUser) -> bool:
|
|
1593
1728
|
"""Add default langchain tools. Return true if successful, false otherwise."""
|
|
1594
1729
|
success = True
|
|
1595
1730
|
tool_creates = ToolCreate.load_default_langchain_tools()
|
|
@@ -1646,7 +1781,7 @@ class SyncServer(Server):
|
|
|
1646
1781
|
save_agent(letta_agent, self.ms)
|
|
1647
1782
|
return response
|
|
1648
1783
|
|
|
1649
|
-
def get_user_or_default(self, user_id: Optional[str]) ->
|
|
1784
|
+
def get_user_or_default(self, user_id: Optional[str]) -> PydanticUser:
|
|
1650
1785
|
"""Get the user object for user_id if it exists, otherwise return the default user object"""
|
|
1651
1786
|
if user_id is None:
|
|
1652
1787
|
user_id = self.user_manager.DEFAULT_USER_ID
|
|
@@ -1827,6 +1962,8 @@ class SyncServer(Server):
|
|
|
1827
1962
|
date=get_utc_time(),
|
|
1828
1963
|
status="success",
|
|
1829
1964
|
function_return=function_response,
|
|
1965
|
+
stdout=sandbox_run_result.stdout,
|
|
1966
|
+
stderr=sandbox_run_result.stderr,
|
|
1830
1967
|
)
|
|
1831
1968
|
except Exception as e:
|
|
1832
1969
|
# same as agent.py
|
|
@@ -1842,6 +1979,8 @@ class SyncServer(Server):
|
|
|
1842
1979
|
date=get_utc_time(),
|
|
1843
1980
|
status="error",
|
|
1844
1981
|
function_return=error_msg,
|
|
1982
|
+
stdout=[""],
|
|
1983
|
+
stderr=[traceback.format_exc()],
|
|
1845
1984
|
)
|
|
1846
1985
|
|
|
1847
1986
|
# Composio wrappers
|