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