letta-nightly 0.11.7.dev20250914103918__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.
- letta/functions/function_sets/multi_agent.py +1 -1
- letta/functions/helpers.py +1 -1
- letta/prompts/gpt_system.py +13 -15
- letta/prompts/system_prompts/__init__.py +27 -0
- letta/prompts/{system/memgpt_chat.txt → system_prompts/memgpt_chat.py} +2 -0
- letta/prompts/{system/memgpt_generate_tool.txt → system_prompts/memgpt_generate_tool.py} +4 -2
- letta/prompts/{system/memgpt_v2_chat.txt → system_prompts/memgpt_v2_chat.py} +2 -0
- letta/prompts/{system/react.txt → system_prompts/react.py} +2 -0
- letta/prompts/{system/sleeptime_doc_ingest.txt → system_prompts/sleeptime_doc_ingest.py} +2 -0
- letta/prompts/{system/sleeptime_v2.txt → system_prompts/sleeptime_v2.py} +2 -0
- letta/prompts/{system/summary_system_prompt.txt → system_prompts/summary_system_prompt.py} +2 -0
- letta/prompts/{system/voice_chat.txt → system_prompts/voice_chat.py} +2 -0
- letta/prompts/{system/voice_sleeptime.txt → system_prompts/voice_sleeptime.py} +2 -0
- letta/prompts/{system/workflow.txt → system_prompts/workflow.py} +2 -0
- letta/server/rest_api/dependencies.py +37 -0
- letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +4 -3
- letta/server/rest_api/routers/v1/agents.py +112 -109
- letta/server/rest_api/routers/v1/blocks.py +44 -20
- letta/server/rest_api/routers/v1/embeddings.py +3 -3
- letta/server/rest_api/routers/v1/folders.py +107 -47
- letta/server/rest_api/routers/v1/groups.py +52 -32
- letta/server/rest_api/routers/v1/health.py +2 -2
- letta/server/rest_api/routers/v1/identities.py +110 -21
- letta/server/rest_api/routers/v1/internal_templates.py +28 -13
- letta/server/rest_api/routers/v1/jobs.py +12 -12
- letta/server/rest_api/routers/v1/llms.py +6 -8
- letta/server/rest_api/routers/v1/messages.py +53 -36
- letta/server/rest_api/routers/v1/organizations.py +1 -1
- letta/server/rest_api/routers/v1/providers.py +47 -20
- letta/server/rest_api/routers/v1/runs.py +19 -19
- letta/server/rest_api/routers/v1/sandbox_configs.py +25 -25
- letta/server/rest_api/routers/v1/sources.py +44 -45
- letta/server/rest_api/routers/v1/steps.py +50 -22
- letta/server/rest_api/routers/v1/tags.py +25 -10
- letta/server/rest_api/routers/v1/telemetry.py +11 -6
- letta/server/rest_api/routers/v1/tools.py +71 -54
- letta/server/rest_api/routers/v1/users.py +1 -1
- letta/server/rest_api/routers/v1/voice.py +6 -5
- letta/server/rest_api/utils.py +1 -18
- letta/services/agent_manager.py +31 -7
- letta/services/file_manager.py +6 -0
- letta/services/group_manager.py +2 -1
- letta/services/identity_manager.py +67 -0
- letta/services/provider_manager.py +14 -1
- letta/services/source_manager.py +11 -1
- letta/services/step_manager.py +5 -1
- letta/services/tool_manager.py +46 -9
- letta/utils.py +6 -2
- {letta_nightly-0.11.7.dev20250914103918.dist-info → letta_nightly-0.11.7.dev20250916104104.dist-info}/METADATA +1 -1
- {letta_nightly-0.11.7.dev20250914103918.dist-info → letta_nightly-0.11.7.dev20250916104104.dist-info}/RECORD +53 -65
- letta/prompts/system/memgpt_base.txt +0 -54
- letta/prompts/system/memgpt_chat_compressed.txt +0 -13
- letta/prompts/system/memgpt_chat_fstring.txt +0 -51
- letta/prompts/system/memgpt_convo_only.txt +0 -12
- letta/prompts/system/memgpt_doc.txt +0 -50
- letta/prompts/system/memgpt_gpt35_extralong.txt +0 -53
- letta/prompts/system/memgpt_intuitive_knowledge.txt +0 -31
- letta/prompts/system/memgpt_memory_only.txt +0 -29
- letta/prompts/system/memgpt_modified_chat.txt +0 -23
- letta/prompts/system/memgpt_modified_o1.txt +0 -31
- letta/prompts/system/memgpt_offline_memory.txt +0 -23
- letta/prompts/system/memgpt_offline_memory_chat.txt +0 -35
- letta/prompts/system/memgpt_sleeptime_chat.txt +0 -52
- letta/prompts/system/sleeptime.txt +0 -37
- {letta_nightly-0.11.7.dev20250914103918.dist-info → letta_nightly-0.11.7.dev20250916104104.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20250914103918.dist-info → letta_nightly-0.11.7.dev20250916104104.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20250914103918.dist-info → letta_nightly-0.11.7.dev20250916104104.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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -1,14 +1,17 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
from typing import List, Literal, Optional
|
3
3
|
|
4
|
-
from fastapi import APIRouter, Depends, Header, HTTPException, Query
|
4
|
+
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query
|
5
|
+
from pydantic import BaseModel, Field
|
5
6
|
|
6
7
|
from letta.orm.errors import NoResultFound
|
8
|
+
from letta.schemas.provider_trace import ProviderTrace
|
7
9
|
from letta.schemas.step import Step
|
8
10
|
from letta.schemas.step_metrics import StepMetrics
|
9
|
-
from letta.server.rest_api.
|
11
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
10
12
|
from letta.server.server import SyncServer
|
11
13
|
from letta.services.step_manager import FeedbackType
|
14
|
+
from letta.settings import settings
|
12
15
|
|
13
16
|
router = APIRouter(prefix="/steps", tags=["steps"])
|
14
17
|
|
@@ -18,7 +21,10 @@ async def list_steps(
|
|
18
21
|
before: Optional[str] = Query(None, description="Return steps before this step ID"),
|
19
22
|
after: Optional[str] = Query(None, description="Return steps after this step ID"),
|
20
23
|
limit: Optional[int] = Query(50, description="Maximum number of steps to return"),
|
21
|
-
order:
|
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"),
|
22
28
|
start_date: Optional[str] = Query(None, description='Return steps after this ISO datetime (e.g. "2025-01-29T15:01:19-08:00")'),
|
23
29
|
end_date: Optional[str] = Query(None, description='Return steps before this ISO datetime (e.g. "2025-01-29T15:01:19-08:00")'),
|
24
30
|
model: Optional[str] = Query(None, description="Filter by the name of the model used for the step"),
|
@@ -29,16 +35,15 @@ async def list_steps(
|
|
29
35
|
tags: Optional[list[str]] = Query(None, description="Filter by tags"),
|
30
36
|
project_id: Optional[str] = Query(None, description="Filter by the project ID that is associated with the step (cloud only)."),
|
31
37
|
server: SyncServer = Depends(get_letta_server),
|
32
|
-
|
38
|
+
headers: HeaderParams = Depends(get_headers),
|
33
39
|
x_project: Optional[str] = Header(
|
34
40
|
None, alias="X-Project", description="Filter by project slug to associate with the group (cloud only)."
|
35
41
|
), # Only handled by next js middleware
|
36
42
|
):
|
37
43
|
"""
|
38
44
|
List steps with optional pagination and date filters.
|
39
|
-
Dates should be provided in ISO 8601 format (e.g. 2025-01-29T15:01:19-08:00)
|
40
45
|
"""
|
41
|
-
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)
|
42
47
|
|
43
48
|
# Convert ISO strings to datetime objects if provided
|
44
49
|
start_dt = datetime.fromisoformat(start_date) if start_date else None
|
@@ -51,7 +56,7 @@ async def list_steps(
|
|
51
56
|
start_date=start_dt,
|
52
57
|
end_date=end_dt,
|
53
58
|
limit=limit,
|
54
|
-
order=order,
|
59
|
+
order=(order == "asc"),
|
55
60
|
model=model,
|
56
61
|
agent_id=agent_id,
|
57
62
|
trace_ids=trace_ids,
|
@@ -64,48 +69,71 @@ async def list_steps(
|
|
64
69
|
@router.get("/{step_id}", response_model=Step, operation_id="retrieve_step")
|
65
70
|
async def retrieve_step(
|
66
71
|
step_id: str,
|
67
|
-
|
72
|
+
headers: HeaderParams = Depends(get_headers),
|
68
73
|
server: SyncServer = Depends(get_letta_server),
|
69
74
|
):
|
70
75
|
"""
|
71
76
|
Get a step by ID.
|
72
77
|
"""
|
73
78
|
try:
|
74
|
-
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)
|
75
80
|
return await server.step_manager.get_step_async(step_id=step_id, actor=actor)
|
76
81
|
except NoResultFound:
|
77
82
|
raise HTTPException(status_code=404, detail="Step not found")
|
78
83
|
|
79
84
|
|
80
|
-
@router.get("/{step_id}/metrics", response_model=StepMetrics, operation_id="
|
81
|
-
async def
|
85
|
+
@router.get("/{step_id}/metrics", response_model=StepMetrics, operation_id="retrieve_metrics_for_step")
|
86
|
+
async def retrieve_metrics_for_step(
|
82
87
|
step_id: str,
|
83
|
-
|
88
|
+
headers: HeaderParams = Depends(get_headers),
|
84
89
|
server: SyncServer = Depends(get_letta_server),
|
85
90
|
):
|
86
91
|
"""
|
87
92
|
Get step metrics by step ID.
|
88
93
|
"""
|
89
94
|
try:
|
90
|
-
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)
|
91
96
|
return await server.step_manager.get_step_metrics_async(step_id=step_id, actor=actor)
|
92
97
|
except NoResultFound:
|
93
98
|
raise HTTPException(status_code=404, detail="Step metrics not found")
|
94
99
|
|
95
100
|
|
96
|
-
@router.
|
97
|
-
async def
|
101
|
+
@router.get("/{step_id}/trace", response_model=Optional[ProviderTrace], operation_id="retrieve_trace_for_step")
|
102
|
+
async def retrieve_trace_for_step(
|
98
103
|
step_id: str,
|
99
|
-
|
100
|
-
|
104
|
+
server: SyncServer = Depends(get_letta_server),
|
105
|
+
headers: HeaderParams = Depends(get_headers),
|
106
|
+
):
|
107
|
+
provider_trace = None
|
108
|
+
if settings.track_provider_trace:
|
109
|
+
try:
|
110
|
+
provider_trace = await server.telemetry_manager.get_provider_trace_by_step_id_async(
|
111
|
+
step_id=step_id, actor=await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
112
|
+
)
|
113
|
+
except:
|
114
|
+
pass
|
115
|
+
|
116
|
+
return provider_trace
|
117
|
+
|
118
|
+
|
119
|
+
class ModifyFeedbackRequest(BaseModel):
|
120
|
+
feedback: FeedbackType | None = Field(None, description="Whether this feedback is positive or negative")
|
121
|
+
tags: list[str] | None = Field(None, description="Feedback tags to add to the step")
|
122
|
+
|
123
|
+
|
124
|
+
@router.patch("/{step_id}/feedback", response_model=Step, operation_id="modify_feedback_for_step")
|
125
|
+
async def modify_feedback_for_step(
|
126
|
+
step_id: str,
|
127
|
+
request: ModifyFeedbackRequest = Body(...),
|
128
|
+
headers: HeaderParams = Depends(get_headers),
|
101
129
|
server: SyncServer = Depends(get_letta_server),
|
102
130
|
):
|
103
131
|
"""
|
104
|
-
|
132
|
+
Modify feedback for a given step.
|
105
133
|
"""
|
106
134
|
try:
|
107
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
108
|
-
return await server.step_manager.add_feedback_async(step_id=step_id, feedback=feedback, actor=actor)
|
135
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
136
|
+
return await server.step_manager.add_feedback_async(step_id=step_id, feedback=request.feedback, tags=request.tags, actor=actor)
|
109
137
|
except NoResultFound:
|
110
138
|
raise HTTPException(status_code=404, detail="Step not found")
|
111
139
|
|
@@ -114,13 +142,13 @@ async def add_feedback(
|
|
114
142
|
async def update_step_transaction_id(
|
115
143
|
step_id: str,
|
116
144
|
transaction_id: str,
|
117
|
-
|
145
|
+
headers: HeaderParams = Depends(get_headers),
|
118
146
|
server: SyncServer = Depends(get_letta_server),
|
119
147
|
):
|
120
148
|
"""
|
121
149
|
Update the transaction ID for a step.
|
122
150
|
"""
|
123
|
-
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)
|
124
152
|
|
125
153
|
try:
|
126
154
|
return await server.step_manager.update_step_transaction_id(actor=actor, step_id=step_id, transaction_id=transaction_id)
|