letta-nightly 0.8.2.dev20250606215616__py3-none-any.whl → 0.8.3.dev20250607000559__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.
- letta/__init__.py +1 -1
- letta/agent.py +15 -11
- letta/agents/base_agent.py +1 -1
- letta/agents/helpers.py +13 -2
- letta/agents/letta_agent.py +23 -5
- letta/agents/voice_agent.py +1 -1
- letta/agents/voice_sleeptime_agent.py +12 -3
- letta/groups/sleeptime_multi_agent_v2.py +12 -2
- letta/llm_api/anthropic_client.py +8 -2
- letta/orm/passage.py +2 -0
- letta/schemas/letta_request.py +6 -0
- letta/schemas/passage.py +1 -0
- letta/server/rest_api/routers/v1/agents.py +9 -1
- letta/server/rest_api/routers/v1/tools.py +7 -2
- letta/server/server.py +7 -1
- letta/services/agent_manager.py +3 -3
- letta/services/context_window_calculator/context_window_calculator.py +1 -1
- letta/services/file_processor/file_processor.py +3 -1
- letta/services/helpers/agent_manager_helper.py +35 -4
- letta/services/mcp/stdio_client.py +5 -1
- letta/services/mcp_manager.py +4 -4
- letta/services/passage_manager.py +603 -18
- letta/services/tool_executor/files_tool_executor.py +9 -2
- letta/settings.py +2 -1
- {letta_nightly-0.8.2.dev20250606215616.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/METADATA +1 -1
- {letta_nightly-0.8.2.dev20250606215616.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/RECORD +29 -29
- {letta_nightly-0.8.2.dev20250606215616.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.2.dev20250606215616.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.2.dev20250606215616.dist-info → letta_nightly-0.8.3.dev20250607000559.dist-info}/entry_points.txt +0 -0
@@ -13,6 +13,7 @@ from letta.orm.errors import NoResultFound
|
|
13
13
|
from letta.orm.passage import AgentPassage, SourcePassage
|
14
14
|
from letta.otel.tracing import trace_method
|
15
15
|
from letta.schemas.agent import AgentState
|
16
|
+
from letta.schemas.file import FileMetadata as PydanticFileMetadata
|
16
17
|
from letta.schemas.passage import Passage as PydanticPassage
|
17
18
|
from letta.schemas.user import User as PydanticUser
|
18
19
|
from letta.server.db import db_registry
|
@@ -42,10 +43,65 @@ async def get_openai_embedding_async(text: str, model: str, endpoint: str) -> Li
|
|
42
43
|
class PassageManager:
|
43
44
|
"""Manager class to handle business logic related to Passages."""
|
44
45
|
|
46
|
+
# AGENT PASSAGE METHODS
|
47
|
+
@enforce_types
|
48
|
+
@trace_method
|
49
|
+
def get_agent_passage_by_id(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
50
|
+
"""Fetch an agent passage by ID."""
|
51
|
+
with db_registry.session() as session:
|
52
|
+
try:
|
53
|
+
passage = AgentPassage.read(db_session=session, identifier=passage_id, actor=actor)
|
54
|
+
return passage.to_pydantic()
|
55
|
+
except NoResultFound:
|
56
|
+
raise NoResultFound(f"Agent passage with id {passage_id} not found in database.")
|
57
|
+
|
58
|
+
@enforce_types
|
59
|
+
@trace_method
|
60
|
+
async def get_agent_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
61
|
+
"""Fetch an agent passage by ID."""
|
62
|
+
async with db_registry.async_session() as session:
|
63
|
+
try:
|
64
|
+
passage = await AgentPassage.read_async(db_session=session, identifier=passage_id, actor=actor)
|
65
|
+
return passage.to_pydantic()
|
66
|
+
except NoResultFound:
|
67
|
+
raise NoResultFound(f"Agent passage with id {passage_id} not found in database.")
|
68
|
+
|
69
|
+
# SOURCE PASSAGE METHODS
|
70
|
+
@enforce_types
|
71
|
+
@trace_method
|
72
|
+
def get_source_passage_by_id(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
73
|
+
"""Fetch a source passage by ID."""
|
74
|
+
with db_registry.session() as session:
|
75
|
+
try:
|
76
|
+
passage = SourcePassage.read(db_session=session, identifier=passage_id, actor=actor)
|
77
|
+
return passage.to_pydantic()
|
78
|
+
except NoResultFound:
|
79
|
+
raise NoResultFound(f"Source passage with id {passage_id} not found in database.")
|
80
|
+
|
81
|
+
@enforce_types
|
82
|
+
@trace_method
|
83
|
+
async def get_source_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
84
|
+
"""Fetch a source passage by ID."""
|
85
|
+
async with db_registry.async_session() as session:
|
86
|
+
try:
|
87
|
+
passage = await SourcePassage.read_async(db_session=session, identifier=passage_id, actor=actor)
|
88
|
+
return passage.to_pydantic()
|
89
|
+
except NoResultFound:
|
90
|
+
raise NoResultFound(f"Source passage with id {passage_id} not found in database.")
|
91
|
+
|
92
|
+
# DEPRECATED - Use specific methods above
|
45
93
|
@enforce_types
|
46
94
|
@trace_method
|
47
95
|
def get_passage_by_id(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
48
|
-
"""
|
96
|
+
"""DEPRECATED: Use get_agent_passage_by_id() or get_source_passage_by_id() instead."""
|
97
|
+
import warnings
|
98
|
+
|
99
|
+
warnings.warn(
|
100
|
+
"get_passage_by_id is deprecated. Use get_agent_passage_by_id() or get_source_passage_by_id() instead.",
|
101
|
+
DeprecationWarning,
|
102
|
+
stacklevel=2,
|
103
|
+
)
|
104
|
+
|
49
105
|
with db_registry.session() as session:
|
50
106
|
# Try source passages first
|
51
107
|
try:
|
@@ -62,7 +118,15 @@ class PassageManager:
|
|
62
118
|
@enforce_types
|
63
119
|
@trace_method
|
64
120
|
async def get_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> Optional[PydanticPassage]:
|
65
|
-
"""
|
121
|
+
"""DEPRECATED: Use get_agent_passage_by_id_async() or get_source_passage_by_id_async() instead."""
|
122
|
+
import warnings
|
123
|
+
|
124
|
+
warnings.warn(
|
125
|
+
"get_passage_by_id_async is deprecated. Use get_agent_passage_by_id_async() or get_source_passage_by_id_async() instead.",
|
126
|
+
DeprecationWarning,
|
127
|
+
stacklevel=2,
|
128
|
+
)
|
129
|
+
|
66
130
|
async with db_registry.async_session() as session:
|
67
131
|
# Try source passages first
|
68
132
|
try:
|
@@ -76,10 +140,137 @@ class PassageManager:
|
|
76
140
|
except NoResultFound:
|
77
141
|
raise NoResultFound(f"Passage with id {passage_id} not found in database.")
|
78
142
|
|
143
|
+
@enforce_types
|
144
|
+
@trace_method
|
145
|
+
def create_agent_passage(self, pydantic_passage: PydanticPassage, actor: PydanticUser) -> PydanticPassage:
|
146
|
+
"""Create a new agent passage."""
|
147
|
+
if not pydantic_passage.agent_id:
|
148
|
+
raise ValueError("Agent passage must have agent_id")
|
149
|
+
if pydantic_passage.source_id:
|
150
|
+
raise ValueError("Agent passage cannot have source_id")
|
151
|
+
|
152
|
+
data = pydantic_passage.model_dump(to_orm=True)
|
153
|
+
common_fields = {
|
154
|
+
"id": data.get("id"),
|
155
|
+
"text": data["text"],
|
156
|
+
"embedding": data["embedding"],
|
157
|
+
"embedding_config": data["embedding_config"],
|
158
|
+
"organization_id": data["organization_id"],
|
159
|
+
"metadata_": data.get("metadata", {}),
|
160
|
+
"is_deleted": data.get("is_deleted", False),
|
161
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
162
|
+
}
|
163
|
+
agent_fields = {"agent_id": data["agent_id"]}
|
164
|
+
passage = AgentPassage(**common_fields, **agent_fields)
|
165
|
+
|
166
|
+
with db_registry.session() as session:
|
167
|
+
passage.create(session, actor=actor)
|
168
|
+
return passage.to_pydantic()
|
169
|
+
|
170
|
+
@enforce_types
|
171
|
+
@trace_method
|
172
|
+
async def create_agent_passage_async(self, pydantic_passage: PydanticPassage, actor: PydanticUser) -> PydanticPassage:
|
173
|
+
"""Create a new agent passage."""
|
174
|
+
if not pydantic_passage.agent_id:
|
175
|
+
raise ValueError("Agent passage must have agent_id")
|
176
|
+
if pydantic_passage.source_id:
|
177
|
+
raise ValueError("Agent passage cannot have source_id")
|
178
|
+
|
179
|
+
data = pydantic_passage.model_dump(to_orm=True)
|
180
|
+
common_fields = {
|
181
|
+
"id": data.get("id"),
|
182
|
+
"text": data["text"],
|
183
|
+
"embedding": data["embedding"],
|
184
|
+
"embedding_config": data["embedding_config"],
|
185
|
+
"organization_id": data["organization_id"],
|
186
|
+
"metadata_": data.get("metadata", {}),
|
187
|
+
"is_deleted": data.get("is_deleted", False),
|
188
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
189
|
+
}
|
190
|
+
agent_fields = {"agent_id": data["agent_id"]}
|
191
|
+
passage = AgentPassage(**common_fields, **agent_fields)
|
192
|
+
|
193
|
+
async with db_registry.async_session() as session:
|
194
|
+
passage = await passage.create_async(session, actor=actor)
|
195
|
+
return passage.to_pydantic()
|
196
|
+
|
197
|
+
@enforce_types
|
198
|
+
@trace_method
|
199
|
+
def create_source_passage(
|
200
|
+
self, pydantic_passage: PydanticPassage, file_metadata: PydanticFileMetadata, actor: PydanticUser
|
201
|
+
) -> PydanticPassage:
|
202
|
+
"""Create a new source passage."""
|
203
|
+
if not pydantic_passage.source_id:
|
204
|
+
raise ValueError("Source passage must have source_id")
|
205
|
+
if pydantic_passage.agent_id:
|
206
|
+
raise ValueError("Source passage cannot have agent_id")
|
207
|
+
|
208
|
+
data = pydantic_passage.model_dump(to_orm=True)
|
209
|
+
common_fields = {
|
210
|
+
"id": data.get("id"),
|
211
|
+
"text": data["text"],
|
212
|
+
"embedding": data["embedding"],
|
213
|
+
"embedding_config": data["embedding_config"],
|
214
|
+
"organization_id": data["organization_id"],
|
215
|
+
"metadata_": data.get("metadata", {}),
|
216
|
+
"is_deleted": data.get("is_deleted", False),
|
217
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
218
|
+
}
|
219
|
+
source_fields = {
|
220
|
+
"source_id": data["source_id"],
|
221
|
+
"file_id": data.get("file_id"),
|
222
|
+
"file_name": file_metadata.file_name,
|
223
|
+
}
|
224
|
+
passage = SourcePassage(**common_fields, **source_fields)
|
225
|
+
|
226
|
+
with db_registry.session() as session:
|
227
|
+
passage.create(session, actor=actor)
|
228
|
+
return passage.to_pydantic()
|
229
|
+
|
230
|
+
@enforce_types
|
231
|
+
@trace_method
|
232
|
+
async def create_source_passage_async(
|
233
|
+
self, pydantic_passage: PydanticPassage, file_metadata: PydanticFileMetadata, actor: PydanticUser
|
234
|
+
) -> PydanticPassage:
|
235
|
+
"""Create a new source passage."""
|
236
|
+
if not pydantic_passage.source_id:
|
237
|
+
raise ValueError("Source passage must have source_id")
|
238
|
+
if pydantic_passage.agent_id:
|
239
|
+
raise ValueError("Source passage cannot have agent_id")
|
240
|
+
|
241
|
+
data = pydantic_passage.model_dump(to_orm=True)
|
242
|
+
common_fields = {
|
243
|
+
"id": data.get("id"),
|
244
|
+
"text": data["text"],
|
245
|
+
"embedding": data["embedding"],
|
246
|
+
"embedding_config": data["embedding_config"],
|
247
|
+
"organization_id": data["organization_id"],
|
248
|
+
"metadata_": data.get("metadata", {}),
|
249
|
+
"is_deleted": data.get("is_deleted", False),
|
250
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
251
|
+
}
|
252
|
+
source_fields = {
|
253
|
+
"source_id": data["source_id"],
|
254
|
+
"file_id": data.get("file_id"),
|
255
|
+
"file_name": file_metadata.file_name,
|
256
|
+
}
|
257
|
+
passage = SourcePassage(**common_fields, **source_fields)
|
258
|
+
|
259
|
+
async with db_registry.async_session() as session:
|
260
|
+
passage = await passage.create_async(session, actor=actor)
|
261
|
+
return passage.to_pydantic()
|
262
|
+
|
263
|
+
# DEPRECATED - Use specific methods above
|
79
264
|
@enforce_types
|
80
265
|
@trace_method
|
81
266
|
def create_passage(self, pydantic_passage: PydanticPassage, actor: PydanticUser) -> PydanticPassage:
|
82
|
-
"""
|
267
|
+
"""DEPRECATED: Use create_agent_passage() or create_source_passage() instead."""
|
268
|
+
import warnings
|
269
|
+
|
270
|
+
warnings.warn(
|
271
|
+
"create_passage is deprecated. Use create_agent_passage() or create_source_passage() instead.", DeprecationWarning, stacklevel=2
|
272
|
+
)
|
273
|
+
|
83
274
|
passage = self._preprocess_passage_for_creation(pydantic_passage=pydantic_passage)
|
84
275
|
|
85
276
|
with db_registry.session() as session:
|
@@ -89,7 +280,15 @@ class PassageManager:
|
|
89
280
|
@enforce_types
|
90
281
|
@trace_method
|
91
282
|
async def create_passage_async(self, pydantic_passage: PydanticPassage, actor: PydanticUser) -> PydanticPassage:
|
92
|
-
"""
|
283
|
+
"""DEPRECATED: Use create_agent_passage_async() or create_source_passage_async() instead."""
|
284
|
+
import warnings
|
285
|
+
|
286
|
+
warnings.warn(
|
287
|
+
"create_passage_async is deprecated. Use create_agent_passage_async() or create_source_passage_async() instead.",
|
288
|
+
DeprecationWarning,
|
289
|
+
stacklevel=2,
|
290
|
+
)
|
291
|
+
|
93
292
|
# Common fields for both passage types
|
94
293
|
passage = self._preprocess_passage_for_creation(pydantic_passage=pydantic_passage)
|
95
294
|
async with db_registry.async_session() as session:
|
@@ -128,16 +327,110 @@ class PassageManager:
|
|
128
327
|
|
129
328
|
return passage
|
130
329
|
|
330
|
+
@enforce_types
|
331
|
+
@trace_method
|
332
|
+
def create_many_agent_passages(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]:
|
333
|
+
"""Create multiple agent passages."""
|
334
|
+
return [self.create_agent_passage(p, actor) for p in passages]
|
335
|
+
|
336
|
+
@enforce_types
|
337
|
+
@trace_method
|
338
|
+
async def create_many_agent_passages_async(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]:
|
339
|
+
"""Create multiple agent passages."""
|
340
|
+
agent_passages = []
|
341
|
+
for p in passages:
|
342
|
+
if not p.agent_id:
|
343
|
+
raise ValueError("Agent passage must have agent_id")
|
344
|
+
if p.source_id:
|
345
|
+
raise ValueError("Agent passage cannot have source_id")
|
346
|
+
|
347
|
+
data = p.model_dump(to_orm=True)
|
348
|
+
common_fields = {
|
349
|
+
"id": data.get("id"),
|
350
|
+
"text": data["text"],
|
351
|
+
"embedding": data["embedding"],
|
352
|
+
"embedding_config": data["embedding_config"],
|
353
|
+
"organization_id": data["organization_id"],
|
354
|
+
"metadata_": data.get("metadata", {}),
|
355
|
+
"is_deleted": data.get("is_deleted", False),
|
356
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
357
|
+
}
|
358
|
+
agent_fields = {"agent_id": data["agent_id"]}
|
359
|
+
agent_passages.append(AgentPassage(**common_fields, **agent_fields))
|
360
|
+
|
361
|
+
async with db_registry.async_session() as session:
|
362
|
+
agent_created = await AgentPassage.batch_create_async(items=agent_passages, db_session=session, actor=actor)
|
363
|
+
return [p.to_pydantic() for p in agent_created]
|
364
|
+
|
365
|
+
@enforce_types
|
366
|
+
@trace_method
|
367
|
+
def create_many_source_passages(
|
368
|
+
self, passages: List[PydanticPassage], file_metadata: PydanticFileMetadata, actor: PydanticUser
|
369
|
+
) -> List[PydanticPassage]:
|
370
|
+
"""Create multiple source passages."""
|
371
|
+
return [self.create_source_passage(p, file_metadata, actor) for p in passages]
|
372
|
+
|
373
|
+
@enforce_types
|
374
|
+
@trace_method
|
375
|
+
async def create_many_source_passages_async(
|
376
|
+
self, passages: List[PydanticPassage], file_metadata: PydanticFileMetadata, actor: PydanticUser
|
377
|
+
) -> List[PydanticPassage]:
|
378
|
+
"""Create multiple source passages."""
|
379
|
+
source_passages = []
|
380
|
+
for p in passages:
|
381
|
+
if not p.source_id:
|
382
|
+
raise ValueError("Source passage must have source_id")
|
383
|
+
if p.agent_id:
|
384
|
+
raise ValueError("Source passage cannot have agent_id")
|
385
|
+
|
386
|
+
data = p.model_dump(to_orm=True)
|
387
|
+
common_fields = {
|
388
|
+
"id": data.get("id"),
|
389
|
+
"text": data["text"],
|
390
|
+
"embedding": data["embedding"],
|
391
|
+
"embedding_config": data["embedding_config"],
|
392
|
+
"organization_id": data["organization_id"],
|
393
|
+
"metadata_": data.get("metadata", {}),
|
394
|
+
"is_deleted": data.get("is_deleted", False),
|
395
|
+
"created_at": data.get("created_at", datetime.now(timezone.utc)),
|
396
|
+
}
|
397
|
+
source_fields = {
|
398
|
+
"source_id": data["source_id"],
|
399
|
+
"file_id": data.get("file_id"),
|
400
|
+
"file_name": file_metadata.file_name,
|
401
|
+
}
|
402
|
+
source_passages.append(SourcePassage(**common_fields, **source_fields))
|
403
|
+
|
404
|
+
async with db_registry.async_session() as session:
|
405
|
+
source_created = await SourcePassage.batch_create_async(items=source_passages, db_session=session, actor=actor)
|
406
|
+
return [p.to_pydantic() for p in source_created]
|
407
|
+
|
408
|
+
# DEPRECATED - Use specific methods above
|
131
409
|
@enforce_types
|
132
410
|
@trace_method
|
133
411
|
def create_many_passages(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]:
|
134
|
-
"""
|
412
|
+
"""DEPRECATED: Use create_many_agent_passages() or create_many_source_passages() instead."""
|
413
|
+
import warnings
|
414
|
+
|
415
|
+
warnings.warn(
|
416
|
+
"create_many_passages is deprecated. Use create_many_agent_passages() or create_many_source_passages() instead.",
|
417
|
+
DeprecationWarning,
|
418
|
+
stacklevel=2,
|
419
|
+
)
|
135
420
|
return [self.create_passage(p, actor) for p in passages]
|
136
421
|
|
137
422
|
@enforce_types
|
138
423
|
@trace_method
|
139
424
|
async def create_many_passages_async(self, passages: List[PydanticPassage], actor: PydanticUser) -> List[PydanticPassage]:
|
140
|
-
"""
|
425
|
+
"""DEPRECATED: Use create_many_agent_passages_async() or create_many_source_passages_async() instead."""
|
426
|
+
import warnings
|
427
|
+
|
428
|
+
warnings.warn(
|
429
|
+
"create_many_passages_async is deprecated. Use create_many_agent_passages_async() or create_many_source_passages_async() instead.",
|
430
|
+
DeprecationWarning,
|
431
|
+
stacklevel=2,
|
432
|
+
)
|
433
|
+
|
141
434
|
async with db_registry.async_session() as session:
|
142
435
|
agent_passages = []
|
143
436
|
source_passages = []
|
@@ -203,7 +496,7 @@ class PassageManager:
|
|
203
496
|
raise TypeError(
|
204
497
|
f"Got back an unexpected payload from text embedding function, type={type(embedding)}, value={embedding}"
|
205
498
|
)
|
206
|
-
passage = self.
|
499
|
+
passage = self.create_agent_passage(
|
207
500
|
PydanticPassage(
|
208
501
|
organization_id=actor.organization_id,
|
209
502
|
agent_id=agent_id,
|
@@ -251,7 +544,7 @@ class PassageManager:
|
|
251
544
|
for chunk_text, embedding in zip(text_chunks, embeddings)
|
252
545
|
]
|
253
546
|
|
254
|
-
passages = await self.
|
547
|
+
passages = await self.create_many_agent_passages_async(passages=passages, actor=actor)
|
255
548
|
|
256
549
|
return passages
|
257
550
|
|
@@ -292,10 +585,191 @@ class PassageManager:
|
|
292
585
|
|
293
586
|
return processed_embeddings
|
294
587
|
|
588
|
+
@enforce_types
|
589
|
+
@trace_method
|
590
|
+
def update_agent_passage_by_id(
|
591
|
+
self, passage_id: str, passage: PydanticPassage, actor: PydanticUser, **kwargs
|
592
|
+
) -> Optional[PydanticPassage]:
|
593
|
+
"""Update an agent passage."""
|
594
|
+
if not passage_id:
|
595
|
+
raise ValueError("Passage ID must be provided.")
|
596
|
+
|
597
|
+
with db_registry.session() as session:
|
598
|
+
try:
|
599
|
+
curr_passage = AgentPassage.read(
|
600
|
+
db_session=session,
|
601
|
+
identifier=passage_id,
|
602
|
+
actor=actor,
|
603
|
+
)
|
604
|
+
except NoResultFound:
|
605
|
+
raise ValueError(f"Agent passage with id {passage_id} does not exist.")
|
606
|
+
|
607
|
+
# Update the database record with values from the provided record
|
608
|
+
update_data = passage.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
609
|
+
for key, value in update_data.items():
|
610
|
+
setattr(curr_passage, key, value)
|
611
|
+
|
612
|
+
# Commit changes
|
613
|
+
curr_passage.update(session, actor=actor)
|
614
|
+
return curr_passage.to_pydantic()
|
615
|
+
|
616
|
+
@enforce_types
|
617
|
+
@trace_method
|
618
|
+
async def update_agent_passage_by_id_async(
|
619
|
+
self, passage_id: str, passage: PydanticPassage, actor: PydanticUser, **kwargs
|
620
|
+
) -> Optional[PydanticPassage]:
|
621
|
+
"""Update an agent passage."""
|
622
|
+
if not passage_id:
|
623
|
+
raise ValueError("Passage ID must be provided.")
|
624
|
+
|
625
|
+
async with db_registry.async_session() as session:
|
626
|
+
try:
|
627
|
+
curr_passage = await AgentPassage.read_async(
|
628
|
+
db_session=session,
|
629
|
+
identifier=passage_id,
|
630
|
+
actor=actor,
|
631
|
+
)
|
632
|
+
except NoResultFound:
|
633
|
+
raise ValueError(f"Agent passage with id {passage_id} does not exist.")
|
634
|
+
|
635
|
+
# Update the database record with values from the provided record
|
636
|
+
update_data = passage.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
637
|
+
for key, value in update_data.items():
|
638
|
+
setattr(curr_passage, key, value)
|
639
|
+
|
640
|
+
# Commit changes
|
641
|
+
await curr_passage.update_async(session, actor=actor)
|
642
|
+
return curr_passage.to_pydantic()
|
643
|
+
|
644
|
+
@enforce_types
|
645
|
+
@trace_method
|
646
|
+
def update_source_passage_by_id(
|
647
|
+
self, passage_id: str, passage: PydanticPassage, actor: PydanticUser, **kwargs
|
648
|
+
) -> Optional[PydanticPassage]:
|
649
|
+
"""Update a source passage."""
|
650
|
+
if not passage_id:
|
651
|
+
raise ValueError("Passage ID must be provided.")
|
652
|
+
|
653
|
+
with db_registry.session() as session:
|
654
|
+
try:
|
655
|
+
curr_passage = SourcePassage.read(
|
656
|
+
db_session=session,
|
657
|
+
identifier=passage_id,
|
658
|
+
actor=actor,
|
659
|
+
)
|
660
|
+
except NoResultFound:
|
661
|
+
raise ValueError(f"Source passage with id {passage_id} does not exist.")
|
662
|
+
|
663
|
+
# Update the database record with values from the provided record
|
664
|
+
update_data = passage.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
665
|
+
for key, value in update_data.items():
|
666
|
+
setattr(curr_passage, key, value)
|
667
|
+
|
668
|
+
# Commit changes
|
669
|
+
curr_passage.update(session, actor=actor)
|
670
|
+
return curr_passage.to_pydantic()
|
671
|
+
|
672
|
+
@enforce_types
|
673
|
+
@trace_method
|
674
|
+
async def update_source_passage_by_id_async(
|
675
|
+
self, passage_id: str, passage: PydanticPassage, actor: PydanticUser, **kwargs
|
676
|
+
) -> Optional[PydanticPassage]:
|
677
|
+
"""Update a source passage."""
|
678
|
+
if not passage_id:
|
679
|
+
raise ValueError("Passage ID must be provided.")
|
680
|
+
|
681
|
+
async with db_registry.async_session() as session:
|
682
|
+
try:
|
683
|
+
curr_passage = await SourcePassage.read_async(
|
684
|
+
db_session=session,
|
685
|
+
identifier=passage_id,
|
686
|
+
actor=actor,
|
687
|
+
)
|
688
|
+
except NoResultFound:
|
689
|
+
raise ValueError(f"Source passage with id {passage_id} does not exist.")
|
690
|
+
|
691
|
+
# Update the database record with values from the provided record
|
692
|
+
update_data = passage.model_dump(to_orm=True, exclude_unset=True, exclude_none=True)
|
693
|
+
for key, value in update_data.items():
|
694
|
+
setattr(curr_passage, key, value)
|
695
|
+
|
696
|
+
# Commit changes
|
697
|
+
await curr_passage.update_async(session, actor=actor)
|
698
|
+
return curr_passage.to_pydantic()
|
699
|
+
|
700
|
+
@enforce_types
|
701
|
+
@trace_method
|
702
|
+
def delete_agent_passage_by_id(self, passage_id: str, actor: PydanticUser) -> bool:
|
703
|
+
"""Delete an agent passage."""
|
704
|
+
if not passage_id:
|
705
|
+
raise ValueError("Passage ID must be provided.")
|
706
|
+
|
707
|
+
with db_registry.session() as session:
|
708
|
+
try:
|
709
|
+
passage = AgentPassage.read(db_session=session, identifier=passage_id, actor=actor)
|
710
|
+
passage.hard_delete(session, actor=actor)
|
711
|
+
return True
|
712
|
+
except NoResultFound:
|
713
|
+
raise NoResultFound(f"Agent passage with id {passage_id} not found.")
|
714
|
+
|
715
|
+
@enforce_types
|
716
|
+
@trace_method
|
717
|
+
async def delete_agent_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> bool:
|
718
|
+
"""Delete an agent passage."""
|
719
|
+
if not passage_id:
|
720
|
+
raise ValueError("Passage ID must be provided.")
|
721
|
+
|
722
|
+
async with db_registry.async_session() as session:
|
723
|
+
try:
|
724
|
+
passage = await AgentPassage.read_async(db_session=session, identifier=passage_id, actor=actor)
|
725
|
+
await passage.hard_delete_async(session, actor=actor)
|
726
|
+
return True
|
727
|
+
except NoResultFound:
|
728
|
+
raise NoResultFound(f"Agent passage with id {passage_id} not found.")
|
729
|
+
|
730
|
+
@enforce_types
|
731
|
+
@trace_method
|
732
|
+
def delete_source_passage_by_id(self, passage_id: str, actor: PydanticUser) -> bool:
|
733
|
+
"""Delete a source passage."""
|
734
|
+
if not passage_id:
|
735
|
+
raise ValueError("Passage ID must be provided.")
|
736
|
+
|
737
|
+
with db_registry.session() as session:
|
738
|
+
try:
|
739
|
+
passage = SourcePassage.read(db_session=session, identifier=passage_id, actor=actor)
|
740
|
+
passage.hard_delete(session, actor=actor)
|
741
|
+
return True
|
742
|
+
except NoResultFound:
|
743
|
+
raise NoResultFound(f"Source passage with id {passage_id} not found.")
|
744
|
+
|
745
|
+
@enforce_types
|
746
|
+
@trace_method
|
747
|
+
async def delete_source_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> bool:
|
748
|
+
"""Delete a source passage."""
|
749
|
+
if not passage_id:
|
750
|
+
raise ValueError("Passage ID must be provided.")
|
751
|
+
|
752
|
+
async with db_registry.async_session() as session:
|
753
|
+
try:
|
754
|
+
passage = await SourcePassage.read_async(db_session=session, identifier=passage_id, actor=actor)
|
755
|
+
await passage.hard_delete_async(session, actor=actor)
|
756
|
+
return True
|
757
|
+
except NoResultFound:
|
758
|
+
raise NoResultFound(f"Source passage with id {passage_id} not found.")
|
759
|
+
|
760
|
+
# DEPRECATED - Use specific methods above
|
295
761
|
@enforce_types
|
296
762
|
@trace_method
|
297
763
|
def update_passage_by_id(self, passage_id: str, passage: PydanticPassage, actor: PydanticUser, **kwargs) -> Optional[PydanticPassage]:
|
298
|
-
"""
|
764
|
+
"""DEPRECATED: Use update_agent_passage_by_id() or update_source_passage_by_id() instead."""
|
765
|
+
import warnings
|
766
|
+
|
767
|
+
warnings.warn(
|
768
|
+
"update_passage_by_id is deprecated. Use update_agent_passage_by_id() or update_source_passage_by_id() instead.",
|
769
|
+
DeprecationWarning,
|
770
|
+
stacklevel=2,
|
771
|
+
)
|
772
|
+
|
299
773
|
if not passage_id:
|
300
774
|
raise ValueError("Passage ID must be provided.")
|
301
775
|
|
@@ -330,7 +804,15 @@ class PassageManager:
|
|
330
804
|
@enforce_types
|
331
805
|
@trace_method
|
332
806
|
def delete_passage_by_id(self, passage_id: str, actor: PydanticUser) -> bool:
|
333
|
-
"""
|
807
|
+
"""DEPRECATED: Use delete_agent_passage_by_id() or delete_source_passage_by_id() instead."""
|
808
|
+
import warnings
|
809
|
+
|
810
|
+
warnings.warn(
|
811
|
+
"delete_passage_by_id is deprecated. Use delete_agent_passage_by_id() or delete_source_passage_by_id() instead.",
|
812
|
+
DeprecationWarning,
|
813
|
+
stacklevel=2,
|
814
|
+
)
|
815
|
+
|
334
816
|
if not passage_id:
|
335
817
|
raise ValueError("Passage ID must be provided.")
|
336
818
|
|
@@ -352,7 +834,15 @@ class PassageManager:
|
|
352
834
|
@enforce_types
|
353
835
|
@trace_method
|
354
836
|
async def delete_passage_by_id_async(self, passage_id: str, actor: PydanticUser) -> bool:
|
355
|
-
"""
|
837
|
+
"""DEPRECATED: Use delete_agent_passage_by_id_async() or delete_source_passage_by_id_async() instead."""
|
838
|
+
import warnings
|
839
|
+
|
840
|
+
warnings.warn(
|
841
|
+
"delete_passage_by_id_async is deprecated. Use delete_agent_passage_by_id_async() or delete_source_passage_by_id_async() instead.",
|
842
|
+
DeprecationWarning,
|
843
|
+
stacklevel=2,
|
844
|
+
)
|
845
|
+
|
356
846
|
if not passage_id:
|
357
847
|
raise ValueError("Passage ID must be provided.")
|
358
848
|
|
@@ -373,15 +863,42 @@ class PassageManager:
|
|
373
863
|
|
374
864
|
@enforce_types
|
375
865
|
@trace_method
|
376
|
-
def
|
866
|
+
def delete_agent_passages(
|
377
867
|
self,
|
378
868
|
actor: PydanticUser,
|
379
869
|
passages: List[PydanticPassage],
|
380
870
|
) -> bool:
|
871
|
+
"""Delete multiple agent passages."""
|
381
872
|
# TODO: This is very inefficient
|
382
873
|
# TODO: We should have a base `delete_all_matching_filters`-esque function
|
383
874
|
for passage in passages:
|
384
|
-
self.
|
875
|
+
self.delete_agent_passage_by_id(passage_id=passage.id, actor=actor)
|
876
|
+
return True
|
877
|
+
|
878
|
+
@enforce_types
|
879
|
+
@trace_method
|
880
|
+
async def delete_agent_passages_async(
|
881
|
+
self,
|
882
|
+
actor: PydanticUser,
|
883
|
+
passages: List[PydanticPassage],
|
884
|
+
) -> bool:
|
885
|
+
"""Delete multiple agent passages."""
|
886
|
+
async with db_registry.async_session() as session:
|
887
|
+
await AgentPassage.bulk_hard_delete_async(db_session=session, identifiers=[p.id for p in passages], actor=actor)
|
888
|
+
return True
|
889
|
+
|
890
|
+
@enforce_types
|
891
|
+
@trace_method
|
892
|
+
def delete_source_passages(
|
893
|
+
self,
|
894
|
+
actor: PydanticUser,
|
895
|
+
passages: List[PydanticPassage],
|
896
|
+
) -> bool:
|
897
|
+
"""Delete multiple source passages."""
|
898
|
+
# TODO: This is very inefficient
|
899
|
+
# TODO: We should have a base `delete_all_matching_filters`-esque function
|
900
|
+
for passage in passages:
|
901
|
+
self.delete_source_passage_by_id(passage_id=passage.id, actor=actor)
|
385
902
|
return True
|
386
903
|
|
387
904
|
@enforce_types
|
@@ -395,14 +912,36 @@ class PassageManager:
|
|
395
912
|
await SourcePassage.bulk_hard_delete_async(db_session=session, identifiers=[p.id for p in passages], actor=actor)
|
396
913
|
return True
|
397
914
|
|
915
|
+
# DEPRECATED - Use specific methods above
|
398
916
|
@enforce_types
|
399
917
|
@trace_method
|
400
|
-
def
|
918
|
+
def delete_passages(
|
919
|
+
self,
|
920
|
+
actor: PydanticUser,
|
921
|
+
passages: List[PydanticPassage],
|
922
|
+
) -> bool:
|
923
|
+
"""DEPRECATED: Use delete_agent_passages() or delete_source_passages() instead."""
|
924
|
+
import warnings
|
925
|
+
|
926
|
+
warnings.warn(
|
927
|
+
"delete_passages is deprecated. Use delete_agent_passages() or delete_source_passages() instead.",
|
928
|
+
DeprecationWarning,
|
929
|
+
stacklevel=2,
|
930
|
+
)
|
931
|
+
# TODO: This is very inefficient
|
932
|
+
# TODO: We should have a base `delete_all_matching_filters`-esque function
|
933
|
+
for passage in passages:
|
934
|
+
self.delete_passage_by_id(passage_id=passage.id, actor=actor)
|
935
|
+
return True
|
936
|
+
|
937
|
+
@enforce_types
|
938
|
+
@trace_method
|
939
|
+
def agent_passage_size(
|
401
940
|
self,
|
402
941
|
actor: PydanticUser,
|
403
942
|
agent_id: Optional[str] = None,
|
404
943
|
) -> int:
|
405
|
-
"""Get the total count of
|
944
|
+
"""Get the total count of agent passages with optional filters.
|
406
945
|
|
407
946
|
Args:
|
408
947
|
actor: The user requesting the count
|
@@ -411,14 +950,29 @@ class PassageManager:
|
|
411
950
|
with db_registry.session() as session:
|
412
951
|
return AgentPassage.size(db_session=session, actor=actor, agent_id=agent_id)
|
413
952
|
|
953
|
+
# DEPRECATED - Use agent_passage_size() instead since this only counted agent passages anyway
|
954
|
+
@enforce_types
|
955
|
+
@trace_method
|
956
|
+
def size(
|
957
|
+
self,
|
958
|
+
actor: PydanticUser,
|
959
|
+
agent_id: Optional[str] = None,
|
960
|
+
) -> int:
|
961
|
+
"""DEPRECATED: Use agent_passage_size() instead (this only counted agent passages anyway)."""
|
962
|
+
import warnings
|
963
|
+
|
964
|
+
warnings.warn("size is deprecated. Use agent_passage_size() instead.", DeprecationWarning, stacklevel=2)
|
965
|
+
with db_registry.session() as session:
|
966
|
+
return AgentPassage.size(db_session=session, actor=actor, agent_id=agent_id)
|
967
|
+
|
414
968
|
@enforce_types
|
415
969
|
@trace_method
|
416
|
-
async def
|
970
|
+
async def agent_passage_size_async(
|
417
971
|
self,
|
418
972
|
actor: PydanticUser,
|
419
973
|
agent_id: Optional[str] = None,
|
420
974
|
) -> int:
|
421
|
-
"""Get the total count of
|
975
|
+
"""Get the total count of agent passages with optional filters.
|
422
976
|
Args:
|
423
977
|
actor: The user requesting the count
|
424
978
|
agent_id: The agent ID of the messages
|
@@ -426,6 +980,37 @@ class PassageManager:
|
|
426
980
|
async with db_registry.async_session() as session:
|
427
981
|
return await AgentPassage.size_async(db_session=session, actor=actor, agent_id=agent_id)
|
428
982
|
|
983
|
+
@enforce_types
|
984
|
+
@trace_method
|
985
|
+
def source_passage_size(
|
986
|
+
self,
|
987
|
+
actor: PydanticUser,
|
988
|
+
source_id: Optional[str] = None,
|
989
|
+
) -> int:
|
990
|
+
"""Get the total count of source passages with optional filters.
|
991
|
+
|
992
|
+
Args:
|
993
|
+
actor: The user requesting the count
|
994
|
+
source_id: The source ID of the passages
|
995
|
+
"""
|
996
|
+
with db_registry.session() as session:
|
997
|
+
return SourcePassage.size(db_session=session, actor=actor, source_id=source_id)
|
998
|
+
|
999
|
+
@enforce_types
|
1000
|
+
@trace_method
|
1001
|
+
async def source_passage_size_async(
|
1002
|
+
self,
|
1003
|
+
actor: PydanticUser,
|
1004
|
+
source_id: Optional[str] = None,
|
1005
|
+
) -> int:
|
1006
|
+
"""Get the total count of source passages with optional filters.
|
1007
|
+
Args:
|
1008
|
+
actor: The user requesting the count
|
1009
|
+
source_id: The source ID of the passages
|
1010
|
+
"""
|
1011
|
+
async with db_registry.async_session() as session:
|
1012
|
+
return await SourcePassage.size_async(db_session=session, actor=actor, source_id=source_id)
|
1013
|
+
|
429
1014
|
@enforce_types
|
430
1015
|
@trace_method
|
431
1016
|
async def estimate_embeddings_size_async(
|
@@ -448,7 +1033,7 @@ class PassageManager:
|
|
448
1033
|
raise ValueError(f"Invalid storage unit: {storage_unit}. Must be one of {list(BYTES_PER_STORAGE_UNIT.keys())}.")
|
449
1034
|
BYTES_PER_EMBEDDING_DIM = 4
|
450
1035
|
GB_PER_EMBEDDING = BYTES_PER_EMBEDDING_DIM / BYTES_PER_STORAGE_UNIT[storage_unit] * MAX_EMBEDDING_DIM
|
451
|
-
return await self.
|
1036
|
+
return await self.agent_passage_size_async(actor=actor, agent_id=agent_id) * GB_PER_EMBEDDING
|
452
1037
|
|
453
1038
|
@enforce_types
|
454
1039
|
@trace_method
|