letta-nightly 0.11.7.dev20250915104130__py3-none-any.whl → 0.11.7.dev20250917104122__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.
Files changed (99) hide show
  1. letta/__init__.py +10 -2
  2. letta/adapters/letta_llm_request_adapter.py +0 -1
  3. letta/adapters/letta_llm_stream_adapter.py +0 -1
  4. letta/agent.py +1 -1
  5. letta/agents/letta_agent.py +1 -4
  6. letta/agents/letta_agent_v2.py +2 -1
  7. letta/agents/voice_agent.py +1 -1
  8. letta/functions/function_sets/multi_agent.py +1 -1
  9. letta/functions/helpers.py +1 -1
  10. letta/helpers/converters.py +8 -2
  11. letta/helpers/crypto_utils.py +144 -0
  12. letta/llm_api/llm_api_tools.py +0 -1
  13. letta/llm_api/llm_client_base.py +0 -2
  14. letta/orm/__init__.py +1 -0
  15. letta/orm/agent.py +5 -1
  16. letta/orm/job.py +3 -1
  17. letta/orm/mcp_oauth.py +6 -0
  18. letta/orm/mcp_server.py +7 -1
  19. letta/orm/sqlalchemy_base.py +2 -1
  20. letta/prompts/gpt_system.py +13 -15
  21. letta/prompts/system_prompts/__init__.py +27 -0
  22. letta/prompts/{system/memgpt_chat.txt → system_prompts/memgpt_chat.py} +2 -0
  23. letta/prompts/{system/memgpt_generate_tool.txt → system_prompts/memgpt_generate_tool.py} +4 -2
  24. letta/prompts/{system/memgpt_v2_chat.txt → system_prompts/memgpt_v2_chat.py} +2 -0
  25. letta/prompts/{system/react.txt → system_prompts/react.py} +2 -0
  26. letta/prompts/{system/sleeptime_doc_ingest.txt → system_prompts/sleeptime_doc_ingest.py} +2 -0
  27. letta/prompts/{system/sleeptime_v2.txt → system_prompts/sleeptime_v2.py} +2 -0
  28. letta/prompts/{system/summary_system_prompt.txt → system_prompts/summary_system_prompt.py} +2 -0
  29. letta/prompts/{system/voice_chat.txt → system_prompts/voice_chat.py} +2 -0
  30. letta/prompts/{system/voice_sleeptime.txt → system_prompts/voice_sleeptime.py} +2 -0
  31. letta/prompts/{system/workflow.txt → system_prompts/workflow.py} +2 -0
  32. letta/schemas/agent.py +10 -7
  33. letta/schemas/job.py +10 -0
  34. letta/schemas/mcp.py +146 -6
  35. letta/schemas/provider_trace.py +0 -2
  36. letta/schemas/run.py +2 -0
  37. letta/schemas/secret.py +378 -0
  38. letta/serialize_schemas/marshmallow_agent.py +4 -0
  39. letta/server/rest_api/dependencies.py +37 -0
  40. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +4 -3
  41. letta/server/rest_api/routers/v1/__init__.py +2 -0
  42. letta/server/rest_api/routers/v1/agents.py +115 -107
  43. letta/server/rest_api/routers/v1/archives.py +113 -0
  44. letta/server/rest_api/routers/v1/blocks.py +44 -20
  45. letta/server/rest_api/routers/v1/embeddings.py +3 -3
  46. letta/server/rest_api/routers/v1/folders.py +107 -47
  47. letta/server/rest_api/routers/v1/groups.py +52 -32
  48. letta/server/rest_api/routers/v1/identities.py +110 -21
  49. letta/server/rest_api/routers/v1/internal_templates.py +28 -13
  50. letta/server/rest_api/routers/v1/jobs.py +19 -14
  51. letta/server/rest_api/routers/v1/llms.py +6 -8
  52. letta/server/rest_api/routers/v1/messages.py +14 -14
  53. letta/server/rest_api/routers/v1/organizations.py +1 -1
  54. letta/server/rest_api/routers/v1/providers.py +40 -16
  55. letta/server/rest_api/routers/v1/runs.py +28 -20
  56. letta/server/rest_api/routers/v1/sandbox_configs.py +25 -25
  57. letta/server/rest_api/routers/v1/sources.py +44 -45
  58. letta/server/rest_api/routers/v1/steps.py +27 -25
  59. letta/server/rest_api/routers/v1/tags.py +11 -7
  60. letta/server/rest_api/routers/v1/telemetry.py +11 -6
  61. letta/server/rest_api/routers/v1/tools.py +78 -80
  62. letta/server/rest_api/routers/v1/users.py +1 -1
  63. letta/server/rest_api/routers/v1/voice.py +6 -5
  64. letta/server/rest_api/utils.py +1 -18
  65. letta/services/agent_manager.py +17 -9
  66. letta/services/agent_serialization_manager.py +11 -3
  67. letta/services/archive_manager.py +73 -0
  68. letta/services/file_manager.py +6 -0
  69. letta/services/group_manager.py +2 -1
  70. letta/services/helpers/agent_manager_helper.py +6 -1
  71. letta/services/identity_manager.py +67 -0
  72. letta/services/job_manager.py +18 -2
  73. letta/services/mcp_manager.py +198 -82
  74. letta/services/provider_manager.py +14 -1
  75. letta/services/source_manager.py +11 -1
  76. letta/services/telemetry_manager.py +2 -0
  77. letta/services/tool_executor/composio_tool_executor.py +1 -1
  78. letta/services/tool_manager.py +46 -9
  79. letta/services/tool_sandbox/base.py +2 -3
  80. letta/utils.py +4 -2
  81. {letta_nightly-0.11.7.dev20250915104130.dist-info → letta_nightly-0.11.7.dev20250917104122.dist-info}/METADATA +5 -2
  82. {letta_nightly-0.11.7.dev20250915104130.dist-info → letta_nightly-0.11.7.dev20250917104122.dist-info}/RECORD +85 -94
  83. letta/prompts/system/memgpt_base.txt +0 -54
  84. letta/prompts/system/memgpt_chat_compressed.txt +0 -13
  85. letta/prompts/system/memgpt_chat_fstring.txt +0 -51
  86. letta/prompts/system/memgpt_convo_only.txt +0 -12
  87. letta/prompts/system/memgpt_doc.txt +0 -50
  88. letta/prompts/system/memgpt_gpt35_extralong.txt +0 -53
  89. letta/prompts/system/memgpt_intuitive_knowledge.txt +0 -31
  90. letta/prompts/system/memgpt_memory_only.txt +0 -29
  91. letta/prompts/system/memgpt_modified_chat.txt +0 -23
  92. letta/prompts/system/memgpt_modified_o1.txt +0 -31
  93. letta/prompts/system/memgpt_offline_memory.txt +0 -23
  94. letta/prompts/system/memgpt_offline_memory_chat.txt +0 -35
  95. letta/prompts/system/memgpt_sleeptime_chat.txt +0 -52
  96. letta/prompts/system/sleeptime.txt +0 -37
  97. {letta_nightly-0.11.7.dev20250915104130.dist-info → letta_nightly-0.11.7.dev20250917104122.dist-info}/WHEEL +0 -0
  98. {letta_nightly-0.11.7.dev20250915104130.dist-info → letta_nightly-0.11.7.dev20250917104122.dist-info}/entry_points.txt +0 -0
  99. {letta_nightly-0.11.7.dev20250915104130.dist-info → letta_nightly-0.11.7.dev20250917104122.dist-info}/licenses/LICENSE +0 -0
@@ -13,7 +13,6 @@ from sqlalchemy.exc import IntegrityError, OperationalError
13
13
  from starlette.responses import Response, StreamingResponse
14
14
 
15
15
  from letta.agents.agent_loop import AgentLoop
16
- from letta.agents.letta_agent import LettaAgent
17
16
  from letta.agents.letta_agent_v2 import LettaAgentV2
18
17
  from letta.constants import AGENT_ID_PATTERN, DEFAULT_MAX_STEPS, DEFAULT_MESSAGE_TOOL, DEFAULT_MESSAGE_TOOL_KWARG, REDIS_RUN_ID_PREFIX
19
18
  from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client
@@ -24,13 +23,12 @@ from letta.errors import (
24
23
  AgentNotFoundForExportError,
25
24
  PendingApprovalError,
26
25
  )
27
- from letta.groups.sleeptime_multi_agent_v2 import SleeptimeMultiAgentV2
28
26
  from letta.helpers.datetime_helpers import get_utc_timestamp_ns
29
27
  from letta.log import get_logger
30
28
  from letta.orm.errors import NoResultFound
31
29
  from letta.otel.context import get_ctx_attributes
32
30
  from letta.otel.metric_registry import MetricRegistry
33
- from letta.schemas.agent import AgentState, AgentType, CreateAgent, UpdateAgent
31
+ from letta.schemas.agent import AgentState, CreateAgent, UpdateAgent
34
32
  from letta.schemas.agent_file import AgentFileSchema
35
33
  from letta.schemas.block import Block, BlockUpdate
36
34
  from letta.schemas.enums import JobType
@@ -40,6 +38,7 @@ from letta.schemas.job import JobStatus, JobUpdate, LettaRequestConfig
40
38
  from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion, MessageType
41
39
  from letta.schemas.letta_request import LettaAsyncRequest, LettaRequest, LettaStreamingRequest
42
40
  from letta.schemas.letta_response import LettaResponse
41
+ from letta.schemas.letta_stop_reason import StopReasonType
43
42
  from letta.schemas.memory import (
44
43
  ArchivalMemorySearchResponse,
45
44
  ArchivalMemorySearchResult,
@@ -54,11 +53,9 @@ from letta.schemas.source import Source
54
53
  from letta.schemas.tool import Tool
55
54
  from letta.schemas.user import User
56
55
  from letta.serialize_schemas.pydantic_agent_schema import AgentSchema
56
+ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
57
57
  from letta.server.rest_api.redis_stream_manager import create_background_stream_processor, redis_sse_stream_generator
58
- from letta.server.rest_api.utils import get_letta_server
59
58
  from letta.server.server import SyncServer
60
- from letta.services.summarizer.enums import SummarizationMode
61
- from letta.services.telemetry_manager import NoopTelemetryManager
62
59
  from letta.settings import settings
63
60
  from letta.utils import safe_create_shielded_task, safe_create_task, truncate_file_visible_content
64
61
 
@@ -79,7 +76,7 @@ async def list_agents(
79
76
  description="If True, only returns agents that match ALL given tags. Otherwise, return agents that have ANY of the passed-in tags.",
80
77
  ),
81
78
  server: SyncServer = Depends(get_letta_server),
82
- actor_id: str | None = Header(None, alias="user_id"),
79
+ headers: HeaderParams = Depends(get_headers),
83
80
  before: str | None = Query(None, description="Cursor for pagination"),
84
81
  after: str | None = Query(None, description="Cursor for pagination"),
85
82
  limit: int | None = Query(50, description="Limit for pagination"),
@@ -97,13 +94,19 @@ async def list_agents(
97
94
  "Using this can optimize performance by reducing unnecessary joins."
98
95
  ),
99
96
  ),
97
+ order: Literal["asc", "desc"] = Query(
98
+ "desc", description="Sort order for agents by creation time. 'asc' for oldest first, 'desc' for newest first"
99
+ ),
100
+ order_by: Literal["created_at", "last_run_completion"] = Query("created_at", description="Field to sort by"),
100
101
  ascending: bool = Query(
101
102
  False,
102
103
  description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default)",
104
+ deprecated=True,
103
105
  ),
104
106
  sort_by: str | None = Query(
105
107
  "created_at",
106
108
  description="Field to sort by. Options: 'created_at' (default), 'last_run_completion'",
109
+ deprecated=True,
107
110
  ),
108
111
  show_hidden_agents: bool | None = Query(
109
112
  False,
@@ -112,14 +115,15 @@ async def list_agents(
112
115
  ),
113
116
  ):
114
117
  """
115
- List all agents associated with a given user.
116
-
117
- This endpoint retrieves a list of all agents and their configurations
118
- associated with the specified user ID.
118
+ Get a list of all agents.
119
119
  """
120
120
 
121
121
  # Retrieve the actor (user) details
122
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
122
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
123
+
124
+ # Handle backwards compatibility - prefer new parameters over legacy ones
125
+ final_ascending = (order == "asc") if order else ascending
126
+ final_sort_by = order_by if order_by else sort_by
123
127
 
124
128
  # Call list_agents directly without unnecessary dict handling
125
129
  return await server.agent_manager.list_agents_async(
@@ -137,8 +141,8 @@ async def list_agents(
137
141
  identity_id=identity_id,
138
142
  identifier_keys=identifier_keys,
139
143
  include_relationships=include_relationships,
140
- ascending=ascending,
141
- sort_by=sort_by,
144
+ ascending=final_ascending,
145
+ sort_by=final_sort_by,
142
146
  show_hidden_agents=show_hidden_agents,
143
147
  )
144
148
 
@@ -146,12 +150,12 @@ async def list_agents(
146
150
  @router.get("/count", response_model=int, operation_id="count_agents")
147
151
  async def count_agents(
148
152
  server: SyncServer = Depends(get_letta_server),
149
- actor_id: str | None = Header(None, alias="user_id"),
153
+ headers: HeaderParams = Depends(get_headers),
150
154
  ):
151
155
  """
152
- Get the count of all agents associated with a given user.
156
+ Get the total number of agents.
153
157
  """
154
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
158
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
155
159
  return await server.agent_manager.size_async(actor=actor)
156
160
 
157
161
 
@@ -167,7 +171,7 @@ async def export_agent(
167
171
  agent_id: str,
168
172
  max_steps: int = 100,
169
173
  server: "SyncServer" = Depends(get_letta_server),
170
- actor_id: str | None = Header(None, alias="user_id"),
174
+ headers: HeaderParams = Depends(get_headers),
171
175
  use_legacy_format: bool = Query(
172
176
  False,
173
177
  description="If true, exports using the legacy single-agent format (v1). If false, exports using the new multi-entity format (v2).",
@@ -184,7 +188,7 @@ async def export_agent(
184
188
  - Legacy format (use_legacy_format=true): Single agent with inline tools/blocks
185
189
  - New format (default): Multi-entity format with separate agents, tools, blocks, files, etc.
186
190
  """
187
- actor = server.user_manager.get_user_or_default(user_id=actor_id)
191
+ actor = server.user_manager.get_user_or_default(user_id=headers.actor_id)
188
192
 
189
193
  if use_legacy_format:
190
194
  # Use the legacy serialization method
@@ -317,7 +321,7 @@ async def _import_agent(
317
321
  async def import_agent(
318
322
  file: UploadFile = File(...),
319
323
  server: "SyncServer" = Depends(get_letta_server),
320
- actor_id: str | None = Header(None, alias="user_id"),
324
+ headers: HeaderParams = Depends(get_headers),
321
325
  x_override_embedding_model: str | None = Header(None, alias="x-override-embedding-model"),
322
326
  append_copy_suffix: bool = Form(True, description='If set to True, appends "_copy" to the end of the agent name.'),
323
327
  override_existing_tools: bool = Form(
@@ -341,7 +345,7 @@ async def import_agent(
341
345
  Import a serialized agent file and recreate the agent(s) in the system.
342
346
  Returns the IDs of all imported agents.
343
347
  """
344
- actor = server.user_manager.get_user_or_default(user_id=actor_id)
348
+ actor = server.user_manager.get_user_or_default(user_id=headers.actor_id)
345
349
 
346
350
  try:
347
351
  serialized_data = file.file.read()
@@ -398,12 +402,12 @@ async def import_agent(
398
402
  async def retrieve_agent_context_window(
399
403
  agent_id: str,
400
404
  server: "SyncServer" = Depends(get_letta_server),
401
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
405
+ headers: HeaderParams = Depends(get_headers),
402
406
  ):
403
407
  """
404
408
  Retrieve the context window of a specific agent.
405
409
  """
406
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
410
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
407
411
  try:
408
412
  return await server.agent_manager.get_context_window(agent_id=agent_id, actor=actor)
409
413
  except Exception as e:
@@ -424,16 +428,16 @@ class CreateAgentRequest(CreateAgent):
424
428
  async def create_agent(
425
429
  agent: CreateAgentRequest = Body(...),
426
430
  server: "SyncServer" = Depends(get_letta_server),
427
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
431
+ headers: HeaderParams = Depends(get_headers),
428
432
  x_project: str | None = Header(
429
433
  None, alias="X-Project", description="The project slug to associate with the agent (cloud only)."
430
434
  ), # Only handled by next js middleware
431
435
  ):
432
436
  """
433
- Create a new agent with the specified configuration.
437
+ Create an agent.
434
438
  """
435
439
  try:
436
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
440
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
437
441
  return await server.create_agent_async(agent, actor=actor)
438
442
  except Exception as e:
439
443
  traceback.print_exc()
@@ -445,10 +449,10 @@ async def modify_agent(
445
449
  agent_id: str,
446
450
  update_agent: UpdateAgent = Body(...),
447
451
  server: "SyncServer" = Depends(get_letta_server),
448
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
452
+ headers: HeaderParams = Depends(get_headers),
449
453
  ):
450
- """Update an existing agent"""
451
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
454
+ """Update an existing agent."""
455
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
452
456
  return await server.update_agent_async(agent_id=agent_id, request=update_agent, actor=actor)
453
457
 
454
458
 
@@ -456,10 +460,10 @@ async def modify_agent(
456
460
  async def list_agent_tools(
457
461
  agent_id: str,
458
462
  server: "SyncServer" = Depends(get_letta_server),
459
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
463
+ headers: HeaderParams = Depends(get_headers),
460
464
  ):
461
465
  """Get tools from an existing agent"""
462
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
466
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
463
467
  return await server.agent_manager.list_attached_tools_async(agent_id=agent_id, actor=actor)
464
468
 
465
469
 
@@ -468,12 +472,12 @@ async def attach_tool(
468
472
  agent_id: str,
469
473
  tool_id: str,
470
474
  server: "SyncServer" = Depends(get_letta_server),
471
- actor_id: str | None = Header(None, alias="user_id"),
475
+ headers: HeaderParams = Depends(get_headers),
472
476
  ):
473
477
  """
474
478
  Attach a tool to an agent.
475
479
  """
476
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
480
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
477
481
  await server.agent_manager.attach_tool_async(agent_id=agent_id, tool_id=tool_id, actor=actor)
478
482
  # TODO: Unfortunately we need this to preserve our current API behavior
479
483
  return await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, actor=actor)
@@ -484,12 +488,12 @@ async def detach_tool(
484
488
  agent_id: str,
485
489
  tool_id: str,
486
490
  server: "SyncServer" = Depends(get_letta_server),
487
- actor_id: str | None = Header(None, alias="user_id"),
491
+ headers: HeaderParams = Depends(get_headers),
488
492
  ):
489
493
  """
490
494
  Detach a tool from an agent.
491
495
  """
492
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
496
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
493
497
  await server.agent_manager.detach_tool_async(agent_id=agent_id, tool_id=tool_id, actor=actor)
494
498
  # TODO: Unfortunately we need this to preserve our current API behavior
495
499
  return await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, actor=actor)
@@ -501,12 +505,12 @@ async def modify_approval(
501
505
  tool_name: str,
502
506
  requires_approval: bool,
503
507
  server: "SyncServer" = Depends(get_letta_server),
504
- actor_id: str | None = Header(None, alias="user_id"),
508
+ headers: HeaderParams = Depends(get_headers),
505
509
  ):
506
510
  """
507
511
  Attach a tool to an agent.
508
512
  """
509
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
513
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
510
514
  await server.agent_manager.modify_approvals_async(
511
515
  agent_id=agent_id, tool_name=tool_name, requires_approval=requires_approval, actor=actor
512
516
  )
@@ -519,12 +523,12 @@ async def attach_source(
519
523
  agent_id: str,
520
524
  source_id: str,
521
525
  server: "SyncServer" = Depends(get_letta_server),
522
- actor_id: str | None = Header(None, alias="user_id"),
526
+ headers: HeaderParams = Depends(get_headers),
523
527
  ):
524
528
  """
525
529
  Attach a source to an agent.
526
530
  """
527
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
531
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
528
532
  agent_state = await server.agent_manager.attach_source_async(agent_id=agent_id, source_id=source_id, actor=actor)
529
533
 
530
534
  # Check if the agent is missing any files tools
@@ -546,12 +550,12 @@ async def attach_folder_to_agent(
546
550
  agent_id: str,
547
551
  folder_id: str,
548
552
  server: "SyncServer" = Depends(get_letta_server),
549
- actor_id: str | None = Header(None, alias="user_id"),
553
+ headers: HeaderParams = Depends(get_headers),
550
554
  ):
551
555
  """
552
556
  Attach a folder to an agent.
553
557
  """
554
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
558
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
555
559
  agent_state = await server.agent_manager.attach_source_async(agent_id=agent_id, source_id=folder_id, actor=actor)
556
560
 
557
561
  # Check if the agent is missing any files tools
@@ -573,12 +577,12 @@ async def detach_source(
573
577
  agent_id: str,
574
578
  source_id: str,
575
579
  server: "SyncServer" = Depends(get_letta_server),
576
- actor_id: str | None = Header(None, alias="user_id"),
580
+ headers: HeaderParams = Depends(get_headers),
577
581
  ):
578
582
  """
579
583
  Detach a source from an agent.
580
584
  """
581
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
585
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
582
586
  agent_state = await server.agent_manager.detach_source_async(agent_id=agent_id, source_id=source_id, actor=actor)
583
587
 
584
588
  if not agent_state.sources:
@@ -603,12 +607,12 @@ async def detach_folder_from_agent(
603
607
  agent_id: str,
604
608
  folder_id: str,
605
609
  server: "SyncServer" = Depends(get_letta_server),
606
- actor_id: str | None = Header(None, alias="user_id"),
610
+ headers: HeaderParams = Depends(get_headers),
607
611
  ):
608
612
  """
609
613
  Detach a folder from an agent.
610
614
  """
611
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
615
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
612
616
  agent_state = await server.agent_manager.detach_source_async(agent_id=agent_id, source_id=folder_id, actor=actor)
613
617
 
614
618
  if not agent_state.sources:
@@ -632,7 +636,7 @@ async def detach_folder_from_agent(
632
636
  async def close_all_open_files(
633
637
  agent_id: str,
634
638
  server: "SyncServer" = Depends(get_letta_server),
635
- actor_id: Optional[str] = Header(None, alias="user_id"),
639
+ headers: HeaderParams = Depends(get_headers),
636
640
  ):
637
641
  """
638
642
  Closes all currently open files for a given agent.
@@ -640,7 +644,7 @@ async def close_all_open_files(
640
644
  This endpoint updates the file state for the agent so that no files are marked as open.
641
645
  Typically used to reset the working memory view for the agent.
642
646
  """
643
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
647
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
644
648
 
645
649
  return await server.file_agent_manager.close_all_other_files(agent_id=agent_id, keep_file_names=[], actor=actor)
646
650
 
@@ -650,7 +654,7 @@ async def open_file(
650
654
  agent_id: str,
651
655
  file_id: str,
652
656
  server: "SyncServer" = Depends(get_letta_server),
653
- actor_id: Optional[str] = Header(None, alias="user_id"),
657
+ headers: HeaderParams = Depends(get_headers),
654
658
  ):
655
659
  """
656
660
  Opens a specific file for a given agent.
@@ -659,7 +663,7 @@ async def open_file(
659
663
  The file will be included in the agent's working memory view.
660
664
  Returns a list of file names that were closed due to LRU eviction.
661
665
  """
662
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
666
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
663
667
 
664
668
  # Get the agent to access files configuration
665
669
  try:
@@ -702,7 +706,7 @@ async def close_file(
702
706
  agent_id: str,
703
707
  file_id: str,
704
708
  server: "SyncServer" = Depends(get_letta_server),
705
- actor_id: Optional[str] = Header(None, alias="user_id"),
709
+ headers: HeaderParams = Depends(get_headers),
706
710
  ):
707
711
  """
708
712
  Closes a specific file for a given agent.
@@ -710,7 +714,7 @@ async def close_file(
710
714
  This endpoint marks a specific file as closed in the agent's file state.
711
715
  The file will be removed from the agent's working memory view.
712
716
  """
713
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
717
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
714
718
 
715
719
  # Use update_file_agent_by_id to close the file
716
720
  try:
@@ -737,7 +741,7 @@ async def retrieve_agent(
737
741
  ),
738
742
  ),
739
743
  server: "SyncServer" = Depends(get_letta_server),
740
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
744
+ headers: HeaderParams = Depends(get_headers),
741
745
  ):
742
746
  """
743
747
  Get the state of the agent.
@@ -746,7 +750,7 @@ async def retrieve_agent(
746
750
  if not AGENT_ID_PATTERN.match(agent_id):
747
751
  raise HTTPException(status_code=400, detail=f"agent_id {agent_id} is not in the valid format 'agent-<uuid4>'")
748
752
 
749
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
753
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
750
754
 
751
755
  try:
752
756
  return await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, include_relationships=include_relationships, actor=actor)
@@ -758,12 +762,12 @@ async def retrieve_agent(
758
762
  async def delete_agent(
759
763
  agent_id: str,
760
764
  server: "SyncServer" = Depends(get_letta_server),
761
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
765
+ headers: HeaderParams = Depends(get_headers),
762
766
  ):
763
767
  """
764
768
  Delete an agent.
765
769
  """
766
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
770
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
767
771
  try:
768
772
  await server.agent_manager.delete_agent_async(agent_id=agent_id, actor=actor)
769
773
  return JSONResponse(status_code=status.HTTP_200_OK, content={"message": f"Agent id={agent_id} successfully deleted"})
@@ -775,12 +779,12 @@ async def delete_agent(
775
779
  async def list_agent_sources(
776
780
  agent_id: str,
777
781
  server: "SyncServer" = Depends(get_letta_server),
778
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
782
+ headers: HeaderParams = Depends(get_headers),
779
783
  ):
780
784
  """
781
785
  Get the sources associated with an agent.
782
786
  """
783
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
787
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
784
788
  return await server.agent_manager.list_attached_sources_async(agent_id=agent_id, actor=actor)
785
789
 
786
790
 
@@ -788,12 +792,12 @@ async def list_agent_sources(
788
792
  async def list_agent_folders(
789
793
  agent_id: str,
790
794
  server: "SyncServer" = Depends(get_letta_server),
791
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
795
+ headers: HeaderParams = Depends(get_headers),
792
796
  ):
793
797
  """
794
798
  Get the folders associated with an agent.
795
799
  """
796
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
800
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
797
801
  return await server.agent_manager.list_attached_sources_async(agent_id=agent_id, actor=actor)
798
802
 
799
803
 
@@ -804,12 +808,12 @@ async def list_agent_files(
804
808
  limit: int = Query(20, ge=1, le=100, description="Number of items to return (1-100)"),
805
809
  is_open: Optional[bool] = Query(None, description="Filter by open status (true for open files, false for closed files)"),
806
810
  server: "SyncServer" = Depends(get_letta_server),
807
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
811
+ headers: HeaderParams = Depends(get_headers),
808
812
  ):
809
813
  """
810
814
  Get the files attached to an agent with their open/closed status (paginated).
811
815
  """
812
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
816
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
813
817
 
814
818
  # get paginated file-agent relationships for this agent
815
819
  file_agents, next_cursor, has_more = await server.file_agent_manager.list_files_for_agent_paginated(
@@ -845,13 +849,13 @@ async def list_agent_files(
845
849
  async def retrieve_agent_memory(
846
850
  agent_id: str,
847
851
  server: "SyncServer" = Depends(get_letta_server),
848
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
852
+ headers: HeaderParams = Depends(get_headers),
849
853
  ):
850
854
  """
851
855
  Retrieve the memory state of a specific agent.
852
856
  This endpoint fetches the current memory state of the agent identified by the user ID and agent ID.
853
857
  """
854
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
858
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
855
859
 
856
860
  return await server.get_agent_memory_async(agent_id=agent_id, actor=actor)
857
861
 
@@ -861,12 +865,12 @@ async def retrieve_block(
861
865
  agent_id: str,
862
866
  block_label: str,
863
867
  server: "SyncServer" = Depends(get_letta_server),
864
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
868
+ headers: HeaderParams = Depends(get_headers),
865
869
  ):
866
870
  """
867
871
  Retrieve a core memory block from an agent.
868
872
  """
869
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
873
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
870
874
 
871
875
  try:
872
876
  return await server.agent_manager.get_block_with_label_async(agent_id=agent_id, block_label=block_label, actor=actor)
@@ -878,12 +882,12 @@ async def retrieve_block(
878
882
  async def list_blocks(
879
883
  agent_id: str,
880
884
  server: "SyncServer" = Depends(get_letta_server),
881
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
885
+ headers: HeaderParams = Depends(get_headers),
882
886
  ):
883
887
  """
884
888
  Retrieve the core memory blocks of a specific agent.
885
889
  """
886
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
890
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
887
891
  try:
888
892
  agent = await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, include_relationships=["memory"], actor=actor)
889
893
  return agent.memory.blocks
@@ -897,12 +901,12 @@ async def modify_block(
897
901
  block_label: str,
898
902
  block_update: BlockUpdate = Body(...),
899
903
  server: "SyncServer" = Depends(get_letta_server),
900
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
904
+ headers: HeaderParams = Depends(get_headers),
901
905
  ):
902
906
  """
903
907
  Updates a core memory block of an agent.
904
908
  """
905
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
909
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
906
910
 
907
911
  block = await server.agent_manager.modify_block_by_label_async(
908
912
  agent_id=agent_id, block_label=block_label, block_update=block_update, actor=actor
@@ -919,12 +923,12 @@ async def attach_block(
919
923
  agent_id: str,
920
924
  block_id: str,
921
925
  server: "SyncServer" = Depends(get_letta_server),
922
- actor_id: str | None = Header(None, alias="user_id"),
926
+ headers: HeaderParams = Depends(get_headers),
923
927
  ):
924
928
  """
925
929
  Attach a core memory block to an agent.
926
930
  """
927
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
931
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
928
932
  return await server.agent_manager.attach_block_async(agent_id=agent_id, block_id=block_id, actor=actor)
929
933
 
930
934
 
@@ -933,12 +937,12 @@ async def detach_block(
933
937
  agent_id: str,
934
938
  block_id: str,
935
939
  server: "SyncServer" = Depends(get_letta_server),
936
- actor_id: str | None = Header(None, alias="user_id"),
940
+ headers: HeaderParams = Depends(get_headers),
937
941
  ):
938
942
  """
939
943
  Detach a core memory block from an agent.
940
944
  """
941
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
945
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
942
946
  return await server.agent_manager.detach_block_async(agent_id=agent_id, block_id=block_id, actor=actor)
943
947
 
944
948
 
@@ -953,12 +957,12 @@ async def list_passages(
953
957
  ascending: bool | None = Query(
954
958
  True, description="Whether to sort passages oldest to newest (True, default) or newest to oldest (False)"
955
959
  ),
956
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
960
+ headers: HeaderParams = Depends(get_headers),
957
961
  ):
958
962
  """
959
963
  Retrieve the memories in an agent's archival memory store (paginated query).
960
964
  """
961
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
965
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
962
966
 
963
967
  return await server.get_agent_archival_async(
964
968
  agent_id=agent_id,
@@ -976,12 +980,12 @@ async def create_passage(
976
980
  agent_id: str,
977
981
  request: CreateArchivalMemory = Body(...),
978
982
  server: "SyncServer" = Depends(get_letta_server),
979
- actor_id: str | None = Header(None, alias="user_id"),
983
+ headers: HeaderParams = Depends(get_headers),
980
984
  ):
981
985
  """
982
986
  Insert a memory into an agent's archival memory store.
983
987
  """
984
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
988
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
985
989
 
986
990
  return await server.insert_archival_memory_async(
987
991
  agent_id=agent_id, memory_contents=request.text, actor=actor, tags=request.tags, created_at=request.created_at
@@ -1000,7 +1004,7 @@ async def search_archival_memory(
1000
1004
  start_datetime: Optional[datetime] = Query(None, description="Filter results to passages created after this datetime"),
1001
1005
  end_datetime: Optional[datetime] = Query(None, description="Filter results to passages created before this datetime"),
1002
1006
  server: "SyncServer" = Depends(get_letta_server),
1003
- actor_id: str | None = Header(None, alias="user_id"),
1007
+ headers: HeaderParams = Depends(get_headers),
1004
1008
  ):
1005
1009
  """
1006
1010
  Search archival memory using semantic (embedding-based) search with optional temporal filtering.
@@ -1009,7 +1013,7 @@ async def search_archival_memory(
1009
1013
  an agent's archival memory store directly via the API. The search uses the same functionality
1010
1014
  as the agent's archival_memory_search tool but is accessible for external API usage.
1011
1015
  """
1012
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1016
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1013
1017
 
1014
1018
  try:
1015
1019
  # convert datetime to string in ISO 8601 format
@@ -1049,12 +1053,12 @@ async def delete_passage(
1049
1053
  memory_id: str,
1050
1054
  # memory_id: str = Query(..., description="Unique ID of the memory to be deleted."),
1051
1055
  server: "SyncServer" = Depends(get_letta_server),
1052
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1056
+ headers: HeaderParams = Depends(get_headers),
1053
1057
  ):
1054
1058
  """
1055
1059
  Delete a memory from an agent's archival memory store.
1056
1060
  """
1057
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1061
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1058
1062
 
1059
1063
  await server.delete_archival_memory_async(memory_id=memory_id, actor=actor)
1060
1064
  return JSONResponse(status_code=status.HTTP_200_OK, content={"message": f"Memory id={memory_id} successfully deleted"})
@@ -1079,12 +1083,12 @@ async def list_messages(
1079
1083
  include_err: bool | None = Query(
1080
1084
  None, description="Whether to include error messages and error statuses. For debugging purposes only."
1081
1085
  ),
1082
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1086
+ headers: HeaderParams = Depends(get_headers),
1083
1087
  ):
1084
1088
  """
1085
1089
  Retrieve message history for an agent.
1086
1090
  """
1087
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1091
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1088
1092
 
1089
1093
  return await server.get_agent_recall_async(
1090
1094
  agent_id=agent_id,
@@ -1108,13 +1112,13 @@ def modify_message(
1108
1112
  message_id: str,
1109
1113
  request: LettaMessageUpdateUnion = Body(...),
1110
1114
  server: "SyncServer" = Depends(get_letta_server),
1111
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1115
+ headers: HeaderParams = Depends(get_headers),
1112
1116
  ):
1113
1117
  """
1114
1118
  Update the details of a message associated with an agent.
1115
1119
  """
1116
1120
  # TODO: support modifying tool calls/returns
1117
- actor = server.user_manager.get_user_or_default(user_id=actor_id)
1121
+ actor = server.user_manager.get_user_or_default(user_id=headers.actor_id)
1118
1122
  return server.message_manager.update_message_by_letta_message(message_id=message_id, letta_message_update=request, actor=actor)
1119
1123
 
1120
1124
 
@@ -1129,7 +1133,7 @@ async def send_message(
1129
1133
  request_obj: Request, # FastAPI Request
1130
1134
  server: SyncServer = Depends(get_letta_server),
1131
1135
  request: LettaRequest = Body(...),
1132
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1136
+ headers: HeaderParams = Depends(get_headers),
1133
1137
  ):
1134
1138
  """
1135
1139
  Process a user message and return the agent's response.
@@ -1140,7 +1144,7 @@ async def send_message(
1140
1144
  request_start_timestamp_ns = get_utc_timestamp_ns()
1141
1145
  MetricRegistry().user_message_counter.add(1, get_ctx_attributes())
1142
1146
 
1143
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1147
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1144
1148
  # TODO: This is redundant, remove soon
1145
1149
  agent = await server.agent_manager.get_agent_by_id_async(
1146
1150
  agent_id, actor, include_relationships=["memory", "multi_agent_group", "sources", "tool_exec_environment_variables", "tools"]
@@ -1189,6 +1193,7 @@ async def send_message(
1189
1193
  await redis_client.set(f"{REDIS_RUN_ID_PREFIX}:{agent_id}", run.id if run else None)
1190
1194
 
1191
1195
  try:
1196
+ result = None
1192
1197
  if agent_eligible and model_compatible:
1193
1198
  agent_loop = AgentLoop.load(agent_state=agent, actor=actor)
1194
1199
  result = await agent_loop.step(
@@ -1226,11 +1231,17 @@ async def send_message(
1226
1231
  raise
1227
1232
  finally:
1228
1233
  if settings.track_agent_run:
1234
+ if result:
1235
+ stop_reason = result.stop_reason.stop_reason
1236
+ else:
1237
+ # NOTE: we could also consider this an error?
1238
+ stop_reason = None
1229
1239
  await server.job_manager.safe_update_job_status_async(
1230
1240
  job_id=run.id,
1231
1241
  new_status=job_status,
1232
1242
  actor=actor,
1233
1243
  metadata=job_update_metadata,
1244
+ stop_reason=stop_reason,
1234
1245
  )
1235
1246
 
1236
1247
 
@@ -1253,7 +1264,7 @@ async def send_message_streaming(
1253
1264
  request_obj: Request, # FastAPI Request
1254
1265
  server: SyncServer = Depends(get_letta_server),
1255
1266
  request: LettaStreamingRequest = Body(...),
1256
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1267
+ headers: HeaderParams = Depends(get_headers),
1257
1268
  ) -> StreamingResponse | LettaResponse:
1258
1269
  """
1259
1270
  Process a user message and return the agent's response.
@@ -1266,7 +1277,7 @@ async def send_message_streaming(
1266
1277
  # TODO (cliandy): clean this up
1267
1278
  redis_client = await get_redis_client()
1268
1279
 
1269
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1280
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1270
1281
  # TODO: This is redundant, remove soon
1271
1282
  agent = await server.agent_manager.get_agent_by_id_async(
1272
1283
  agent_id, actor, include_relationships=["memory", "multi_agent_group", "sources", "tool_exec_environment_variables", "tools"]
@@ -1437,10 +1448,7 @@ async def send_message_streaming(
1437
1448
  finally:
1438
1449
  if settings.track_agent_run:
1439
1450
  await server.job_manager.safe_update_job_status_async(
1440
- job_id=run.id,
1441
- new_status=job_status,
1442
- actor=actor,
1443
- metadata=job_update_metadata,
1451
+ job_id=run.id, new_status=job_status, actor=actor, metadata=job_update_metadata
1444
1452
  )
1445
1453
 
1446
1454
 
@@ -1453,14 +1461,14 @@ async def cancel_agent_run(
1453
1461
  agent_id: str,
1454
1462
  request: CancelAgentRunRequest = Body(None),
1455
1463
  server: SyncServer = Depends(get_letta_server),
1456
- actor_id: str | None = Header(None, alias="user_id"),
1464
+ headers: HeaderParams = Depends(get_headers),
1457
1465
  ) -> dict:
1458
1466
  """
1459
1467
  Cancel runs associated with an agent. If run_ids are passed in, cancel those in particular.
1460
1468
 
1461
1469
  Note to cancel active runs associated with an agent, redis is required.
1462
1470
  """
1463
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1471
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1464
1472
  if not settings.track_agent_run:
1465
1473
  raise HTTPException(status_code=400, detail="Agent run tracking is disabled")
1466
1474
  run_ids = request.run_ids if request else None
@@ -1494,14 +1502,14 @@ async def cancel_agent_run(
1494
1502
  async def search_messages(
1495
1503
  request: MessageSearchRequest = Body(...),
1496
1504
  server: SyncServer = Depends(get_letta_server),
1497
- actor_id: str | None = Header(None, alias="user_id"),
1505
+ headers: HeaderParams = Depends(get_headers),
1498
1506
  ):
1499
1507
  """
1500
1508
  Search messages across the entire organization with optional project and template filtering. Returns messages with FTS/vector ranks and total RRF score.
1501
1509
 
1502
1510
  This is a cloud-only feature.
1503
1511
  """
1504
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1512
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1505
1513
 
1506
1514
  # get embedding config from the default agent if needed
1507
1515
  # check if any agents exist in the org
@@ -1617,7 +1625,7 @@ async def send_message_async(
1617
1625
  agent_id: str,
1618
1626
  server: SyncServer = Depends(get_letta_server),
1619
1627
  request: LettaAsyncRequest = Body(...),
1620
- actor_id: str | None = Header(None, alias="user_id"),
1628
+ headers: HeaderParams = Depends(get_headers),
1621
1629
  ):
1622
1630
  """
1623
1631
  Asynchronously process a user message and return a run object.
@@ -1627,7 +1635,7 @@ async def send_message_async(
1627
1635
  This is more like `send_message_job`
1628
1636
  """
1629
1637
  MetricRegistry().user_message_counter.add(1, get_ctx_attributes())
1630
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1638
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1631
1639
 
1632
1640
  # Create a new job
1633
1641
  run = Run(
@@ -1696,10 +1704,10 @@ async def reset_messages(
1696
1704
  agent_id: str,
1697
1705
  add_default_initial_messages: bool = Query(default=False, description="If true, adds the default initial messages after resetting."),
1698
1706
  server: "SyncServer" = Depends(get_letta_server),
1699
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1707
+ headers: HeaderParams = Depends(get_headers),
1700
1708
  ):
1701
1709
  """Resets the messages for an agent"""
1702
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1710
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1703
1711
  return await server.agent_manager.reset_messages_async(
1704
1712
  agent_id=agent_id, actor=actor, add_default_initial_messages=add_default_initial_messages
1705
1713
  )
@@ -1710,10 +1718,10 @@ async def list_agent_groups(
1710
1718
  agent_id: str,
1711
1719
  manager_type: str | None = Query(None, description="Manager type to filter groups by"),
1712
1720
  server: "SyncServer" = Depends(get_letta_server),
1713
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
1721
+ headers: HeaderParams = Depends(get_headers),
1714
1722
  ):
1715
1723
  """Lists the groups for an agent"""
1716
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1724
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1717
1725
  logger.info("in list agents with manager_type", manager_type)
1718
1726
  return server.agent_manager.list_groups(agent_id=agent_id, manager_type=manager_type, actor=actor)
1719
1727
 
@@ -1727,7 +1735,7 @@ async def preview_raw_payload(
1727
1735
  agent_id: str,
1728
1736
  request: Union[LettaRequest, LettaStreamingRequest] = Body(...),
1729
1737
  server: SyncServer = Depends(get_letta_server),
1730
- actor_id: str | None = Header(None, alias="user_id"),
1738
+ headers: HeaderParams = Depends(get_headers),
1731
1739
  ):
1732
1740
  """
1733
1741
  Inspect the raw LLM request payload without sending it.
@@ -1736,7 +1744,7 @@ async def preview_raw_payload(
1736
1744
  the LLM request, then returns the raw request payload that would
1737
1745
  be sent to the LLM provider. Useful for debugging and inspection.
1738
1746
  """
1739
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1747
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1740
1748
  agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor, include_relationships=["multi_agent_group"])
1741
1749
  agent_eligible = agent.multi_agent_group is None or agent.multi_agent_group.manager_type in ["sleeptime", "voice_sleeptime"]
1742
1750
  model_compatible = agent.llm_config.model_endpoint_type in [
@@ -1771,7 +1779,7 @@ async def summarize_agent_conversation(
1771
1779
  request_obj: Request, # FastAPI Request
1772
1780
  max_message_length: int = Query(..., description="Maximum number of messages to retain after summarization."),
1773
1781
  server: SyncServer = Depends(get_letta_server),
1774
- actor_id: str | None = Header(None, alias="user_id"),
1782
+ headers: HeaderParams = Depends(get_headers),
1775
1783
  ):
1776
1784
  """
1777
1785
  Summarize an agent's conversation history to a target message length.
@@ -1780,7 +1788,7 @@ async def summarize_agent_conversation(
1780
1788
  truncating and compressing it down to the specified `max_message_length`.
1781
1789
  """
1782
1790
 
1783
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
1791
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
1784
1792
  agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor, include_relationships=["multi_agent_group"])
1785
1793
  agent_eligible = agent.multi_agent_group is None or agent.multi_agent_group.manager_type in ["sleeptime", "voice_sleeptime"]
1786
1794
  model_compatible = agent.llm_config.model_endpoint_type in [