letta-nightly 0.6.4.dev20241213193437__py3-none-any.whl → 0.6.4.dev20241215104129__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 +54 -45
- letta/chat_only_agent.py +6 -8
- letta/cli/cli.py +2 -10
- letta/client/client.py +121 -138
- letta/config.py +0 -161
- letta/main.py +3 -8
- letta/memory.py +3 -14
- letta/o1_agent.py +1 -5
- letta/offline_memory_agent.py +2 -6
- letta/orm/__init__.py +2 -0
- letta/orm/agent.py +109 -0
- letta/orm/agents_tags.py +10 -18
- letta/orm/block.py +29 -4
- letta/orm/blocks_agents.py +5 -11
- letta/orm/custom_columns.py +152 -0
- letta/orm/message.py +3 -38
- letta/orm/organization.py +2 -7
- letta/orm/passage.py +10 -32
- letta/orm/source.py +5 -25
- letta/orm/sources_agents.py +13 -0
- letta/orm/sqlalchemy_base.py +54 -30
- letta/orm/tool.py +1 -19
- letta/orm/tools_agents.py +7 -24
- letta/orm/user.py +3 -4
- letta/schemas/agent.py +48 -65
- letta/schemas/memory.py +2 -1
- letta/schemas/sandbox_config.py +12 -1
- letta/server/rest_api/app.py +0 -5
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +1 -1
- letta/server/rest_api/routers/v1/agents.py +99 -78
- letta/server/rest_api/routers/v1/blocks.py +22 -25
- letta/server/rest_api/routers/v1/jobs.py +4 -4
- letta/server/rest_api/routers/v1/sandbox_configs.py +10 -10
- letta/server/rest_api/routers/v1/sources.py +12 -12
- letta/server/rest_api/routers/v1/tools.py +35 -15
- letta/server/rest_api/routers/v1/users.py +0 -46
- letta/server/server.py +172 -716
- letta/server/ws_api/server.py +0 -5
- letta/services/agent_manager.py +405 -0
- letta/services/block_manager.py +13 -21
- letta/services/helpers/agent_manager_helper.py +90 -0
- letta/services/organization_manager.py +0 -1
- letta/services/passage_manager.py +62 -62
- letta/services/sandbox_config_manager.py +3 -3
- letta/services/source_manager.py +22 -1
- letta/services/user_manager.py +11 -6
- letta/utils.py +2 -2
- {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/METADATA +1 -1
- {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/RECORD +53 -57
- letta/metadata.py +0 -407
- letta/schemas/agents_tags.py +0 -33
- letta/schemas/api_key.py +0 -21
- letta/schemas/blocks_agents.py +0 -32
- letta/schemas/tools_agents.py +0 -32
- letta/server/rest_api/routers/openai/assistants/threads.py +0 -338
- letta/services/agents_tags_manager.py +0 -64
- letta/services/blocks_agents_manager.py +0 -106
- letta/services/tools_agents_manager.py +0 -94
- {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/LICENSE +0 -0
- {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/WHEEL +0 -0
- {letta_nightly-0.6.4.dev20241213193437.dist-info → letta_nightly-0.6.4.dev20241215104129.dist-info}/entry_points.txt +0 -0
|
@@ -17,7 +17,8 @@ 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.
|
|
20
|
+
from letta.orm.errors import NoResultFound
|
|
21
|
+
from letta.schemas.agent import AgentState, CreateAgent, UpdateAgent
|
|
21
22
|
from letta.schemas.block import ( # , BlockLabelUpdate, BlockLimitUpdate
|
|
22
23
|
Block,
|
|
23
24
|
BlockUpdate,
|
|
@@ -54,23 +55,38 @@ from letta.server.server import SyncServer
|
|
|
54
55
|
router = APIRouter(prefix="/agents", tags=["agents"])
|
|
55
56
|
|
|
56
57
|
|
|
58
|
+
# TODO: This should be paginated
|
|
57
59
|
@router.get("/", response_model=List[AgentState], operation_id="list_agents")
|
|
58
60
|
def list_agents(
|
|
59
61
|
name: Optional[str] = Query(None, description="Name of the agent"),
|
|
60
62
|
tags: Optional[List[str]] = Query(None, description="List of tags to filter agents by"),
|
|
63
|
+
match_all_tags: bool = Query(
|
|
64
|
+
False,
|
|
65
|
+
description="If True, only returns agents that match ALL given tags. Otherwise, return agents that have ANY of the passed in tags.",
|
|
66
|
+
),
|
|
61
67
|
server: "SyncServer" = Depends(get_letta_server),
|
|
62
|
-
user_id: Optional[str] = Header(None, alias="user_id"),
|
|
68
|
+
user_id: Optional[str] = Header(None, alias="user_id"),
|
|
69
|
+
# Extract user_id from header, default to None if not present
|
|
63
70
|
):
|
|
64
71
|
"""
|
|
65
72
|
List all agents associated with a given user.
|
|
66
73
|
This endpoint retrieves a list of all agents and their configurations associated with the specified user ID.
|
|
67
74
|
"""
|
|
68
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
76
|
+
|
|
77
|
+
# Use dictionary comprehension to build kwargs dynamically
|
|
78
|
+
kwargs = {
|
|
79
|
+
key: value
|
|
80
|
+
for key, value in {
|
|
81
|
+
"tags": tags,
|
|
82
|
+
"match_all_tags": match_all_tags,
|
|
83
|
+
"name": name,
|
|
84
|
+
}.items()
|
|
85
|
+
if value is not None
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Call list_agents with the dynamic kwargs
|
|
89
|
+
agents = server.agent_manager.list_agents(actor=actor, **kwargs)
|
|
74
90
|
return agents
|
|
75
91
|
|
|
76
92
|
|
|
@@ -83,7 +99,7 @@ def get_agent_context_window(
|
|
|
83
99
|
"""
|
|
84
100
|
Retrieve the context window of a specific agent.
|
|
85
101
|
"""
|
|
86
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
102
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
87
103
|
|
|
88
104
|
return server.get_agent_context_window(user_id=actor.id, agent_id=agent_id)
|
|
89
105
|
|
|
@@ -106,20 +122,20 @@ def create_agent(
|
|
|
106
122
|
"""
|
|
107
123
|
Create a new agent with the specified configuration.
|
|
108
124
|
"""
|
|
109
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
125
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
110
126
|
return server.create_agent(agent, actor=actor)
|
|
111
127
|
|
|
112
128
|
|
|
113
129
|
@router.patch("/{agent_id}", response_model=AgentState, operation_id="update_agent")
|
|
114
130
|
def update_agent(
|
|
115
131
|
agent_id: str,
|
|
116
|
-
update_agent:
|
|
132
|
+
update_agent: UpdateAgent = Body(...),
|
|
117
133
|
server: "SyncServer" = Depends(get_letta_server),
|
|
118
134
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
119
135
|
):
|
|
120
136
|
"""Update an exsiting agent"""
|
|
121
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
122
|
-
return server.update_agent(update_agent, actor=actor)
|
|
137
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
138
|
+
return server.update_agent(agent_id, update_agent, actor=actor)
|
|
123
139
|
|
|
124
140
|
|
|
125
141
|
@router.get("/{agent_id}/tools", response_model=List[Tool], operation_id="get_tools_from_agent")
|
|
@@ -129,7 +145,7 @@ def get_tools_from_agent(
|
|
|
129
145
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
130
146
|
):
|
|
131
147
|
"""Get tools from an existing agent"""
|
|
132
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
148
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
133
149
|
return server.get_tools_from_agent(agent_id=agent_id, user_id=actor.id)
|
|
134
150
|
|
|
135
151
|
|
|
@@ -141,7 +157,7 @@ def add_tool_to_agent(
|
|
|
141
157
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
142
158
|
):
|
|
143
159
|
"""Add tools to an existing agent"""
|
|
144
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
160
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
145
161
|
return server.add_tool_to_agent(agent_id=agent_id, tool_id=tool_id, user_id=actor.id)
|
|
146
162
|
|
|
147
163
|
|
|
@@ -153,7 +169,7 @@ def remove_tool_from_agent(
|
|
|
153
169
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
154
170
|
):
|
|
155
171
|
"""Add tools to an existing agent"""
|
|
156
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
172
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
157
173
|
return server.remove_tool_from_agent(agent_id=agent_id, tool_id=tool_id, user_id=actor.id)
|
|
158
174
|
|
|
159
175
|
|
|
@@ -166,13 +182,12 @@ def get_agent_state(
|
|
|
166
182
|
"""
|
|
167
183
|
Get the state of the agent.
|
|
168
184
|
"""
|
|
169
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
185
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
170
186
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return server.get_agent_state(user_id=actor.id, agent_id=agent_id)
|
|
187
|
+
try:
|
|
188
|
+
return server.agent_manager.get_agent_by_id(agent_id=agent_id, actor=actor)
|
|
189
|
+
except NoResultFound as e:
|
|
190
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
176
191
|
|
|
177
192
|
|
|
178
193
|
@router.delete("/{agent_id}", response_model=AgentState, operation_id="delete_agent")
|
|
@@ -184,38 +199,37 @@ def delete_agent(
|
|
|
184
199
|
"""
|
|
185
200
|
Delete an agent.
|
|
186
201
|
"""
|
|
187
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
raise HTTPException(status_code=404, detail=f"Agent agent_id={agent_id} not found.")
|
|
192
|
-
|
|
193
|
-
server.delete_agent(user_id=actor.id, agent_id=agent_id)
|
|
194
|
-
return agent
|
|
202
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
203
|
+
try:
|
|
204
|
+
return server.agent_manager.delete_agent(agent_id=agent_id, actor=actor)
|
|
205
|
+
except NoResultFound:
|
|
206
|
+
raise HTTPException(status_code=404, detail=f"Agent agent_id={agent_id} not found for user_id={actor.id}.")
|
|
195
207
|
|
|
196
208
|
|
|
197
209
|
@router.get("/{agent_id}/sources", response_model=List[Source], operation_id="get_agent_sources")
|
|
198
210
|
def get_agent_sources(
|
|
199
211
|
agent_id: str,
|
|
200
212
|
server: "SyncServer" = Depends(get_letta_server),
|
|
213
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
201
214
|
):
|
|
202
215
|
"""
|
|
203
216
|
Get the sources associated with an agent.
|
|
204
217
|
"""
|
|
205
|
-
|
|
206
|
-
return server.list_attached_sources(agent_id)
|
|
218
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
219
|
+
return server.agent_manager.list_attached_sources(agent_id=agent_id, actor=actor)
|
|
207
220
|
|
|
208
221
|
|
|
209
222
|
@router.get("/{agent_id}/memory/messages", response_model=List[Message], operation_id="list_agent_in_context_messages")
|
|
210
223
|
def get_agent_in_context_messages(
|
|
211
224
|
agent_id: str,
|
|
212
225
|
server: "SyncServer" = Depends(get_letta_server),
|
|
226
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
213
227
|
):
|
|
214
228
|
"""
|
|
215
229
|
Retrieve the messages in the context of a specific agent.
|
|
216
230
|
"""
|
|
217
|
-
|
|
218
|
-
return server.get_in_context_messages(agent_id=agent_id)
|
|
231
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
232
|
+
return server.get_in_context_messages(agent_id=agent_id, actor=actor)
|
|
219
233
|
|
|
220
234
|
|
|
221
235
|
# TODO: remove? can also get with agent blocks
|
|
@@ -223,13 +237,15 @@ def get_agent_in_context_messages(
|
|
|
223
237
|
def get_agent_memory(
|
|
224
238
|
agent_id: str,
|
|
225
239
|
server: "SyncServer" = Depends(get_letta_server),
|
|
240
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
226
241
|
):
|
|
227
242
|
"""
|
|
228
243
|
Retrieve the memory state of a specific agent.
|
|
229
244
|
This endpoint fetches the current memory state of the agent identified by the user ID and agent ID.
|
|
230
245
|
"""
|
|
246
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
231
247
|
|
|
232
|
-
return server.get_agent_memory(agent_id=agent_id)
|
|
248
|
+
return server.get_agent_memory(agent_id=agent_id, actor=actor)
|
|
233
249
|
|
|
234
250
|
|
|
235
251
|
@router.get("/{agent_id}/memory/block/{block_label}", response_model=Block, operation_id="get_agent_memory_block")
|
|
@@ -242,10 +258,12 @@ def get_agent_memory_block(
|
|
|
242
258
|
"""
|
|
243
259
|
Retrieve a memory block from an agent.
|
|
244
260
|
"""
|
|
245
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
261
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
246
262
|
|
|
247
|
-
|
|
248
|
-
|
|
263
|
+
try:
|
|
264
|
+
return server.agent_manager.get_block_with_label(agent_id=agent_id, block_label=block_label, actor=actor)
|
|
265
|
+
except NoResultFound as e:
|
|
266
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
249
267
|
|
|
250
268
|
|
|
251
269
|
@router.get("/{agent_id}/memory/block", response_model=List[Block], operation_id="get_agent_memory_blocks")
|
|
@@ -257,9 +275,12 @@ def get_agent_memory_blocks(
|
|
|
257
275
|
"""
|
|
258
276
|
Retrieve the memory blocks of a specific agent.
|
|
259
277
|
"""
|
|
260
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
261
|
-
|
|
262
|
-
|
|
278
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
279
|
+
try:
|
|
280
|
+
agent = server.agent_manager.get_agent_by_id(agent_id, actor=actor)
|
|
281
|
+
return agent.memory.blocks
|
|
282
|
+
except NoResultFound as e:
|
|
283
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
263
284
|
|
|
264
285
|
|
|
265
286
|
@router.post("/{agent_id}/memory/block", response_model=Memory, operation_id="add_agent_memory_block")
|
|
@@ -272,16 +293,17 @@ def add_agent_memory_block(
|
|
|
272
293
|
"""
|
|
273
294
|
Creates a memory block and links it to the agent.
|
|
274
295
|
"""
|
|
275
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
296
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
276
297
|
|
|
277
298
|
# Copied from POST /blocks
|
|
299
|
+
# TODO: Should have block_manager accept only CreateBlock
|
|
300
|
+
# TODO: This will be possible once we move ID creation to the ORM
|
|
278
301
|
block_req = Block(**create_block.model_dump())
|
|
279
302
|
block = server.block_manager.create_or_update_block(actor=actor, block=block_req)
|
|
280
303
|
|
|
281
304
|
# Link the block to the agent
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return updated_memory
|
|
305
|
+
agent = server.agent_manager.attach_block(agent_id=agent_id, block_id=block.id, actor=actor)
|
|
306
|
+
return agent.memory
|
|
285
307
|
|
|
286
308
|
|
|
287
309
|
@router.delete("/{agent_id}/memory/block/{block_label}", response_model=Memory, operation_id="remove_agent_memory_block_by_label")
|
|
@@ -296,56 +318,56 @@ def remove_agent_memory_block(
|
|
|
296
318
|
"""
|
|
297
319
|
Removes a memory block from an agent by unlnking it. If the block is not linked to any other agent, it is deleted.
|
|
298
320
|
"""
|
|
299
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
321
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
300
322
|
|
|
301
323
|
# Unlink the block from the agent
|
|
302
|
-
|
|
324
|
+
agent = server.agent_manager.detach_block_with_label(agent_id=agent_id, block_label=block_label, actor=actor)
|
|
303
325
|
|
|
304
|
-
return
|
|
326
|
+
return agent.memory
|
|
305
327
|
|
|
306
328
|
|
|
307
329
|
@router.patch("/{agent_id}/memory/block/{block_label}", response_model=Block, operation_id="update_agent_memory_block_by_label")
|
|
308
330
|
def update_agent_memory_block(
|
|
309
331
|
agent_id: str,
|
|
310
332
|
block_label: str,
|
|
311
|
-
|
|
333
|
+
block_update: BlockUpdate = Body(...),
|
|
312
334
|
server: "SyncServer" = Depends(get_letta_server),
|
|
313
335
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
314
336
|
):
|
|
315
337
|
"""
|
|
316
338
|
Removes a memory block from an agent by unlnking it. If the block is not linked to any other agent, it is deleted.
|
|
317
339
|
"""
|
|
318
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
319
|
-
|
|
320
|
-
# get the block_id from the label
|
|
321
|
-
block_id = server.blocks_agents_manager.get_block_id_for_label(agent_id=agent_id, block_label=block_label)
|
|
340
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
322
341
|
|
|
323
|
-
|
|
324
|
-
return server.block_manager.update_block(
|
|
342
|
+
block = server.agent_manager.get_block_with_label(agent_id=agent_id, block_label=block_label, actor=actor)
|
|
343
|
+
return server.block_manager.update_block(block.id, block_update=block_update, actor=actor)
|
|
325
344
|
|
|
326
345
|
|
|
327
346
|
@router.get("/{agent_id}/memory/recall", response_model=RecallMemorySummary, operation_id="get_agent_recall_memory_summary")
|
|
328
347
|
def get_agent_recall_memory_summary(
|
|
329
348
|
agent_id: str,
|
|
330
349
|
server: "SyncServer" = Depends(get_letta_server),
|
|
350
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
331
351
|
):
|
|
332
352
|
"""
|
|
333
353
|
Retrieve the summary of the recall memory of a specific agent.
|
|
334
354
|
"""
|
|
355
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
335
356
|
|
|
336
|
-
return server.get_recall_memory_summary(agent_id=agent_id)
|
|
357
|
+
return server.get_recall_memory_summary(agent_id=agent_id, actor=actor)
|
|
337
358
|
|
|
338
359
|
|
|
339
360
|
@router.get("/{agent_id}/memory/archival", response_model=ArchivalMemorySummary, operation_id="get_agent_archival_memory_summary")
|
|
340
361
|
def get_agent_archival_memory_summary(
|
|
341
362
|
agent_id: str,
|
|
342
363
|
server: "SyncServer" = Depends(get_letta_server),
|
|
364
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
343
365
|
):
|
|
344
366
|
"""
|
|
345
367
|
Retrieve the summary of the archival memory of a specific agent.
|
|
346
368
|
"""
|
|
347
|
-
|
|
348
|
-
return server.get_archival_memory_summary(agent_id=agent_id)
|
|
369
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
370
|
+
return server.get_archival_memory_summary(agent_id=agent_id, actor=actor)
|
|
349
371
|
|
|
350
372
|
|
|
351
373
|
@router.get("/{agent_id}/archival", response_model=List[Passage], operation_id="list_agent_archival_memory")
|
|
@@ -360,7 +382,7 @@ def get_agent_archival_memory(
|
|
|
360
382
|
"""
|
|
361
383
|
Retrieve the memories in an agent's archival memory store (paginated query).
|
|
362
384
|
"""
|
|
363
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
385
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
364
386
|
|
|
365
387
|
# TODO need to add support for non-postgres here
|
|
366
388
|
# chroma will throw:
|
|
@@ -369,7 +391,7 @@ def get_agent_archival_memory(
|
|
|
369
391
|
return server.get_agent_archival_cursor(
|
|
370
392
|
user_id=actor.id,
|
|
371
393
|
agent_id=agent_id,
|
|
372
|
-
cursor=after,
|
|
394
|
+
cursor=after, # TODO: deleting before, after. is this expected?
|
|
373
395
|
limit=limit,
|
|
374
396
|
)
|
|
375
397
|
|
|
@@ -384,9 +406,9 @@ def insert_agent_archival_memory(
|
|
|
384
406
|
"""
|
|
385
407
|
Insert a memory into an agent's archival memory store.
|
|
386
408
|
"""
|
|
387
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
409
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
388
410
|
|
|
389
|
-
return server.insert_archival_memory(
|
|
411
|
+
return server.insert_archival_memory(agent_id=agent_id, memory_contents=request.text, actor=actor)
|
|
390
412
|
|
|
391
413
|
|
|
392
414
|
# TODO(ethan): query or path parameter for memory_id?
|
|
@@ -402,9 +424,9 @@ def delete_agent_archival_memory(
|
|
|
402
424
|
"""
|
|
403
425
|
Delete a memory from an agent's archival memory store.
|
|
404
426
|
"""
|
|
405
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
427
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
406
428
|
|
|
407
|
-
server.delete_archival_memory(
|
|
429
|
+
server.delete_archival_memory(agent_id=agent_id, memory_id=memory_id, actor=actor)
|
|
408
430
|
return JSONResponse(status_code=status.HTTP_200_OK, content={"message": f"Memory id={memory_id} successfully deleted"})
|
|
409
431
|
|
|
410
432
|
|
|
@@ -429,7 +451,7 @@ def get_agent_messages(
|
|
|
429
451
|
"""
|
|
430
452
|
Retrieve message history for an agent.
|
|
431
453
|
"""
|
|
432
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
454
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
433
455
|
|
|
434
456
|
return server.get_agent_recall_cursor(
|
|
435
457
|
user_id=actor.id,
|
|
@@ -449,11 +471,13 @@ def update_message(
|
|
|
449
471
|
message_id: str,
|
|
450
472
|
request: MessageUpdate = Body(...),
|
|
451
473
|
server: "SyncServer" = Depends(get_letta_server),
|
|
474
|
+
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
452
475
|
):
|
|
453
476
|
"""
|
|
454
477
|
Update the details of a message associated with an agent.
|
|
455
478
|
"""
|
|
456
|
-
|
|
479
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
480
|
+
return server.update_agent_message(agent_id=agent_id, message_id=message_id, request=request, actor=actor)
|
|
457
481
|
|
|
458
482
|
|
|
459
483
|
@router.post(
|
|
@@ -471,11 +495,11 @@ async def send_message(
|
|
|
471
495
|
Process a user message and return the agent's response.
|
|
472
496
|
This endpoint accepts a message from a user and processes it through the agent.
|
|
473
497
|
"""
|
|
474
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
498
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
475
499
|
result = await send_message_to_agent(
|
|
476
500
|
server=server,
|
|
477
501
|
agent_id=agent_id,
|
|
478
|
-
|
|
502
|
+
actor=actor,
|
|
479
503
|
messages=request.messages,
|
|
480
504
|
stream_steps=False,
|
|
481
505
|
stream_tokens=False,
|
|
@@ -511,11 +535,11 @@ async def send_message_streaming(
|
|
|
511
535
|
It will stream the steps of the response always, and stream the tokens if 'stream_tokens' is set to True.
|
|
512
536
|
"""
|
|
513
537
|
|
|
514
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
538
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
515
539
|
result = await send_message_to_agent(
|
|
516
540
|
server=server,
|
|
517
541
|
agent_id=agent_id,
|
|
518
|
-
|
|
542
|
+
actor=actor,
|
|
519
543
|
messages=request.messages,
|
|
520
544
|
stream_steps=True,
|
|
521
545
|
stream_tokens=request.stream_tokens,
|
|
@@ -531,7 +555,6 @@ async def process_message_background(
|
|
|
531
555
|
server: SyncServer,
|
|
532
556
|
actor: User,
|
|
533
557
|
agent_id: str,
|
|
534
|
-
user_id: str,
|
|
535
558
|
messages: list,
|
|
536
559
|
assistant_message_tool_name: str,
|
|
537
560
|
assistant_message_tool_kwarg: str,
|
|
@@ -542,7 +565,7 @@ async def process_message_background(
|
|
|
542
565
|
result = await send_message_to_agent(
|
|
543
566
|
server=server,
|
|
544
567
|
agent_id=agent_id,
|
|
545
|
-
|
|
568
|
+
actor=actor,
|
|
546
569
|
messages=messages,
|
|
547
570
|
stream_steps=False, # NOTE(matt)
|
|
548
571
|
stream_tokens=False,
|
|
@@ -585,7 +608,7 @@ async def send_message_async(
|
|
|
585
608
|
Asynchronously process a user message and return a job ID.
|
|
586
609
|
The actual processing happens in the background, and the status can be checked using the job ID.
|
|
587
610
|
"""
|
|
588
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
611
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
589
612
|
|
|
590
613
|
# Create a new job
|
|
591
614
|
job = Job(
|
|
@@ -605,7 +628,6 @@ async def send_message_async(
|
|
|
605
628
|
server=server,
|
|
606
629
|
actor=actor,
|
|
607
630
|
agent_id=agent_id,
|
|
608
|
-
user_id=actor.id,
|
|
609
631
|
messages=request.messages,
|
|
610
632
|
assistant_message_tool_name=request.assistant_message_tool_name,
|
|
611
633
|
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
|
@@ -618,7 +640,7 @@ async def send_message_async(
|
|
|
618
640
|
async def send_message_to_agent(
|
|
619
641
|
server: SyncServer,
|
|
620
642
|
agent_id: str,
|
|
621
|
-
|
|
643
|
+
actor: User,
|
|
622
644
|
# role: MessageRole,
|
|
623
645
|
messages: Union[List[Message], List[MessageCreate]],
|
|
624
646
|
stream_steps: bool,
|
|
@@ -645,8 +667,7 @@ async def send_message_to_agent(
|
|
|
645
667
|
|
|
646
668
|
# Get the generator object off of the agent's streaming interface
|
|
647
669
|
# This will be attached to the POST SSE request used under-the-hood
|
|
648
|
-
|
|
649
|
-
letta_agent = server.load_agent(agent_id=agent_id)
|
|
670
|
+
letta_agent = server.load_agent(agent_id=agent_id, actor=actor)
|
|
650
671
|
|
|
651
672
|
# Disable token streaming if not OpenAI
|
|
652
673
|
# TODO: cleanup this logic
|
|
@@ -685,7 +706,7 @@ async def send_message_to_agent(
|
|
|
685
706
|
task = asyncio.create_task(
|
|
686
707
|
asyncio.to_thread(
|
|
687
708
|
server.send_messages,
|
|
688
|
-
|
|
709
|
+
actor=actor,
|
|
689
710
|
agent_id=agent_id,
|
|
690
711
|
messages=messages,
|
|
691
712
|
interface=streaming_interface,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, List, Optional
|
|
2
2
|
|
|
3
|
-
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query
|
|
3
|
+
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response
|
|
4
4
|
|
|
5
5
|
from letta.orm.errors import NoResultFound
|
|
6
6
|
from letta.schemas.block import Block, BlockUpdate, CreateBlock
|
|
7
|
-
from letta.schemas.memory import Memory
|
|
8
7
|
from letta.server.rest_api.utils import get_letta_server
|
|
9
8
|
from letta.server.server import SyncServer
|
|
10
9
|
|
|
@@ -23,7 +22,7 @@ def list_blocks(
|
|
|
23
22
|
server: SyncServer = Depends(get_letta_server),
|
|
24
23
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
25
24
|
):
|
|
26
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
25
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
27
26
|
return server.block_manager.get_blocks(actor=actor, label=label, is_template=templates_only, template_name=name)
|
|
28
27
|
|
|
29
28
|
|
|
@@ -33,7 +32,7 @@ def create_block(
|
|
|
33
32
|
server: SyncServer = Depends(get_letta_server),
|
|
34
33
|
user_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
|
35
34
|
):
|
|
36
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
35
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
37
36
|
block = Block(**create_block.model_dump())
|
|
38
37
|
return server.block_manager.create_or_update_block(actor=actor, block=block)
|
|
39
38
|
|
|
@@ -41,12 +40,12 @@ def create_block(
|
|
|
41
40
|
@router.patch("/{block_id}", response_model=Block, operation_id="update_memory_block")
|
|
42
41
|
def update_block(
|
|
43
42
|
block_id: str,
|
|
44
|
-
|
|
43
|
+
block_update: BlockUpdate = Body(...),
|
|
45
44
|
server: SyncServer = Depends(get_letta_server),
|
|
46
45
|
user_id: Optional[str] = Header(None, alias="user_id"),
|
|
47
46
|
):
|
|
48
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
49
|
-
return server.block_manager.update_block(block_id=block_id, block_update=
|
|
47
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
48
|
+
return server.block_manager.update_block(block_id=block_id, block_update=block_update, actor=actor)
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
@router.delete("/{block_id}", response_model=Block, operation_id="delete_memory_block")
|
|
@@ -55,7 +54,7 @@ def delete_block(
|
|
|
55
54
|
server: SyncServer = Depends(get_letta_server),
|
|
56
55
|
user_id: Optional[str] = Header(None, alias="user_id"),
|
|
57
56
|
):
|
|
58
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
57
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
59
58
|
return server.block_manager.delete_block(block_id=block_id, actor=actor)
|
|
60
59
|
|
|
61
60
|
|
|
@@ -66,7 +65,7 @@ def get_block(
|
|
|
66
65
|
user_id: Optional[str] = Header(None, alias="user_id"),
|
|
67
66
|
):
|
|
68
67
|
print("call get block", block_id)
|
|
69
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
68
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
70
69
|
try:
|
|
71
70
|
block = server.block_manager.get_block_by_id(block_id=block_id, actor=actor)
|
|
72
71
|
if block is None:
|
|
@@ -76,7 +75,7 @@ def get_block(
|
|
|
76
75
|
raise HTTPException(status_code=404, detail="Block not found")
|
|
77
76
|
|
|
78
77
|
|
|
79
|
-
@router.patch("/{block_id}/attach", response_model=
|
|
78
|
+
@router.patch("/{block_id}/attach", response_model=None, status_code=204, operation_id="link_agent_memory_block")
|
|
80
79
|
def link_agent_memory_block(
|
|
81
80
|
block_id: str,
|
|
82
81
|
agent_id: str = Query(..., description="The unique identifier of the agent to attach the source to."),
|
|
@@ -86,17 +85,16 @@ def link_agent_memory_block(
|
|
|
86
85
|
"""
|
|
87
86
|
Link a memory block to an agent.
|
|
88
87
|
"""
|
|
89
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
88
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return block
|
|
90
|
+
try:
|
|
91
|
+
server.agent_manager.attach_block(agent_id=agent_id, block_id=block_id, actor=actor)
|
|
92
|
+
return Response(status_code=204)
|
|
93
|
+
except NoResultFound as e:
|
|
94
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
97
95
|
|
|
98
96
|
|
|
99
|
-
@router.patch("/{block_id}/detach", response_model=
|
|
97
|
+
@router.patch("/{block_id}/detach", response_model=None, status_code=204, operation_id="unlink_agent_memory_block")
|
|
100
98
|
def unlink_agent_memory_block(
|
|
101
99
|
block_id: str,
|
|
102
100
|
agent_id: str = Query(..., description="The unique identifier of the agent to attach the source to."),
|
|
@@ -106,11 +104,10 @@ def unlink_agent_memory_block(
|
|
|
106
104
|
"""
|
|
107
105
|
Unlink a memory block from an agent
|
|
108
106
|
"""
|
|
109
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
107
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return block
|
|
109
|
+
try:
|
|
110
|
+
server.agent_manager.detach_block(agent_id=agent_id, block_id=block_id, actor=actor)
|
|
111
|
+
return Response(status_code=204)
|
|
112
|
+
except NoResultFound as e:
|
|
113
|
+
raise HTTPException(status_code=404, detail=str(e))
|
|
@@ -20,7 +20,7 @@ def list_jobs(
|
|
|
20
20
|
"""
|
|
21
21
|
List all jobs.
|
|
22
22
|
"""
|
|
23
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
23
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
24
24
|
|
|
25
25
|
# TODO: add filtering by status
|
|
26
26
|
jobs = server.job_manager.list_jobs(actor=actor)
|
|
@@ -40,7 +40,7 @@ def list_active_jobs(
|
|
|
40
40
|
"""
|
|
41
41
|
List all active jobs.
|
|
42
42
|
"""
|
|
43
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
43
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
44
44
|
|
|
45
45
|
return server.job_manager.list_jobs(actor=actor, statuses=[JobStatus.created, JobStatus.running])
|
|
46
46
|
|
|
@@ -54,7 +54,7 @@ def get_job(
|
|
|
54
54
|
"""
|
|
55
55
|
Get the status of a job.
|
|
56
56
|
"""
|
|
57
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
57
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
58
58
|
|
|
59
59
|
try:
|
|
60
60
|
return server.job_manager.get_job_by_id(job_id=job_id, actor=actor)
|
|
@@ -71,7 +71,7 @@ def delete_job(
|
|
|
71
71
|
"""
|
|
72
72
|
Delete a job by its job_id.
|
|
73
73
|
"""
|
|
74
|
-
actor = server.get_user_or_default(user_id=user_id)
|
|
74
|
+
actor = server.user_manager.get_user_or_default(user_id=user_id)
|
|
75
75
|
|
|
76
76
|
try:
|
|
77
77
|
job = server.job_manager.delete_job_by_id(job_id=job_id, actor=actor)
|