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
@@ -17,7 +17,7 @@ from letta.schemas.sandbox_config import (
17
17
  SandboxConfigCreate,
18
18
  SandboxConfigUpdate,
19
19
  )
20
- from letta.server.rest_api.utils import get_letta_server, get_user_id
20
+ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
21
21
  from letta.server.server import SyncServer
22
22
  from letta.services.helpers.tool_execution_helper import create_venv_for_local_sandbox, install_pip_requirements_for_sandbox
23
23
 
@@ -32,9 +32,9 @@ logger = get_logger(__name__)
32
32
  async def create_sandbox_config(
33
33
  config_create: SandboxConfigCreate,
34
34
  server: SyncServer = Depends(get_letta_server),
35
- actor_id: str = Depends(get_user_id),
35
+ headers: HeaderParams = Depends(get_headers),
36
36
  ):
37
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
37
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
38
38
 
39
39
  return await server.sandbox_config_manager.create_or_update_sandbox_config_async(config_create, actor)
40
40
 
@@ -42,18 +42,18 @@ async def create_sandbox_config(
42
42
  @router.post("/e2b/default", response_model=PydanticSandboxConfig)
43
43
  async def create_default_e2b_sandbox_config(
44
44
  server: SyncServer = Depends(get_letta_server),
45
- actor_id: str = Depends(get_user_id),
45
+ headers: HeaderParams = Depends(get_headers),
46
46
  ):
47
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
47
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
48
48
  return await server.sandbox_config_manager.get_or_create_default_sandbox_config_async(sandbox_type=SandboxType.E2B, actor=actor)
49
49
 
50
50
 
51
51
  @router.post("/local/default", response_model=PydanticSandboxConfig)
52
52
  async def create_default_local_sandbox_config(
53
53
  server: SyncServer = Depends(get_letta_server),
54
- actor_id: str = Depends(get_user_id),
54
+ headers: HeaderParams = Depends(get_headers),
55
55
  ):
56
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
56
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
57
57
  return await server.sandbox_config_manager.get_or_create_default_sandbox_config_async(sandbox_type=SandboxType.LOCAL, actor=actor)
58
58
 
59
59
 
@@ -61,7 +61,7 @@ async def create_default_local_sandbox_config(
61
61
  async def create_custom_local_sandbox_config(
62
62
  local_sandbox_config: LocalSandboxConfig,
63
63
  server: SyncServer = Depends(get_letta_server),
64
- actor_id: str = Depends(get_user_id),
64
+ headers: HeaderParams = Depends(get_headers),
65
65
  ):
66
66
  """
67
67
  Create or update a custom LocalSandboxConfig, including pip_requirements.
@@ -74,7 +74,7 @@ async def create_custom_local_sandbox_config(
74
74
  )
75
75
 
76
76
  # Retrieve the user (actor)
77
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
77
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
78
78
 
79
79
  # Wrap the LocalSandboxConfig into a SandboxConfigCreate
80
80
  sandbox_config_create = SandboxConfigCreate(config=local_sandbox_config)
@@ -90,9 +90,9 @@ async def update_sandbox_config(
90
90
  sandbox_config_id: str,
91
91
  config_update: SandboxConfigUpdate,
92
92
  server: SyncServer = Depends(get_letta_server),
93
- actor_id: str = Depends(get_user_id),
93
+ headers: HeaderParams = Depends(get_headers),
94
94
  ):
95
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
95
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
96
96
  return await server.sandbox_config_manager.update_sandbox_config_async(sandbox_config_id, config_update, actor)
97
97
 
98
98
 
@@ -100,9 +100,9 @@ async def update_sandbox_config(
100
100
  async def delete_sandbox_config(
101
101
  sandbox_config_id: str,
102
102
  server: SyncServer = Depends(get_letta_server),
103
- actor_id: str = Depends(get_user_id),
103
+ headers: HeaderParams = Depends(get_headers),
104
104
  ):
105
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
105
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
106
106
  await server.sandbox_config_manager.delete_sandbox_config_async(sandbox_config_id, actor)
107
107
 
108
108
 
@@ -112,22 +112,22 @@ async def list_sandbox_configs(
112
112
  after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
113
113
  sandbox_type: Optional[SandboxType] = Query(None, description="Filter for this specific sandbox type"),
114
114
  server: SyncServer = Depends(get_letta_server),
115
- actor_id: str = Depends(get_user_id),
115
+ headers: HeaderParams = Depends(get_headers),
116
116
  ):
117
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
117
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
118
118
  return await server.sandbox_config_manager.list_sandbox_configs_async(actor, limit=limit, after=after, sandbox_type=sandbox_type)
119
119
 
120
120
 
121
121
  @router.post("/local/recreate-venv", response_model=PydanticSandboxConfig)
122
122
  async def force_recreate_local_sandbox_venv(
123
123
  server: SyncServer = Depends(get_letta_server),
124
- actor_id: str = Depends(get_user_id),
124
+ headers: HeaderParams = Depends(get_headers),
125
125
  ):
126
126
  """
127
127
  Forcefully recreate the virtual environment for the local sandbox.
128
128
  Deletes and recreates the venv, then reinstalls required dependencies.
129
129
  """
130
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
130
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
131
131
 
132
132
  # Retrieve the local sandbox config
133
133
  sbx_config = await server.sandbox_config_manager.get_or_create_default_sandbox_config_async(sandbox_type=SandboxType.LOCAL, actor=actor)
@@ -169,9 +169,9 @@ async def create_sandbox_env_var(
169
169
  sandbox_config_id: str,
170
170
  env_var_create: SandboxEnvironmentVariableCreate,
171
171
  server: SyncServer = Depends(get_letta_server),
172
- actor_id: str = Depends(get_user_id),
172
+ headers: HeaderParams = Depends(get_headers),
173
173
  ):
174
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
174
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
175
175
  return await server.sandbox_config_manager.create_sandbox_env_var_async(env_var_create, sandbox_config_id, actor)
176
176
 
177
177
 
@@ -180,9 +180,9 @@ async def update_sandbox_env_var(
180
180
  env_var_id: str,
181
181
  env_var_update: SandboxEnvironmentVariableUpdate,
182
182
  server: SyncServer = Depends(get_letta_server),
183
- actor_id: str = Depends(get_user_id),
183
+ headers: HeaderParams = Depends(get_headers),
184
184
  ):
185
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
185
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
186
186
  return await server.sandbox_config_manager.update_sandbox_env_var_async(env_var_id, env_var_update, actor)
187
187
 
188
188
 
@@ -190,9 +190,9 @@ async def update_sandbox_env_var(
190
190
  async def delete_sandbox_env_var(
191
191
  env_var_id: str,
192
192
  server: SyncServer = Depends(get_letta_server),
193
- actor_id: str = Depends(get_user_id),
193
+ headers: HeaderParams = Depends(get_headers),
194
194
  ):
195
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
195
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
196
196
  await server.sandbox_config_manager.delete_sandbox_env_var_async(env_var_id, actor)
197
197
 
198
198
 
@@ -202,7 +202,7 @@ async def list_sandbox_env_vars(
202
202
  limit: int = Query(1000, description="Number of results to return"),
203
203
  after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
204
204
  server: SyncServer = Depends(get_letta_server),
205
- actor_id: str = Depends(get_user_id),
205
+ headers: HeaderParams = Depends(get_headers),
206
206
  ):
207
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
207
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
208
208
  return await server.sandbox_config_manager.list_sandbox_env_vars_async(sandbox_config_id, actor, limit=limit, after=after)
@@ -1,11 +1,10 @@
1
- import asyncio
2
1
  import mimetypes
3
2
  import os
4
3
  import tempfile
5
4
  from pathlib import Path
6
5
  from typing import List, Optional
7
6
 
8
- from fastapi import APIRouter, Depends, Header, HTTPException, Query, UploadFile
7
+ from fastapi import APIRouter, Depends, HTTPException, Query, UploadFile
9
8
  from starlette import status
10
9
  from starlette.responses import Response
11
10
 
@@ -26,7 +25,7 @@ from letta.schemas.passage import Passage
26
25
  from letta.schemas.source import Source, SourceCreate, SourceUpdate
27
26
  from letta.schemas.source_metadata import OrganizationSourcesStats
28
27
  from letta.schemas.user import User
29
- from letta.server.rest_api.utils import get_letta_server
28
+ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
30
29
  from letta.server.server import SyncServer
31
30
  from letta.services.file_processor.embedder.openai_embedder import OpenAIEmbedder
32
31
  from letta.services.file_processor.embedder.pinecone_embedder import PineconeEmbedder
@@ -45,28 +44,28 @@ register_mime_types()
45
44
  router = APIRouter(prefix="/sources", tags=["sources"])
46
45
 
47
46
 
48
- @router.get("/count", response_model=int, operation_id="count_sources")
47
+ @router.get("/count", response_model=int, operation_id="count_sources", deprecated=True)
49
48
  async def count_sources(
50
49
  server: "SyncServer" = Depends(get_letta_server),
51
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
50
+ headers: HeaderParams = Depends(get_headers),
52
51
  ):
53
52
  """
54
53
  Count all data sources created by a user.
55
54
  """
56
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
55
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
57
56
  return await server.source_manager.size_async(actor=actor)
58
57
 
59
58
 
60
- @router.get("/{source_id}", response_model=Source, operation_id="retrieve_source")
59
+ @router.get("/{source_id}", response_model=Source, operation_id="retrieve_source", deprecated=True)
61
60
  async def retrieve_source(
62
61
  source_id: str,
63
62
  server: "SyncServer" = Depends(get_letta_server),
64
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
63
+ headers: HeaderParams = Depends(get_headers),
65
64
  ):
66
65
  """
67
66
  Get all sources
68
67
  """
69
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
68
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
70
69
 
71
70
  source = await server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
72
71
  if not source:
@@ -74,16 +73,16 @@ async def retrieve_source(
74
73
  return source
75
74
 
76
75
 
77
- @router.get("/name/{source_name}", response_model=str, operation_id="get_source_id_by_name")
76
+ @router.get("/name/{source_name}", response_model=str, operation_id="get_source_id_by_name", deprecated=True)
78
77
  async def get_source_id_by_name(
79
78
  source_name: str,
80
79
  server: "SyncServer" = Depends(get_letta_server),
81
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
80
+ headers: HeaderParams = Depends(get_headers),
82
81
  ):
83
82
  """
84
83
  Get a source by name
85
84
  """
86
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
85
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
87
86
 
88
87
  source = await server.source_manager.get_source_by_name(source_name=source_name, actor=actor)
89
88
  if not source:
@@ -91,10 +90,10 @@ async def get_source_id_by_name(
91
90
  return source.id
92
91
 
93
92
 
94
- @router.get("/metadata", response_model=OrganizationSourcesStats, operation_id="get_sources_metadata")
93
+ @router.get("/metadata", response_model=OrganizationSourcesStats, operation_id="get_sources_metadata", deprecated=True)
95
94
  async def get_sources_metadata(
96
95
  server: "SyncServer" = Depends(get_letta_server),
97
- actor_id: Optional[str] = Header(None, alias="user_id"),
96
+ headers: HeaderParams = Depends(get_headers),
98
97
  include_detailed_per_source_metadata: bool = False,
99
98
  ):
100
99
  """
@@ -106,34 +105,34 @@ async def get_sources_metadata(
106
105
  - Total size of all files
107
106
  - Per-source breakdown with file details (file_name, file_size per file) if include_detailed_per_source_metadata is True
108
107
  """
109
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
108
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
110
109
  return await server.file_manager.get_organization_sources_metadata(
111
110
  actor=actor, include_detailed_per_source_metadata=include_detailed_per_source_metadata
112
111
  )
113
112
 
114
113
 
115
- @router.get("/", response_model=List[Source], operation_id="list_sources")
114
+ @router.get("/", response_model=List[Source], operation_id="list_sources", deprecated=True)
116
115
  async def list_sources(
117
116
  server: "SyncServer" = Depends(get_letta_server),
118
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
117
+ headers: HeaderParams = Depends(get_headers),
119
118
  ):
120
119
  """
121
120
  List all data sources created by a user.
122
121
  """
123
- 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)
124
123
  return await server.source_manager.list_sources(actor=actor)
125
124
 
126
125
 
127
- @router.post("/", response_model=Source, operation_id="create_source")
126
+ @router.post("/", response_model=Source, operation_id="create_source", deprecated=True)
128
127
  async def create_source(
129
128
  source_create: SourceCreate,
130
129
  server: "SyncServer" = Depends(get_letta_server),
131
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
130
+ headers: HeaderParams = Depends(get_headers),
132
131
  ):
133
132
  """
134
133
  Create a new data source.
135
134
  """
136
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
135
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
137
136
 
138
137
  # TODO: need to asyncify this
139
138
  if not source_create.embedding_config:
@@ -158,33 +157,33 @@ async def create_source(
158
157
  return await server.source_manager.create_source(source=source, actor=actor)
159
158
 
160
159
 
161
- @router.patch("/{source_id}", response_model=Source, operation_id="modify_source")
160
+ @router.patch("/{source_id}", response_model=Source, operation_id="modify_source", deprecated=True)
162
161
  async def modify_source(
163
162
  source_id: str,
164
163
  source: SourceUpdate,
165
164
  server: "SyncServer" = Depends(get_letta_server),
166
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
165
+ headers: HeaderParams = Depends(get_headers),
167
166
  ):
168
167
  """
169
168
  Update the name or documentation of an existing data source.
170
169
  """
171
170
  # TODO: allow updating the handle/embedding config
172
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
171
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
173
172
  if not await server.source_manager.get_source_by_id(source_id=source_id, actor=actor):
174
173
  raise HTTPException(status_code=404, detail=f"Source with id={source_id} does not exist.")
175
174
  return await server.source_manager.update_source(source_id=source_id, source_update=source, actor=actor)
176
175
 
177
176
 
178
- @router.delete("/{source_id}", response_model=None, operation_id="delete_source")
177
+ @router.delete("/{source_id}", response_model=None, operation_id="delete_source", deprecated=True)
179
178
  async def delete_source(
180
179
  source_id: str,
181
180
  server: "SyncServer" = Depends(get_letta_server),
182
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
181
+ headers: HeaderParams = Depends(get_headers),
183
182
  ):
184
183
  """
185
184
  Delete a data source.
186
185
  """
187
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
186
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
188
187
  source = await server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
189
188
  agent_states = await server.source_manager.list_attached_agents(source_id=source_id, actor=actor)
190
189
  files = await server.file_manager.list_files(source_id, actor)
@@ -212,14 +211,14 @@ async def delete_source(
212
211
  await server.delete_source(source_id=source_id, actor=actor)
213
212
 
214
213
 
215
- @router.post("/{source_id}/upload", response_model=FileMetadata, operation_id="upload_file_to_source")
214
+ @router.post("/{source_id}/upload", response_model=FileMetadata, operation_id="upload_file_to_source", deprecated=True)
216
215
  async def upload_file_to_source(
217
216
  file: UploadFile,
218
217
  source_id: str,
219
218
  duplicate_handling: DuplicateFileHandling = Query(DuplicateFileHandling.SUFFIX, description="How to handle duplicate filenames"),
220
219
  name: Optional[str] = Query(None, description="Optional custom name to override the uploaded file's name"),
221
220
  server: "SyncServer" = Depends(get_letta_server),
222
- actor_id: Optional[str] = Header(None, alias="user_id"),
221
+ headers: HeaderParams = Depends(get_headers),
223
222
  ):
224
223
  """
225
224
  Upload a file to a data source.
@@ -256,7 +255,7 @@ async def upload_file_to_source(
256
255
  ),
257
256
  )
258
257
 
259
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
258
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
260
259
 
261
260
  source = await server.source_manager.get_source_by_id(source_id=source_id, actor=actor)
262
261
  if source is None:
@@ -330,32 +329,32 @@ async def upload_file_to_source(
330
329
  return file_metadata
331
330
 
332
331
 
333
- @router.get("/{source_id}/agents", response_model=List[str], operation_id="get_agents_for_source")
332
+ @router.get("/{source_id}/agents", response_model=List[str], operation_id="get_agents_for_source", deprecated=True)
334
333
  async def get_agents_for_source(
335
334
  source_id: str,
336
335
  server: SyncServer = Depends(get_letta_server),
337
- actor_id: Optional[str] = Header(None, alias="user_id"),
336
+ headers: HeaderParams = Depends(get_headers),
338
337
  ):
339
338
  """
340
339
  Get all agent IDs that have the specified source attached.
341
340
  """
342
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
341
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
343
342
  return await server.source_manager.get_agents_for_source_id(source_id=source_id, actor=actor)
344
343
 
345
344
 
346
- @router.get("/{source_id}/passages", response_model=List[Passage], operation_id="list_source_passages")
345
+ @router.get("/{source_id}/passages", response_model=List[Passage], operation_id="list_source_passages", deprecated=True)
347
346
  async def list_source_passages(
348
347
  source_id: str,
349
348
  after: Optional[str] = Query(None, description="Message after which to retrieve the returned messages."),
350
349
  before: Optional[str] = Query(None, description="Message before which to retrieve the returned messages."),
351
350
  limit: int = Query(100, description="Maximum number of messages to retrieve."),
352
351
  server: SyncServer = Depends(get_letta_server),
353
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
352
+ headers: HeaderParams = Depends(get_headers),
354
353
  ):
355
354
  """
356
355
  List all passages associated with a data source.
357
356
  """
358
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
357
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
359
358
  return await server.agent_manager.query_source_passages_async(
360
359
  actor=actor,
361
360
  source_id=source_id,
@@ -365,7 +364,7 @@ async def list_source_passages(
365
364
  )
366
365
 
367
366
 
368
- @router.get("/{source_id}/files", response_model=List[FileMetadata], operation_id="list_source_files")
367
+ @router.get("/{source_id}/files", response_model=List[FileMetadata], operation_id="list_source_files", deprecated=True)
369
368
  async def list_source_files(
370
369
  source_id: str,
371
370
  limit: int = Query(1000, description="Number of files to return"),
@@ -376,12 +375,12 @@ async def list_source_files(
376
375
  description="Whether to check and update file processing status (from the vector db service). If False, will not fetch and update the status, which may lead to performance gains.",
377
376
  ),
378
377
  server: "SyncServer" = Depends(get_letta_server),
379
- actor_id: Optional[str] = Header(None, alias="user_id"),
378
+ headers: HeaderParams = Depends(get_headers),
380
379
  ):
381
380
  """
382
381
  List paginated files associated with a data source.
383
382
  """
384
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
383
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
385
384
  return await server.file_manager.list_files(
386
385
  source_id=source_id,
387
386
  limit=limit,
@@ -393,18 +392,18 @@ async def list_source_files(
393
392
  )
394
393
 
395
394
 
396
- @router.get("/{source_id}/files/{file_id}", response_model=FileMetadata, operation_id="get_file_metadata")
395
+ @router.get("/{source_id}/files/{file_id}", response_model=FileMetadata, operation_id="get_file_metadata", deprecated=True)
397
396
  async def get_file_metadata(
398
397
  source_id: str,
399
398
  file_id: str,
400
399
  include_content: bool = Query(False, description="Whether to include full file content"),
401
400
  server: "SyncServer" = Depends(get_letta_server),
402
- actor_id: Optional[str] = Header(None, alias="user_id"),
401
+ headers: HeaderParams = Depends(get_headers),
403
402
  ):
404
403
  """
405
404
  Retrieve metadata for a specific file by its ID.
406
405
  """
407
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
406
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
408
407
 
409
408
  # Get file metadata using the file manager
410
409
  file_metadata = await server.file_manager.get_file_by_id(
@@ -426,17 +425,17 @@ async def get_file_metadata(
426
425
 
427
426
  # it's redundant to include /delete in the URL path. The HTTP verb DELETE already implies that action.
428
427
  # it's still good practice to return a status indicating the success or failure of the deletion
429
- @router.delete("/{source_id}/{file_id}", status_code=204, operation_id="delete_file_from_source")
428
+ @router.delete("/{source_id}/{file_id}", status_code=204, operation_id="delete_file_from_source", deprecated=True)
430
429
  async def delete_file_from_source(
431
430
  source_id: str,
432
431
  file_id: str,
433
432
  server: "SyncServer" = Depends(get_letta_server),
434
- actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
433
+ headers: HeaderParams = Depends(get_headers),
435
434
  ):
436
435
  """
437
436
  Delete a data source.
438
437
  """
439
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
438
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
440
439
 
441
440
  deleted_file = await server.file_manager.delete_file(file_id=file_id, actor=actor)
442
441
 
@@ -8,7 +8,7 @@ from letta.orm.errors import NoResultFound
8
8
  from letta.schemas.provider_trace import ProviderTrace
9
9
  from letta.schemas.step import Step
10
10
  from letta.schemas.step_metrics import StepMetrics
11
- from letta.server.rest_api.utils import get_letta_server
11
+ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
12
12
  from letta.server.server import SyncServer
13
13
  from letta.services.step_manager import FeedbackType
14
14
  from letta.settings import settings
@@ -21,7 +21,10 @@ async def list_steps(
21
21
  before: Optional[str] = Query(None, description="Return steps before this step ID"),
22
22
  after: Optional[str] = Query(None, description="Return steps after this step ID"),
23
23
  limit: Optional[int] = Query(50, description="Maximum number of steps to return"),
24
- order: Optional[str] = Query("desc", description="Sort order (asc or desc)"),
24
+ order: Literal["asc", "desc"] = Query(
25
+ "desc", description="Sort order for steps by creation time. 'asc' for oldest first, 'desc' for newest first"
26
+ ),
27
+ order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
25
28
  start_date: Optional[str] = Query(None, description='Return steps after this ISO datetime (e.g. "2025-01-29T15:01:19-08:00")'),
26
29
  end_date: Optional[str] = Query(None, description='Return steps before this ISO datetime (e.g. "2025-01-29T15:01:19-08:00")'),
27
30
  model: Optional[str] = Query(None, description="Filter by the name of the model used for the step"),
@@ -32,16 +35,15 @@ async def list_steps(
32
35
  tags: Optional[list[str]] = Query(None, description="Filter by tags"),
33
36
  project_id: Optional[str] = Query(None, description="Filter by the project ID that is associated with the step (cloud only)."),
34
37
  server: SyncServer = Depends(get_letta_server),
35
- actor_id: Optional[str] = Header(None, alias="user_id"),
38
+ headers: HeaderParams = Depends(get_headers),
36
39
  x_project: Optional[str] = Header(
37
40
  None, alias="X-Project", description="Filter by project slug to associate with the group (cloud only)."
38
41
  ), # Only handled by next js middleware
39
42
  ):
40
43
  """
41
44
  List steps with optional pagination and date filters.
42
- Dates should be provided in ISO 8601 format (e.g. 2025-01-29T15:01:19-08:00)
43
45
  """
44
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
46
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
45
47
 
46
48
  # Convert ISO strings to datetime objects if provided
47
49
  start_dt = datetime.fromisoformat(start_date) if start_date else None
@@ -54,7 +56,7 @@ async def list_steps(
54
56
  start_date=start_dt,
55
57
  end_date=end_dt,
56
58
  limit=limit,
57
- order=order,
59
+ order=(order == "asc"),
58
60
  model=model,
59
61
  agent_id=agent_id,
60
62
  trace_ids=trace_ids,
@@ -67,46 +69,46 @@ async def list_steps(
67
69
  @router.get("/{step_id}", response_model=Step, operation_id="retrieve_step")
68
70
  async def retrieve_step(
69
71
  step_id: str,
70
- actor_id: Optional[str] = Header(None, alias="user_id"),
72
+ headers: HeaderParams = Depends(get_headers),
71
73
  server: SyncServer = Depends(get_letta_server),
72
74
  ):
73
75
  """
74
76
  Get a step by ID.
75
77
  """
76
78
  try:
77
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
79
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
78
80
  return await server.step_manager.get_step_async(step_id=step_id, actor=actor)
79
81
  except NoResultFound:
80
82
  raise HTTPException(status_code=404, detail="Step not found")
81
83
 
82
84
 
83
- @router.get("/{step_id}/metrics", response_model=StepMetrics, operation_id="retrieve_step_metrics")
84
- async def retrieve_step_metrics(
85
+ @router.get("/{step_id}/metrics", response_model=StepMetrics, operation_id="retrieve_metrics_for_step")
86
+ async def retrieve_metrics_for_step(
85
87
  step_id: str,
86
- actor_id: Optional[str] = Header(None, alias="user_id"),
88
+ headers: HeaderParams = Depends(get_headers),
87
89
  server: SyncServer = Depends(get_letta_server),
88
90
  ):
89
91
  """
90
92
  Get step metrics by step ID.
91
93
  """
92
94
  try:
93
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
95
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
94
96
  return await server.step_manager.get_step_metrics_async(step_id=step_id, actor=actor)
95
97
  except NoResultFound:
96
98
  raise HTTPException(status_code=404, detail="Step metrics not found")
97
99
 
98
100
 
99
- @router.get("/{step_id}/trace", response_model=Optional[ProviderTrace], operation_id="retrieve_step_trace")
100
- async def retrieve_step_trace(
101
+ @router.get("/{step_id}/trace", response_model=Optional[ProviderTrace], operation_id="retrieve_trace_for_step")
102
+ async def retrieve_trace_for_step(
101
103
  step_id: str,
102
104
  server: SyncServer = Depends(get_letta_server),
103
- actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
105
+ headers: HeaderParams = Depends(get_headers),
104
106
  ):
105
107
  provider_trace = None
106
108
  if settings.track_provider_trace:
107
109
  try:
108
110
  provider_trace = await server.telemetry_manager.get_provider_trace_by_step_id_async(
109
- step_id=step_id, actor=await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
111
+ step_id=step_id, actor=await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
110
112
  )
111
113
  except:
112
114
  pass
@@ -114,23 +116,23 @@ async def retrieve_step_trace(
114
116
  return provider_trace
115
117
 
116
118
 
117
- class AddFeedbackRequest(BaseModel):
119
+ class ModifyFeedbackRequest(BaseModel):
118
120
  feedback: FeedbackType | None = Field(None, description="Whether this feedback is positive or negative")
119
121
  tags: list[str] | None = Field(None, description="Feedback tags to add to the step")
120
122
 
121
123
 
122
- @router.patch("/{step_id}/feedback", response_model=Step, operation_id="add_feedback")
123
- async def add_feedback(
124
+ @router.patch("/{step_id}/feedback", response_model=Step, operation_id="modify_feedback_for_step")
125
+ async def modify_feedback_for_step(
124
126
  step_id: str,
125
- request: AddFeedbackRequest = Body(...),
126
- actor_id: Optional[str] = Header(None, alias="user_id"),
127
+ request: ModifyFeedbackRequest = Body(...),
128
+ headers: HeaderParams = Depends(get_headers),
127
129
  server: SyncServer = Depends(get_letta_server),
128
130
  ):
129
131
  """
130
- Add feedback to a step.
132
+ Modify feedback for a given step.
131
133
  """
132
134
  try:
133
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
135
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
134
136
  return await server.step_manager.add_feedback_async(step_id=step_id, feedback=request.feedback, tags=request.tags, actor=actor)
135
137
  except NoResultFound:
136
138
  raise HTTPException(status_code=404, detail="Step not found")
@@ -140,13 +142,13 @@ async def add_feedback(
140
142
  async def update_step_transaction_id(
141
143
  step_id: str,
142
144
  transaction_id: str,
143
- actor_id: Optional[str] = Header(None, alias="user_id"),
145
+ headers: HeaderParams = Depends(get_headers),
144
146
  server: SyncServer = Depends(get_letta_server),
145
147
  ):
146
148
  """
147
149
  Update the transaction ID for a step.
148
150
  """
149
- actor = server.user_manager.get_user_or_default(user_id=actor_id)
151
+ actor = server.user_manager.get_user_or_default(user_id=headers.actor_id)
150
152
 
151
153
  try:
152
154
  return await server.step_manager.update_step_transaction_id(actor=actor, step_id=step_id, transaction_id=transaction_id)
@@ -1,8 +1,8 @@
1
1
  from typing import TYPE_CHECKING, List, Literal, Optional
2
2
 
3
- from fastapi import APIRouter, Depends, Header, Query
3
+ from fastapi import APIRouter, Depends, Query
4
4
 
5
- from letta.server.rest_api.utils import get_letta_server
5
+ from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from letta.server.server import SyncServer
@@ -24,15 +24,19 @@ async def list_tags(
24
24
  "asc", description="Sort order for tags. 'asc' for alphabetical order, 'desc' for reverse alphabetical order"
25
25
  ),
26
26
  order_by: Literal["name"] = Query("name", description="Field to sort by"),
27
- query_text: Optional[str] = Query(None, description="Filter tags by text search"),
27
+ query_text: Optional[str] = Query(
28
+ None, description="Filter tags by text search. Deprecated, please use name field instead", deprecated=True
29
+ ),
30
+ name: Optional[str] = Query(None, description="Filter tags by name"),
28
31
  server: "SyncServer" = Depends(get_letta_server),
29
- actor_id: Optional[str] = Header(None, alias="user_id"),
32
+ headers: HeaderParams = Depends(get_headers),
30
33
  ):
31
34
  """
32
- Get a list of all agent tags in the database.
35
+ Get the list of all agent tags that have been created.
33
36
  """
34
- actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
37
+ actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
38
+ text_filter = name or query_text
35
39
  tags = await server.agent_manager.list_tags_async(
36
- actor=actor, before=before, after=after, limit=limit, query_text=query_text, ascending=(order == "asc")
40
+ actor=actor, before=before, after=after, limit=limit, query_text=text_filter, ascending=(order == "asc")
37
41
  )
38
42
  return tags