letta-nightly 0.8.8.dev20250703104323__py3-none-any.whl → 0.8.8.dev20250703174903__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/agent.py +1 -0
- letta/agents/base_agent.py +8 -2
- letta/agents/ephemeral_summary_agent.py +33 -33
- letta/agents/letta_agent.py +104 -53
- letta/agents/voice_agent.py +2 -1
- letta/constants.py +8 -4
- letta/functions/function_sets/files.py +22 -7
- letta/functions/function_sets/multi_agent.py +34 -0
- letta/functions/types.py +1 -1
- letta/groups/helpers.py +8 -5
- letta/groups/sleeptime_multi_agent_v2.py +20 -15
- letta/interface.py +1 -1
- letta/interfaces/anthropic_streaming_interface.py +15 -8
- letta/interfaces/openai_chat_completions_streaming_interface.py +9 -6
- letta/interfaces/openai_streaming_interface.py +17 -11
- letta/llm_api/openai_client.py +2 -1
- letta/orm/agent.py +1 -0
- letta/orm/file.py +8 -2
- letta/orm/files_agents.py +36 -11
- letta/orm/mcp_server.py +3 -0
- letta/orm/source.py +2 -1
- letta/orm/step.py +3 -0
- letta/prompts/system/memgpt_v2_chat.txt +5 -8
- letta/schemas/agent.py +58 -23
- letta/schemas/embedding_config.py +3 -2
- letta/schemas/enums.py +4 -0
- letta/schemas/file.py +1 -0
- letta/schemas/letta_stop_reason.py +18 -0
- letta/schemas/mcp.py +15 -10
- letta/schemas/memory.py +35 -5
- letta/schemas/providers.py +11 -0
- letta/schemas/step.py +1 -0
- letta/schemas/tool.py +2 -1
- letta/server/rest_api/routers/v1/agents.py +320 -184
- letta/server/rest_api/routers/v1/groups.py +6 -2
- letta/server/rest_api/routers/v1/identities.py +6 -2
- letta/server/rest_api/routers/v1/jobs.py +49 -1
- letta/server/rest_api/routers/v1/sources.py +28 -19
- letta/server/rest_api/routers/v1/steps.py +7 -2
- letta/server/rest_api/routers/v1/tools.py +40 -9
- letta/server/rest_api/streaming_response.py +88 -0
- letta/server/server.py +61 -55
- letta/services/agent_manager.py +28 -16
- letta/services/file_manager.py +58 -9
- letta/services/file_processor/chunker/llama_index_chunker.py +2 -0
- letta/services/file_processor/embedder/openai_embedder.py +54 -10
- letta/services/file_processor/file_processor.py +59 -0
- letta/services/file_processor/parser/mistral_parser.py +2 -0
- letta/services/files_agents_manager.py +120 -2
- letta/services/helpers/agent_manager_helper.py +21 -4
- letta/services/job_manager.py +57 -6
- letta/services/mcp/base_client.py +1 -0
- letta/services/mcp_manager.py +13 -1
- letta/services/step_manager.py +14 -5
- letta/services/summarizer/summarizer.py +6 -22
- letta/services/tool_executor/builtin_tool_executor.py +0 -1
- letta/services/tool_executor/files_tool_executor.py +2 -2
- letta/services/tool_executor/multi_agent_tool_executor.py +23 -0
- letta/services/tool_manager.py +7 -7
- letta/settings.py +11 -2
- letta/templates/summary_request_text.j2 +19 -0
- letta/utils.py +95 -14
- {letta_nightly-0.8.8.dev20250703104323.dist-info → letta_nightly-0.8.8.dev20250703174903.dist-info}/METADATA +2 -2
- {letta_nightly-0.8.8.dev20250703104323.dist-info → letta_nightly-0.8.8.dev20250703174903.dist-info}/RECORD +68 -67
- /letta/{agents/prompts → prompts/system}/summary_system_prompt.txt +0 -0
- {letta_nightly-0.8.8.dev20250703104323.dist-info → letta_nightly-0.8.8.dev20250703174903.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.8.dev20250703104323.dist-info → letta_nightly-0.8.8.dev20250703174903.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.8.dev20250703104323.dist-info → letta_nightly-0.8.8.dev20250703174903.dist-info}/entry_points.txt +0 -0
@@ -13,7 +13,8 @@ from sqlalchemy.exc import IntegrityError, OperationalError
|
|
13
13
|
from starlette.responses import Response, StreamingResponse
|
14
14
|
|
15
15
|
from letta.agents.letta_agent import LettaAgent
|
16
|
-
from letta.constants import DEFAULT_MAX_STEPS, DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, LETTA_MODEL_ENDPOINT
|
16
|
+
from letta.constants import DEFAULT_MAX_STEPS, DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, LETTA_MODEL_ENDPOINT, REDIS_RUN_ID_PREFIX
|
17
|
+
from letta.data_sources.redis_client import get_redis_client
|
17
18
|
from letta.groups.sleeptime_multi_agent_v2 import SleeptimeMultiAgentV2
|
18
19
|
from letta.helpers.datetime_helpers import get_utc_timestamp_ns
|
19
20
|
from letta.log import get_logger
|
@@ -49,26 +50,26 @@ router = APIRouter(prefix="/agents", tags=["agents"])
|
|
49
50
|
logger = get_logger(__name__)
|
50
51
|
|
51
52
|
|
52
|
-
@router.get("/", response_model=
|
53
|
+
@router.get("/", response_model=list[AgentState], operation_id="list_agents")
|
53
54
|
async def list_agents(
|
54
|
-
name:
|
55
|
-
tags:
|
55
|
+
name: str | None = Query(None, description="Name of the agent"),
|
56
|
+
tags: list[str] | None = Query(None, description="List of tags to filter agents by"),
|
56
57
|
match_all_tags: bool = Query(
|
57
58
|
False,
|
58
59
|
description="If True, only returns agents that match ALL given tags. Otherwise, return agents that have ANY of the passed-in tags.",
|
59
60
|
),
|
60
61
|
server: SyncServer = Depends(get_letta_server),
|
61
|
-
actor_id:
|
62
|
-
before:
|
63
|
-
after:
|
64
|
-
limit:
|
65
|
-
query_text:
|
66
|
-
project_id:
|
67
|
-
template_id:
|
68
|
-
base_template_id:
|
69
|
-
identity_id:
|
70
|
-
identifier_keys:
|
71
|
-
include_relationships:
|
62
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
63
|
+
before: str | None = Query(None, description="Cursor for pagination"),
|
64
|
+
after: str | None = Query(None, description="Cursor for pagination"),
|
65
|
+
limit: int | None = Query(50, description="Limit for pagination"),
|
66
|
+
query_text: str | None = Query(None, description="Search agents by name"),
|
67
|
+
project_id: str | None = Query(None, description="Search agents by project ID"),
|
68
|
+
template_id: str | None = Query(None, description="Search agents by template ID"),
|
69
|
+
base_template_id: str | None = Query(None, description="Search agents by base template ID"),
|
70
|
+
identity_id: str | None = Query(None, description="Search agents by identity ID"),
|
71
|
+
identifier_keys: list[str] | None = Query(None, description="Search agents by identifier keys"),
|
72
|
+
include_relationships: list[str] | None = Query(
|
72
73
|
None,
|
73
74
|
description=(
|
74
75
|
"Specify which relational fields (e.g., 'tools', 'sources', 'memory') to include in the response. "
|
@@ -80,7 +81,7 @@ async def list_agents(
|
|
80
81
|
False,
|
81
82
|
description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default)",
|
82
83
|
),
|
83
|
-
sort_by:
|
84
|
+
sort_by: str | None = Query(
|
84
85
|
"created_at",
|
85
86
|
description="Field to sort by. Options: 'created_at' (default), 'last_run_completion'",
|
86
87
|
),
|
@@ -119,7 +120,7 @@ async def list_agents(
|
|
119
120
|
@router.get("/count", response_model=int, operation_id="count_agents")
|
120
121
|
async def count_agents(
|
121
122
|
server: SyncServer = Depends(get_letta_server),
|
122
|
-
actor_id:
|
123
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
123
124
|
):
|
124
125
|
"""
|
125
126
|
Get the count of all agents associated with a given user.
|
@@ -139,10 +140,10 @@ class IndentedORJSONResponse(Response):
|
|
139
140
|
def export_agent_serialized(
|
140
141
|
agent_id: str,
|
141
142
|
server: "SyncServer" = Depends(get_letta_server),
|
142
|
-
actor_id:
|
143
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
143
144
|
# do not remove, used to autogeneration of spec
|
144
145
|
# TODO: Think of a better way to export AgentSchema
|
145
|
-
spec:
|
146
|
+
spec: AgentSchema | None = None,
|
146
147
|
) -> JSONResponse:
|
147
148
|
"""
|
148
149
|
Export the serialized JSON representation of an agent, formatted with indentation.
|
@@ -160,13 +161,13 @@ def export_agent_serialized(
|
|
160
161
|
def import_agent_serialized(
|
161
162
|
file: UploadFile = File(...),
|
162
163
|
server: "SyncServer" = Depends(get_letta_server),
|
163
|
-
actor_id:
|
164
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
164
165
|
append_copy_suffix: bool = Query(True, description='If set to True, appends "_copy" to the end of the agent name.'),
|
165
166
|
override_existing_tools: bool = Query(
|
166
167
|
True,
|
167
168
|
description="If set to True, existing tools can get their source code overwritten by the uploaded tool definitions. Note that Letta core tools can never be updated externally.",
|
168
169
|
),
|
169
|
-
project_id:
|
170
|
+
project_id: str | None = Query(None, description="The project ID to associate the uploaded agent with."),
|
170
171
|
strip_messages: bool = Query(
|
171
172
|
False,
|
172
173
|
description="If set to True, strips all messages from the agent before importing.",
|
@@ -198,24 +199,24 @@ def import_agent_serialized(
|
|
198
199
|
raise HTTPException(status_code=400, detail="Corrupted agent file format.")
|
199
200
|
|
200
201
|
except ValidationError as e:
|
201
|
-
raise HTTPException(status_code=422, detail=f"Invalid agent schema: {
|
202
|
+
raise HTTPException(status_code=422, detail=f"Invalid agent schema: {e!s}")
|
202
203
|
|
203
204
|
except IntegrityError as e:
|
204
|
-
raise HTTPException(status_code=409, detail=f"Database integrity error: {
|
205
|
+
raise HTTPException(status_code=409, detail=f"Database integrity error: {e!s}")
|
205
206
|
|
206
207
|
except OperationalError as e:
|
207
|
-
raise HTTPException(status_code=503, detail=f"Database connection error. Please try again later: {
|
208
|
+
raise HTTPException(status_code=503, detail=f"Database connection error. Please try again later: {e!s}")
|
208
209
|
|
209
210
|
except Exception as e:
|
210
211
|
traceback.print_exc()
|
211
|
-
raise HTTPException(status_code=500, detail=f"An unexpected error occurred while uploading the agent: {
|
212
|
+
raise HTTPException(status_code=500, detail=f"An unexpected error occurred while uploading the agent: {e!s}")
|
212
213
|
|
213
214
|
|
214
215
|
@router.get("/{agent_id}/context", response_model=ContextWindowOverview, operation_id="retrieve_agent_context_window")
|
215
216
|
async def retrieve_agent_context_window(
|
216
217
|
agent_id: str,
|
217
218
|
server: "SyncServer" = Depends(get_letta_server),
|
218
|
-
actor_id:
|
219
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
219
220
|
):
|
220
221
|
"""
|
221
222
|
Retrieve the context window of a specific agent.
|
@@ -234,15 +235,17 @@ class CreateAgentRequest(CreateAgent):
|
|
234
235
|
"""
|
235
236
|
|
236
237
|
# Override the user_id field to exclude it from the request body validation
|
237
|
-
actor_id:
|
238
|
+
actor_id: str | None = Field(None, exclude=True)
|
238
239
|
|
239
240
|
|
240
241
|
@router.post("/", response_model=AgentState, operation_id="create_agent")
|
241
242
|
async def create_agent(
|
242
243
|
agent: CreateAgentRequest = Body(...),
|
243
244
|
server: "SyncServer" = Depends(get_letta_server),
|
244
|
-
actor_id:
|
245
|
-
x_project:
|
245
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
246
|
+
x_project: str | None = Header(
|
247
|
+
None, alias="X-Project", description="The project slug to associate with the agent (cloud only)."
|
248
|
+
), # Only handled by next js middleware
|
246
249
|
):
|
247
250
|
"""
|
248
251
|
Create a new agent with the specified configuration.
|
@@ -260,18 +263,18 @@ async def modify_agent(
|
|
260
263
|
agent_id: str,
|
261
264
|
update_agent: UpdateAgent = Body(...),
|
262
265
|
server: "SyncServer" = Depends(get_letta_server),
|
263
|
-
actor_id:
|
266
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
264
267
|
):
|
265
268
|
"""Update an existing agent"""
|
266
269
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
267
270
|
return await server.update_agent_async(agent_id=agent_id, request=update_agent, actor=actor)
|
268
271
|
|
269
272
|
|
270
|
-
@router.get("/{agent_id}/tools", response_model=
|
273
|
+
@router.get("/{agent_id}/tools", response_model=list[Tool], operation_id="list_agent_tools")
|
271
274
|
def list_agent_tools(
|
272
275
|
agent_id: str,
|
273
276
|
server: "SyncServer" = Depends(get_letta_server),
|
274
|
-
actor_id:
|
277
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
275
278
|
):
|
276
279
|
"""Get tools from an existing agent"""
|
277
280
|
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
@@ -283,7 +286,7 @@ async def attach_tool(
|
|
283
286
|
agent_id: str,
|
284
287
|
tool_id: str,
|
285
288
|
server: "SyncServer" = Depends(get_letta_server),
|
286
|
-
actor_id:
|
289
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
287
290
|
):
|
288
291
|
"""
|
289
292
|
Attach a tool to an agent.
|
@@ -297,7 +300,7 @@ async def detach_tool(
|
|
297
300
|
agent_id: str,
|
298
301
|
tool_id: str,
|
299
302
|
server: "SyncServer" = Depends(get_letta_server),
|
300
|
-
actor_id:
|
303
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
301
304
|
):
|
302
305
|
"""
|
303
306
|
Detach a tool from an agent.
|
@@ -311,7 +314,7 @@ async def attach_source(
|
|
311
314
|
agent_id: str,
|
312
315
|
source_id: str,
|
313
316
|
server: "SyncServer" = Depends(get_letta_server),
|
314
|
-
actor_id:
|
317
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
315
318
|
):
|
316
319
|
"""
|
317
320
|
Attach a source to an agent.
|
@@ -339,7 +342,7 @@ async def detach_source(
|
|
339
342
|
agent_id: str,
|
340
343
|
source_id: str,
|
341
344
|
server: "SyncServer" = Depends(get_letta_server),
|
342
|
-
actor_id:
|
345
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
343
346
|
):
|
344
347
|
"""
|
345
348
|
Detach a source from an agent.
|
@@ -364,10 +367,27 @@ async def detach_source(
|
|
364
367
|
return agent_state
|
365
368
|
|
366
369
|
|
370
|
+
@router.patch("/{agent_id}/files/close-all", response_model=List[str], operation_id="close_all_open_files")
|
371
|
+
async def close_all_open_files(
|
372
|
+
agent_id: str,
|
373
|
+
server: "SyncServer" = Depends(get_letta_server),
|
374
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
375
|
+
):
|
376
|
+
"""
|
377
|
+
Closes all currently open files for a given agent.
|
378
|
+
|
379
|
+
This endpoint updates the file state for the agent so that no files are marked as open.
|
380
|
+
Typically used to reset the working memory view for the agent.
|
381
|
+
"""
|
382
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
383
|
+
|
384
|
+
return server.file_agent_manager.close_all_other_files(agent_id=agent_id, keep_file_names=[], actor=actor)
|
385
|
+
|
386
|
+
|
367
387
|
@router.get("/{agent_id}", response_model=AgentState, operation_id="retrieve_agent")
|
368
388
|
async def retrieve_agent(
|
369
389
|
agent_id: str,
|
370
|
-
include_relationships:
|
390
|
+
include_relationships: list[str] | None = Query(
|
371
391
|
None,
|
372
392
|
description=(
|
373
393
|
"Specify which relational fields (e.g., 'tools', 'sources', 'memory') to include in the response. "
|
@@ -376,7 +396,7 @@ async def retrieve_agent(
|
|
376
396
|
),
|
377
397
|
),
|
378
398
|
server: "SyncServer" = Depends(get_letta_server),
|
379
|
-
actor_id:
|
399
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
380
400
|
):
|
381
401
|
"""
|
382
402
|
Get the state of the agent.
|
@@ -393,7 +413,7 @@ async def retrieve_agent(
|
|
393
413
|
async def delete_agent(
|
394
414
|
agent_id: str,
|
395
415
|
server: "SyncServer" = Depends(get_letta_server),
|
396
|
-
actor_id:
|
416
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
397
417
|
):
|
398
418
|
"""
|
399
419
|
Delete an agent.
|
@@ -406,11 +426,11 @@ async def delete_agent(
|
|
406
426
|
raise HTTPException(status_code=404, detail=f"Agent agent_id={agent_id} not found for user_id={actor.id}.")
|
407
427
|
|
408
428
|
|
409
|
-
@router.get("/{agent_id}/sources", response_model=
|
429
|
+
@router.get("/{agent_id}/sources", response_model=list[Source], operation_id="list_agent_sources")
|
410
430
|
async def list_agent_sources(
|
411
431
|
agent_id: str,
|
412
432
|
server: "SyncServer" = Depends(get_letta_server),
|
413
|
-
actor_id:
|
433
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
414
434
|
):
|
415
435
|
"""
|
416
436
|
Get the sources associated with an agent.
|
@@ -424,7 +444,7 @@ async def list_agent_sources(
|
|
424
444
|
async def retrieve_agent_memory(
|
425
445
|
agent_id: str,
|
426
446
|
server: "SyncServer" = Depends(get_letta_server),
|
427
|
-
actor_id:
|
447
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
428
448
|
):
|
429
449
|
"""
|
430
450
|
Retrieve the memory state of a specific agent.
|
@@ -440,7 +460,7 @@ async def retrieve_block(
|
|
440
460
|
agent_id: str,
|
441
461
|
block_label: str,
|
442
462
|
server: "SyncServer" = Depends(get_letta_server),
|
443
|
-
actor_id:
|
463
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
444
464
|
):
|
445
465
|
"""
|
446
466
|
Retrieve a core memory block from an agent.
|
@@ -453,11 +473,11 @@ async def retrieve_block(
|
|
453
473
|
raise HTTPException(status_code=404, detail=str(e))
|
454
474
|
|
455
475
|
|
456
|
-
@router.get("/{agent_id}/core-memory/blocks", response_model=
|
476
|
+
@router.get("/{agent_id}/core-memory/blocks", response_model=list[Block], operation_id="list_core_memory_blocks")
|
457
477
|
async def list_blocks(
|
458
478
|
agent_id: str,
|
459
479
|
server: "SyncServer" = Depends(get_letta_server),
|
460
|
-
actor_id:
|
480
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
461
481
|
):
|
462
482
|
"""
|
463
483
|
Retrieve the core memory blocks of a specific agent.
|
@@ -476,7 +496,7 @@ async def modify_block(
|
|
476
496
|
block_label: str,
|
477
497
|
block_update: BlockUpdate = Body(...),
|
478
498
|
server: "SyncServer" = Depends(get_letta_server),
|
479
|
-
actor_id:
|
499
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
480
500
|
):
|
481
501
|
"""
|
482
502
|
Updates a core memory block of an agent.
|
@@ -498,7 +518,7 @@ async def attach_block(
|
|
498
518
|
agent_id: str,
|
499
519
|
block_id: str,
|
500
520
|
server: "SyncServer" = Depends(get_letta_server),
|
501
|
-
actor_id:
|
521
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
502
522
|
):
|
503
523
|
"""
|
504
524
|
Attach a core memoryblock to an agent.
|
@@ -512,7 +532,7 @@ async def detach_block(
|
|
512
532
|
agent_id: str,
|
513
533
|
block_id: str,
|
514
534
|
server: "SyncServer" = Depends(get_letta_server),
|
515
|
-
actor_id:
|
535
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
516
536
|
):
|
517
537
|
"""
|
518
538
|
Detach a core memory block from an agent.
|
@@ -521,18 +541,18 @@ async def detach_block(
|
|
521
541
|
return await server.agent_manager.detach_block_async(agent_id=agent_id, block_id=block_id, actor=actor)
|
522
542
|
|
523
543
|
|
524
|
-
@router.get("/{agent_id}/archival-memory", response_model=
|
544
|
+
@router.get("/{agent_id}/archival-memory", response_model=list[Passage], operation_id="list_passages")
|
525
545
|
async def list_passages(
|
526
546
|
agent_id: str,
|
527
547
|
server: "SyncServer" = Depends(get_letta_server),
|
528
|
-
after:
|
529
|
-
before:
|
530
|
-
limit:
|
531
|
-
search:
|
532
|
-
ascending:
|
548
|
+
after: str | None = Query(None, description="Unique ID of the memory to start the query range at."),
|
549
|
+
before: str | None = Query(None, description="Unique ID of the memory to end the query range at."),
|
550
|
+
limit: int | None = Query(None, description="How many results to include in the response."),
|
551
|
+
search: str | None = Query(None, description="Search passages by text"),
|
552
|
+
ascending: bool | None = Query(
|
533
553
|
True, description="Whether to sort passages oldest to newest (True, default) or newest to oldest (False)"
|
534
554
|
),
|
535
|
-
actor_id:
|
555
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
536
556
|
):
|
537
557
|
"""
|
538
558
|
Retrieve the memories in an agent's archival memory store (paginated query).
|
@@ -550,12 +570,12 @@ async def list_passages(
|
|
550
570
|
)
|
551
571
|
|
552
572
|
|
553
|
-
@router.post("/{agent_id}/archival-memory", response_model=
|
573
|
+
@router.post("/{agent_id}/archival-memory", response_model=list[Passage], operation_id="create_passage")
|
554
574
|
async def create_passage(
|
555
575
|
agent_id: str,
|
556
576
|
request: CreateArchivalMemory = Body(...),
|
557
577
|
server: "SyncServer" = Depends(get_letta_server),
|
558
|
-
actor_id:
|
578
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
559
579
|
):
|
560
580
|
"""
|
561
581
|
Insert a memory into an agent's archival memory store.
|
@@ -565,13 +585,13 @@ async def create_passage(
|
|
565
585
|
return await server.insert_archival_memory_async(agent_id=agent_id, memory_contents=request.text, actor=actor)
|
566
586
|
|
567
587
|
|
568
|
-
@router.patch("/{agent_id}/archival-memory/{memory_id}", response_model=
|
588
|
+
@router.patch("/{agent_id}/archival-memory/{memory_id}", response_model=list[Passage], operation_id="modify_passage")
|
569
589
|
def modify_passage(
|
570
590
|
agent_id: str,
|
571
591
|
memory_id: str,
|
572
592
|
passage: PassageUpdate = Body(...),
|
573
593
|
server: "SyncServer" = Depends(get_letta_server),
|
574
|
-
actor_id:
|
594
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
575
595
|
):
|
576
596
|
"""
|
577
597
|
Modify a memory in the agent's archival memory store.
|
@@ -588,7 +608,7 @@ async def delete_passage(
|
|
588
608
|
memory_id: str,
|
589
609
|
# memory_id: str = Query(..., description="Unique ID of the memory to be deleted."),
|
590
610
|
server: "SyncServer" = Depends(get_letta_server),
|
591
|
-
actor_id:
|
611
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
592
612
|
):
|
593
613
|
"""
|
594
614
|
Delete a memory from an agent's archival memory store.
|
@@ -600,7 +620,7 @@ async def delete_passage(
|
|
600
620
|
|
601
621
|
|
602
622
|
AgentMessagesResponse = Annotated[
|
603
|
-
|
623
|
+
list[LettaMessageUnion], Field(json_schema_extra={"type": "array", "items": {"$ref": "#/components/schemas/LettaMessageUnion"}})
|
604
624
|
]
|
605
625
|
|
606
626
|
|
@@ -608,14 +628,14 @@ AgentMessagesResponse = Annotated[
|
|
608
628
|
async def list_messages(
|
609
629
|
agent_id: str,
|
610
630
|
server: "SyncServer" = Depends(get_letta_server),
|
611
|
-
after:
|
612
|
-
before:
|
631
|
+
after: str | None = Query(None, description="Message after which to retrieve the returned messages."),
|
632
|
+
before: str | None = Query(None, description="Message before which to retrieve the returned messages."),
|
613
633
|
limit: int = Query(10, description="Maximum number of messages to retrieve."),
|
614
|
-
group_id:
|
634
|
+
group_id: str | None = Query(None, description="Group ID to filter messages by."),
|
615
635
|
use_assistant_message: bool = Query(True, description="Whether to use assistant messages"),
|
616
636
|
assistant_message_tool_name: str = Query(DEFAULT_MESSAGE_TOOL, description="The name of the designated message tool."),
|
617
637
|
assistant_message_tool_kwarg: str = Query(DEFAULT_MESSAGE_TOOL_KWARG, description="The name of the message argument."),
|
618
|
-
actor_id:
|
638
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
619
639
|
):
|
620
640
|
"""
|
621
641
|
Retrieve message history for an agent.
|
@@ -643,7 +663,7 @@ def modify_message(
|
|
643
663
|
message_id: str,
|
644
664
|
request: LettaMessageUpdateUnion = Body(...),
|
645
665
|
server: "SyncServer" = Depends(get_letta_server),
|
646
|
-
actor_id:
|
666
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
647
667
|
):
|
648
668
|
"""
|
649
669
|
Update the details of a message associated with an agent.
|
@@ -653,6 +673,7 @@ def modify_message(
|
|
653
673
|
return server.message_manager.update_message_by_letta_message(message_id=message_id, letta_message_update=request, actor=actor)
|
654
674
|
|
655
675
|
|
676
|
+
# noinspection PyInconsistentReturns
|
656
677
|
@router.post(
|
657
678
|
"/{agent_id}/messages",
|
658
679
|
response_model=LettaResponse,
|
@@ -663,7 +684,7 @@ async def send_message(
|
|
663
684
|
request_obj: Request, # FastAPI Request
|
664
685
|
server: SyncServer = Depends(get_letta_server),
|
665
686
|
request: LettaRequest = Body(...),
|
666
|
-
actor_id:
|
687
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
667
688
|
):
|
668
689
|
"""
|
669
690
|
Process a user message and return the agent's response.
|
@@ -678,55 +699,95 @@ async def send_message(
|
|
678
699
|
agent_eligible = agent.multi_agent_group is None or agent.multi_agent_group.manager_type in ["sleeptime", "voice_sleeptime"]
|
679
700
|
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "together", "google_ai", "google_vertex", "bedrock"]
|
680
701
|
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
702
|
+
# Create a new run for execution tracking
|
703
|
+
job_status = JobStatus.created
|
704
|
+
run = await server.job_manager.create_job_async(
|
705
|
+
pydantic_job=Run(
|
706
|
+
user_id=actor.id,
|
707
|
+
status=job_status,
|
708
|
+
metadata={
|
709
|
+
"job_type": "send_message",
|
710
|
+
"agent_id": agent_id,
|
711
|
+
},
|
712
|
+
request_config=LettaRequestConfig(
|
713
|
+
use_assistant_message=request.use_assistant_message,
|
714
|
+
assistant_message_tool_name=request.assistant_message_tool_name,
|
715
|
+
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
716
|
+
include_return_message_types=request.include_return_message_types,
|
717
|
+
),
|
718
|
+
),
|
719
|
+
actor=actor,
|
720
|
+
)
|
721
|
+
job_update_metadata = None
|
722
|
+
# TODO (cliandy): clean this up
|
723
|
+
redis_client = await get_redis_client()
|
724
|
+
await redis_client.set(f"{REDIS_RUN_ID_PREFIX}:{agent_id}", run.id)
|
725
|
+
|
726
|
+
try:
|
727
|
+
if agent_eligible and model_compatible:
|
728
|
+
if agent.enable_sleeptime and agent.agent_type != AgentType.voice_convo_agent:
|
729
|
+
agent_loop = SleeptimeMultiAgentV2(
|
730
|
+
agent_id=agent_id,
|
731
|
+
message_manager=server.message_manager,
|
732
|
+
agent_manager=server.agent_manager,
|
733
|
+
block_manager=server.block_manager,
|
734
|
+
passage_manager=server.passage_manager,
|
735
|
+
group_manager=server.group_manager,
|
736
|
+
job_manager=server.job_manager,
|
737
|
+
actor=actor,
|
738
|
+
group=agent.multi_agent_group,
|
739
|
+
current_run_id=run.id,
|
740
|
+
)
|
741
|
+
else:
|
742
|
+
agent_loop = LettaAgent(
|
743
|
+
agent_id=agent_id,
|
744
|
+
message_manager=server.message_manager,
|
745
|
+
agent_manager=server.agent_manager,
|
746
|
+
block_manager=server.block_manager,
|
747
|
+
job_manager=server.job_manager,
|
748
|
+
passage_manager=server.passage_manager,
|
749
|
+
actor=actor,
|
750
|
+
step_manager=server.step_manager,
|
751
|
+
telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(),
|
752
|
+
current_run_id=run.id,
|
753
|
+
)
|
754
|
+
|
755
|
+
result = await agent_loop.step(
|
756
|
+
request.messages,
|
757
|
+
max_steps=request.max_steps,
|
758
|
+
use_assistant_message=request.use_assistant_message,
|
759
|
+
request_start_timestamp_ns=request_start_timestamp_ns,
|
760
|
+
include_return_message_types=request.include_return_message_types,
|
693
761
|
)
|
694
762
|
else:
|
695
|
-
|
763
|
+
result = await server.send_message_to_agent(
|
696
764
|
agent_id=agent_id,
|
697
|
-
message_manager=server.message_manager,
|
698
|
-
agent_manager=server.agent_manager,
|
699
|
-
block_manager=server.block_manager,
|
700
|
-
job_manager=server.job_manager,
|
701
|
-
passage_manager=server.passage_manager,
|
702
765
|
actor=actor,
|
703
|
-
|
704
|
-
|
766
|
+
input_messages=request.messages,
|
767
|
+
stream_steps=False,
|
768
|
+
stream_tokens=False,
|
769
|
+
# Support for AssistantMessage
|
770
|
+
use_assistant_message=request.use_assistant_message,
|
771
|
+
assistant_message_tool_name=request.assistant_message_tool_name,
|
772
|
+
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
773
|
+
include_return_message_types=request.include_return_message_types,
|
705
774
|
)
|
706
|
-
|
707
|
-
result
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
agent_id=agent_id,
|
775
|
+
job_status = result.stop_reason.stop_reason.run_status
|
776
|
+
return result
|
777
|
+
except Exception as e:
|
778
|
+
job_update_metadata = {"error": str(e)}
|
779
|
+
job_status = JobStatus.failed
|
780
|
+
raise
|
781
|
+
finally:
|
782
|
+
await server.job_manager.safe_update_job_status_async(
|
783
|
+
job_id=run.id,
|
784
|
+
new_status=job_status,
|
717
785
|
actor=actor,
|
718
|
-
|
719
|
-
stream_steps=False,
|
720
|
-
stream_tokens=False,
|
721
|
-
# Support for AssistantMessage
|
722
|
-
use_assistant_message=request.use_assistant_message,
|
723
|
-
assistant_message_tool_name=request.assistant_message_tool_name,
|
724
|
-
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
725
|
-
include_return_message_types=request.include_return_message_types,
|
786
|
+
metadata=job_update_metadata,
|
726
787
|
)
|
727
|
-
return result
|
728
788
|
|
729
789
|
|
790
|
+
# noinspection PyInconsistentReturns
|
730
791
|
@router.post(
|
731
792
|
"/{agent_id}/messages/stream",
|
732
793
|
response_model=None,
|
@@ -745,7 +806,7 @@ async def send_message_streaming(
|
|
745
806
|
request_obj: Request, # FastAPI Request
|
746
807
|
server: SyncServer = Depends(get_letta_server),
|
747
808
|
request: LettaStreamingRequest = Body(...),
|
748
|
-
actor_id:
|
809
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
749
810
|
) -> StreamingResponse | LettaResponse:
|
750
811
|
"""
|
751
812
|
Process a user message and return the agent's response.
|
@@ -761,88 +822,160 @@ async def send_message_streaming(
|
|
761
822
|
agent_eligible = agent.multi_agent_group is None or agent.multi_agent_group.manager_type in ["sleeptime", "voice_sleeptime"]
|
762
823
|
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "together", "google_ai", "google_vertex", "bedrock"]
|
763
824
|
model_compatible_token_streaming = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "bedrock"]
|
764
|
-
not_letta_endpoint =
|
825
|
+
not_letta_endpoint = agent.llm_config.model_endpoint != LETTA_MODEL_ENDPOINT
|
826
|
+
|
827
|
+
# Create a new job for execution tracking
|
828
|
+
job_status = JobStatus.created
|
829
|
+
run = await server.job_manager.create_job_async(
|
830
|
+
pydantic_job=Run(
|
831
|
+
user_id=actor.id,
|
832
|
+
status=job_status,
|
833
|
+
metadata={
|
834
|
+
"job_type": "send_message_streaming",
|
835
|
+
"agent_id": agent_id,
|
836
|
+
},
|
837
|
+
request_config=LettaRequestConfig(
|
838
|
+
use_assistant_message=request.use_assistant_message,
|
839
|
+
assistant_message_tool_name=request.assistant_message_tool_name,
|
840
|
+
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
841
|
+
include_return_message_types=request.include_return_message_types,
|
842
|
+
),
|
843
|
+
),
|
844
|
+
actor=actor,
|
845
|
+
)
|
765
846
|
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
847
|
+
job_update_metadata = None
|
848
|
+
# TODO (cliandy): clean this up
|
849
|
+
redis_client = await get_redis_client()
|
850
|
+
await redis_client.set(f"{REDIS_RUN_ID_PREFIX}:{agent_id}", run.id)
|
851
|
+
|
852
|
+
try:
|
853
|
+
if agent_eligible and model_compatible:
|
854
|
+
if agent.enable_sleeptime and agent.agent_type != AgentType.voice_convo_agent:
|
855
|
+
agent_loop = SleeptimeMultiAgentV2(
|
856
|
+
agent_id=agent_id,
|
857
|
+
message_manager=server.message_manager,
|
858
|
+
agent_manager=server.agent_manager,
|
859
|
+
block_manager=server.block_manager,
|
860
|
+
passage_manager=server.passage_manager,
|
861
|
+
group_manager=server.group_manager,
|
862
|
+
job_manager=server.job_manager,
|
863
|
+
actor=actor,
|
864
|
+
step_manager=server.step_manager,
|
865
|
+
telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(),
|
866
|
+
group=agent.multi_agent_group,
|
867
|
+
current_run_id=run.id,
|
868
|
+
)
|
869
|
+
else:
|
870
|
+
agent_loop = LettaAgent(
|
871
|
+
agent_id=agent_id,
|
872
|
+
message_manager=server.message_manager,
|
873
|
+
agent_manager=server.agent_manager,
|
874
|
+
block_manager=server.block_manager,
|
875
|
+
job_manager=server.job_manager,
|
876
|
+
passage_manager=server.passage_manager,
|
877
|
+
actor=actor,
|
878
|
+
step_manager=server.step_manager,
|
879
|
+
telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(),
|
880
|
+
current_run_id=run.id,
|
881
|
+
)
|
882
|
+
from letta.server.rest_api.streaming_response import StreamingResponseWithStatusCode
|
883
|
+
|
884
|
+
if request.stream_tokens and model_compatible_token_streaming and not_letta_endpoint:
|
885
|
+
result = StreamingResponseWithStatusCode(
|
886
|
+
agent_loop.step_stream(
|
887
|
+
input_messages=request.messages,
|
888
|
+
max_steps=request.max_steps,
|
889
|
+
use_assistant_message=request.use_assistant_message,
|
890
|
+
request_start_timestamp_ns=request_start_timestamp_ns,
|
891
|
+
include_return_message_types=request.include_return_message_types,
|
892
|
+
),
|
893
|
+
media_type="text/event-stream",
|
894
|
+
)
|
895
|
+
else:
|
896
|
+
result = StreamingResponseWithStatusCode(
|
897
|
+
agent_loop.step_stream_no_tokens(
|
898
|
+
request.messages,
|
899
|
+
max_steps=request.max_steps,
|
900
|
+
use_assistant_message=request.use_assistant_message,
|
901
|
+
request_start_timestamp_ns=request_start_timestamp_ns,
|
902
|
+
include_return_message_types=request.include_return_message_types,
|
903
|
+
),
|
904
|
+
media_type="text/event-stream",
|
905
|
+
)
|
781
906
|
else:
|
782
|
-
|
907
|
+
result = await server.send_message_to_agent(
|
783
908
|
agent_id=agent_id,
|
784
|
-
message_manager=server.message_manager,
|
785
|
-
agent_manager=server.agent_manager,
|
786
|
-
block_manager=server.block_manager,
|
787
|
-
job_manager=server.job_manager,
|
788
|
-
passage_manager=server.passage_manager,
|
789
909
|
actor=actor,
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
max_steps=request.max_steps,
|
800
|
-
use_assistant_message=request.use_assistant_message,
|
801
|
-
request_start_timestamp_ns=request_start_timestamp_ns,
|
802
|
-
include_return_message_types=request.include_return_message_types,
|
803
|
-
),
|
804
|
-
media_type="text/event-stream",
|
805
|
-
)
|
806
|
-
else:
|
807
|
-
result = StreamingResponseWithStatusCode(
|
808
|
-
agent_loop.step_stream_no_tokens(
|
809
|
-
request.messages,
|
810
|
-
max_steps=request.max_steps,
|
811
|
-
use_assistant_message=request.use_assistant_message,
|
812
|
-
request_start_timestamp_ns=request_start_timestamp_ns,
|
813
|
-
include_return_message_types=request.include_return_message_types,
|
814
|
-
),
|
815
|
-
media_type="text/event-stream",
|
910
|
+
input_messages=request.messages,
|
911
|
+
stream_steps=True,
|
912
|
+
stream_tokens=request.stream_tokens,
|
913
|
+
# Support for AssistantMessage
|
914
|
+
use_assistant_message=request.use_assistant_message,
|
915
|
+
assistant_message_tool_name=request.assistant_message_tool_name,
|
916
|
+
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
917
|
+
request_start_timestamp_ns=request_start_timestamp_ns,
|
918
|
+
include_return_message_types=request.include_return_message_types,
|
816
919
|
)
|
817
|
-
|
818
|
-
result
|
819
|
-
|
920
|
+
job_status = JobStatus.running
|
921
|
+
return result
|
922
|
+
except Exception as e:
|
923
|
+
job_update_metadata = {"error": str(e)}
|
924
|
+
job_status = JobStatus.failed
|
925
|
+
raise
|
926
|
+
finally:
|
927
|
+
await server.job_manager.safe_update_job_status_async(
|
928
|
+
job_id=run.id,
|
929
|
+
new_status=job_status,
|
820
930
|
actor=actor,
|
821
|
-
|
822
|
-
stream_steps=True,
|
823
|
-
stream_tokens=request.stream_tokens,
|
824
|
-
# Support for AssistantMessage
|
825
|
-
use_assistant_message=request.use_assistant_message,
|
826
|
-
assistant_message_tool_name=request.assistant_message_tool_name,
|
827
|
-
assistant_message_tool_kwarg=request.assistant_message_tool_kwarg,
|
828
|
-
request_start_timestamp_ns=request_start_timestamp_ns,
|
829
|
-
include_return_message_types=request.include_return_message_types,
|
931
|
+
metadata=job_update_metadata,
|
830
932
|
)
|
831
933
|
|
832
|
-
return result
|
833
934
|
|
935
|
+
@router.post("/{agent_id}/messages/cancel", operation_id="cancel_agent_run")
|
936
|
+
async def cancel_agent_run(
|
937
|
+
agent_id: str,
|
938
|
+
run_ids: list[str] | None = None,
|
939
|
+
server: SyncServer = Depends(get_letta_server),
|
940
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
941
|
+
) -> dict:
|
942
|
+
"""
|
943
|
+
Cancel runs associated with an agent. If run_ids are passed in, cancel those in particular.
|
834
944
|
|
835
|
-
|
836
|
-
|
945
|
+
Note to cancel active runs associated with an agent, redis is required.
|
946
|
+
"""
|
947
|
+
|
948
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
949
|
+
if not run_ids:
|
950
|
+
redis_client = await get_redis_client()
|
951
|
+
run_id = await redis_client.get(f"{REDIS_RUN_ID_PREFIX}:{agent_id}")
|
952
|
+
if run_id is None:
|
953
|
+
logger.warning("Cannot find run associated with agent to cancel.")
|
954
|
+
return {}
|
955
|
+
run_ids = [run_id]
|
956
|
+
|
957
|
+
results = {}
|
958
|
+
for run_id in run_ids:
|
959
|
+
success = await server.job_manager.safe_update_job_status_async(
|
960
|
+
job_id=run_id,
|
961
|
+
new_status=JobStatus.cancelled,
|
962
|
+
actor=actor,
|
963
|
+
)
|
964
|
+
results[run_id] = "cancelled" if success else "failed"
|
965
|
+
return results
|
966
|
+
|
967
|
+
|
968
|
+
async def _process_message_background(
|
969
|
+
run_id: str,
|
837
970
|
server: SyncServer,
|
838
971
|
actor: User,
|
839
972
|
agent_id: str,
|
840
|
-
messages:
|
973
|
+
messages: list[MessageCreate],
|
841
974
|
use_assistant_message: bool,
|
842
975
|
assistant_message_tool_name: str,
|
843
976
|
assistant_message_tool_kwarg: str,
|
844
977
|
max_steps: int = DEFAULT_MAX_STEPS,
|
845
|
-
include_return_message_types:
|
978
|
+
include_return_message_types: list[MessageType] | None = None,
|
846
979
|
) -> None:
|
847
980
|
"""Background task to process the message and update job status."""
|
848
981
|
request_start_timestamp_ns = get_utc_timestamp_ns()
|
@@ -886,7 +1019,7 @@ async def process_message_background(
|
|
886
1019
|
result = await agent_loop.step(
|
887
1020
|
messages,
|
888
1021
|
max_steps=max_steps,
|
889
|
-
run_id=
|
1022
|
+
run_id=run_id,
|
890
1023
|
use_assistant_message=use_assistant_message,
|
891
1024
|
request_start_timestamp_ns=request_start_timestamp_ns,
|
892
1025
|
include_return_message_types=include_return_message_types,
|
@@ -898,7 +1031,7 @@ async def process_message_background(
|
|
898
1031
|
input_messages=messages,
|
899
1032
|
stream_steps=False,
|
900
1033
|
stream_tokens=False,
|
901
|
-
metadata={"job_id":
|
1034
|
+
metadata={"job_id": run_id},
|
902
1035
|
# Support for AssistantMessage
|
903
1036
|
use_assistant_message=use_assistant_message,
|
904
1037
|
assistant_message_tool_name=assistant_message_tool_name,
|
@@ -911,7 +1044,7 @@ async def process_message_background(
|
|
911
1044
|
completed_at=datetime.now(timezone.utc),
|
912
1045
|
metadata={"result": result.model_dump(mode="json")},
|
913
1046
|
)
|
914
|
-
await server.job_manager.update_job_by_id_async(job_id=
|
1047
|
+
await server.job_manager.update_job_by_id_async(job_id=run_id, job_update=job_update, actor=actor)
|
915
1048
|
|
916
1049
|
except Exception as e:
|
917
1050
|
# Update job status to failed
|
@@ -932,11 +1065,14 @@ async def send_message_async(
|
|
932
1065
|
agent_id: str,
|
933
1066
|
server: SyncServer = Depends(get_letta_server),
|
934
1067
|
request: LettaAsyncRequest = Body(...),
|
935
|
-
actor_id:
|
1068
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
936
1069
|
):
|
937
1070
|
"""
|
938
1071
|
Asynchronously process a user message and return a run object.
|
939
1072
|
The actual processing happens in the background, and the status can be checked using the run ID.
|
1073
|
+
|
1074
|
+
This is "asynchronous" in the sense that it's a background job and explicitly must be fetched by the run ID.
|
1075
|
+
This is more like `send_message_job`
|
940
1076
|
"""
|
941
1077
|
MetricRegistry().user_message_counter.add(1, get_ctx_attributes())
|
942
1078
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
@@ -961,8 +1097,8 @@ async def send_message_async(
|
|
961
1097
|
|
962
1098
|
# Create asyncio task for background processing
|
963
1099
|
asyncio.create_task(
|
964
|
-
|
965
|
-
|
1100
|
+
_process_message_background(
|
1101
|
+
run_id=run.id,
|
966
1102
|
server=server,
|
967
1103
|
actor=actor,
|
968
1104
|
agent_id=agent_id,
|
@@ -983,7 +1119,7 @@ async def reset_messages(
|
|
983
1119
|
agent_id: str,
|
984
1120
|
add_default_initial_messages: bool = Query(default=False, description="If true, adds the default initial messages after resetting."),
|
985
1121
|
server: "SyncServer" = Depends(get_letta_server),
|
986
|
-
actor_id:
|
1122
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
987
1123
|
):
|
988
1124
|
"""Resets the messages for an agent"""
|
989
1125
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
@@ -992,12 +1128,12 @@ async def reset_messages(
|
|
992
1128
|
)
|
993
1129
|
|
994
1130
|
|
995
|
-
@router.get("/{agent_id}/groups", response_model=
|
1131
|
+
@router.get("/{agent_id}/groups", response_model=list[Group], operation_id="list_agent_groups")
|
996
1132
|
async def list_agent_groups(
|
997
1133
|
agent_id: str,
|
998
|
-
manager_type:
|
1134
|
+
manager_type: str | None = Query(None, description="Manager type to filter groups by"),
|
999
1135
|
server: "SyncServer" = Depends(get_letta_server),
|
1000
|
-
actor_id:
|
1136
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
1001
1137
|
):
|
1002
1138
|
"""Lists the groups for an agent"""
|
1003
1139
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
@@ -1011,7 +1147,7 @@ async def summarize_agent_conversation(
|
|
1011
1147
|
request_obj: Request, # FastAPI Request
|
1012
1148
|
max_message_length: int = Query(..., description="Maximum number of messages to retain after summarization."),
|
1013
1149
|
server: SyncServer = Depends(get_letta_server),
|
1014
|
-
actor_id:
|
1150
|
+
actor_id: str | None = Header(None, alias="user_id"),
|
1015
1151
|
):
|
1016
1152
|
"""
|
1017
1153
|
Summarize an agent's conversation history to a target message length.
|