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
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Annotated, List, Optional
|
1
|
+
from typing import Annotated, List, Literal, Optional
|
2
2
|
|
3
3
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, status
|
4
4
|
from fastapi.responses import JSONResponse
|
@@ -10,7 +10,7 @@ from letta.schemas.group import Group, GroupCreate, GroupUpdate, ManagerType
|
|
10
10
|
from letta.schemas.letta_message import LettaMessageUnion, LettaMessageUpdateUnion
|
11
11
|
from letta.schemas.letta_request import LettaRequest, LettaStreamingRequest
|
12
12
|
from letta.schemas.letta_response import LettaResponse
|
13
|
-
from letta.server.rest_api.
|
13
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
14
14
|
from letta.server.server import SyncServer
|
15
15
|
|
16
16
|
router = APIRouter(prefix="/groups", tags=["groups"])
|
@@ -19,11 +19,19 @@ router = APIRouter(prefix="/groups", tags=["groups"])
|
|
19
19
|
@router.get("/", response_model=List[Group], operation_id="list_groups")
|
20
20
|
async def list_groups(
|
21
21
|
server: "SyncServer" = Depends(get_letta_server),
|
22
|
-
|
22
|
+
headers: HeaderParams = Depends(get_headers),
|
23
23
|
manager_type: Optional[ManagerType] = Query(None, description="Search groups by manager type"),
|
24
|
-
before: Optional[str] = Query(
|
25
|
-
|
26
|
-
|
24
|
+
before: Optional[str] = Query(
|
25
|
+
None, description="Group ID cursor for pagination. Returns groups that come before this group ID in the specified sort order"
|
26
|
+
),
|
27
|
+
after: Optional[str] = Query(
|
28
|
+
None, description="Group ID cursor for pagination. Returns groups that come after this group ID in the specified sort order"
|
29
|
+
),
|
30
|
+
limit: Optional[int] = Query(50, description="Maximum number of groups to return"),
|
31
|
+
order: Literal["asc", "desc"] = Query(
|
32
|
+
"asc", description="Sort order for groups by creation time. 'asc' for oldest first, 'desc' for newest first"
|
33
|
+
),
|
34
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
27
35
|
project_id: Optional[str] = Query(None, description="Search groups by project id"),
|
28
36
|
show_hidden_groups: bool | None = Query(
|
29
37
|
False,
|
@@ -34,7 +42,7 @@ async def list_groups(
|
|
34
42
|
"""
|
35
43
|
Fetch all multi-agent groups matching query.
|
36
44
|
"""
|
37
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
45
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
38
46
|
return await server.group_manager.list_groups_async(
|
39
47
|
actor=actor,
|
40
48
|
project_id=project_id,
|
@@ -42,6 +50,7 @@ async def list_groups(
|
|
42
50
|
before=before,
|
43
51
|
after=after,
|
44
52
|
limit=limit,
|
53
|
+
ascending=(order == "asc"),
|
45
54
|
show_hidden_groups=show_hidden_groups,
|
46
55
|
)
|
47
56
|
|
@@ -49,12 +58,12 @@ async def list_groups(
|
|
49
58
|
@router.get("/count", response_model=int, operation_id="count_groups")
|
50
59
|
async def count_groups(
|
51
60
|
server: SyncServer = Depends(get_letta_server),
|
52
|
-
|
61
|
+
headers: HeaderParams = Depends(get_headers),
|
53
62
|
):
|
54
63
|
"""
|
55
64
|
Get the count of all groups associated with a given user.
|
56
65
|
"""
|
57
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
66
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
58
67
|
return await server.group_manager.size(actor=actor)
|
59
68
|
|
60
69
|
|
@@ -62,12 +71,12 @@ async def count_groups(
|
|
62
71
|
async def retrieve_group(
|
63
72
|
group_id: str,
|
64
73
|
server: "SyncServer" = Depends(get_letta_server),
|
65
|
-
|
74
|
+
headers: HeaderParams = Depends(get_headers),
|
66
75
|
):
|
67
76
|
"""
|
68
77
|
Retrieve the group by id.
|
69
78
|
"""
|
70
|
-
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)
|
71
80
|
|
72
81
|
try:
|
73
82
|
return await server.group_manager.retrieve_group_async(group_id=group_id, actor=actor)
|
@@ -79,7 +88,7 @@ async def retrieve_group(
|
|
79
88
|
async def create_group(
|
80
89
|
group: GroupCreate = Body(...),
|
81
90
|
server: "SyncServer" = Depends(get_letta_server),
|
82
|
-
|
91
|
+
headers: HeaderParams = Depends(get_headers),
|
83
92
|
x_project: Optional[str] = Header(
|
84
93
|
None, alias="X-Project", description="The project slug to associate with the group (cloud only)."
|
85
94
|
), # Only handled by next js middleware
|
@@ -88,7 +97,7 @@ async def create_group(
|
|
88
97
|
Create a new multi-agent group with the specified configuration.
|
89
98
|
"""
|
90
99
|
try:
|
91
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
100
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
92
101
|
return await server.group_manager.create_group_async(group, actor=actor)
|
93
102
|
except Exception as e:
|
94
103
|
raise HTTPException(status_code=500, detail=str(e))
|
@@ -99,7 +108,7 @@ async def modify_group(
|
|
99
108
|
group_id: str,
|
100
109
|
group: GroupUpdate = Body(...),
|
101
110
|
server: "SyncServer" = Depends(get_letta_server),
|
102
|
-
|
111
|
+
headers: HeaderParams = Depends(get_headers),
|
103
112
|
x_project: Optional[str] = Header(
|
104
113
|
None, alias="X-Project", description="The project slug to associate with the group (cloud only)."
|
105
114
|
), # Only handled by next js middleware
|
@@ -108,7 +117,7 @@ async def modify_group(
|
|
108
117
|
Create a new multi-agent group with the specified configuration.
|
109
118
|
"""
|
110
119
|
try:
|
111
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
120
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
112
121
|
return await server.group_manager.modify_group_async(group_id=group_id, group_update=group, actor=actor)
|
113
122
|
except Exception as e:
|
114
123
|
raise HTTPException(status_code=500, detail=str(e))
|
@@ -118,12 +127,12 @@ async def modify_group(
|
|
118
127
|
async def delete_group(
|
119
128
|
group_id: str,
|
120
129
|
server: "SyncServer" = Depends(get_letta_server),
|
121
|
-
|
130
|
+
headers: HeaderParams = Depends(get_headers),
|
122
131
|
):
|
123
132
|
"""
|
124
133
|
Delete a multi-agent group.
|
125
134
|
"""
|
126
|
-
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)
|
127
136
|
try:
|
128
137
|
await server.group_manager.delete_group_async(group_id=group_id, actor=actor)
|
129
138
|
return JSONResponse(status_code=status.HTTP_200_OK, content={"message": f"Group id={group_id} successfully deleted"})
|
@@ -140,13 +149,13 @@ async def send_group_message(
|
|
140
149
|
group_id: str,
|
141
150
|
server: SyncServer = Depends(get_letta_server),
|
142
151
|
request: LettaRequest = Body(...),
|
143
|
-
|
152
|
+
headers: HeaderParams = Depends(get_headers),
|
144
153
|
):
|
145
154
|
"""
|
146
155
|
Process a user message and return the group's response.
|
147
156
|
This endpoint accepts a message from a user and processes it through through agents in the group based on the specified pattern
|
148
157
|
"""
|
149
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
158
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
150
159
|
result = await server.send_group_message_to_agent(
|
151
160
|
group_id=group_id,
|
152
161
|
actor=actor,
|
@@ -178,14 +187,14 @@ async def send_group_message_streaming(
|
|
178
187
|
group_id: str,
|
179
188
|
server: SyncServer = Depends(get_letta_server),
|
180
189
|
request: LettaStreamingRequest = Body(...),
|
181
|
-
|
190
|
+
headers: HeaderParams = Depends(get_headers),
|
182
191
|
):
|
183
192
|
"""
|
184
193
|
Process a user message and return the group's responses.
|
185
194
|
This endpoint accepts a message from a user and processes it through agents in the group based on the specified pattern.
|
186
195
|
It will stream the steps of the response always, and stream the tokens if 'stream_tokens' is set to True.
|
187
196
|
"""
|
188
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
197
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
189
198
|
result = await server.send_group_message_to_agent(
|
190
199
|
group_id=group_id,
|
191
200
|
actor=actor,
|
@@ -211,32 +220,42 @@ async def modify_group_message(
|
|
211
220
|
message_id: str,
|
212
221
|
request: LettaMessageUpdateUnion = Body(...),
|
213
222
|
server: "SyncServer" = Depends(get_letta_server),
|
214
|
-
|
223
|
+
headers: HeaderParams = Depends(get_headers),
|
215
224
|
):
|
216
225
|
"""
|
217
226
|
Update the details of a message associated with an agent.
|
218
227
|
"""
|
219
228
|
# TODO: support modifying tool calls/returns
|
220
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
229
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
221
230
|
return await server.message_manager.update_message_by_letta_message(message_id=message_id, letta_message_update=request, actor=actor)
|
222
231
|
|
223
232
|
|
224
233
|
@router.get("/{group_id}/messages", response_model=GroupMessagesResponse, operation_id="list_group_messages")
|
225
234
|
async def list_group_messages(
|
226
235
|
group_id: str,
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
236
|
+
before: Optional[str] = Query(
|
237
|
+
None,
|
238
|
+
description="Message ID cursor for pagination. Returns messages that come before this message ID in the specified sort order",
|
239
|
+
),
|
240
|
+
after: Optional[str] = Query(
|
241
|
+
None,
|
242
|
+
description="Message ID cursor for pagination. Returns messages that come after this message ID in the specified sort order",
|
243
|
+
),
|
244
|
+
limit: Optional[int] = Query(10, description="Maximum number of messages to retrieve"),
|
245
|
+
order: Literal["asc", "desc"] = Query(
|
246
|
+
"desc", description="Sort order for messages by creation time. 'asc' for oldest first, 'desc' for newest first"
|
247
|
+
),
|
248
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
231
249
|
use_assistant_message: bool = Query(True, description="Whether to use assistant messages"),
|
232
250
|
assistant_message_tool_name: str = Query(DEFAULT_MESSAGE_TOOL, description="The name of the designated message tool."),
|
233
251
|
assistant_message_tool_kwarg: str = Query(DEFAULT_MESSAGE_TOOL_KWARG, description="The name of the message argument."),
|
234
|
-
|
252
|
+
server: "SyncServer" = Depends(get_letta_server),
|
253
|
+
headers: HeaderParams = Depends(get_headers),
|
235
254
|
):
|
236
255
|
"""
|
237
256
|
Retrieve message history for an agent.
|
238
257
|
"""
|
239
|
-
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)
|
240
259
|
group = await server.group_manager.retrieve_group_async(group_id=group_id, actor=actor)
|
241
260
|
if group.manager_agent_id:
|
242
261
|
return await server.get_agent_recall_async(
|
@@ -246,7 +265,7 @@ async def list_group_messages(
|
|
246
265
|
before=before,
|
247
266
|
limit=limit,
|
248
267
|
group_id=group_id,
|
249
|
-
reverse=
|
268
|
+
reverse=(order == "desc"),
|
250
269
|
return_message_object=False,
|
251
270
|
use_assistant_message=use_assistant_message,
|
252
271
|
assistant_message_tool_name=assistant_message_tool_name,
|
@@ -258,6 +277,7 @@ async def list_group_messages(
|
|
258
277
|
after=after,
|
259
278
|
before=before,
|
260
279
|
limit=limit,
|
280
|
+
ascending=(order == "asc"),
|
261
281
|
actor=actor,
|
262
282
|
use_assistant_message=use_assistant_message,
|
263
283
|
assistant_message_tool_name=assistant_message_tool_name,
|
@@ -269,10 +289,10 @@ async def list_group_messages(
|
|
269
289
|
async def reset_group_messages(
|
270
290
|
group_id: str,
|
271
291
|
server: "SyncServer" = Depends(get_letta_server),
|
272
|
-
|
292
|
+
headers: HeaderParams = Depends(get_headers),
|
273
293
|
):
|
274
294
|
"""
|
275
295
|
Delete the group messages for all agents that are part of the multi-agent group.
|
276
296
|
"""
|
277
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
297
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
278
298
|
await server.group_manager.reset_messages_async(group_id=group_id, actor=actor)
|
@@ -12,8 +12,8 @@ router = APIRouter(prefix="/health", tags=["health"])
|
|
12
12
|
|
13
13
|
|
14
14
|
# Health check
|
15
|
-
@router.get("/", response_model=Health, operation_id="
|
16
|
-
def
|
15
|
+
@router.get("/", response_model=Health, operation_id="check_health")
|
16
|
+
def check_health():
|
17
17
|
return Health(
|
18
18
|
version=__version__,
|
19
19
|
status="ok",
|
@@ -1,10 +1,12 @@
|
|
1
|
-
from typing import TYPE_CHECKING, List, Optional
|
1
|
+
from typing import TYPE_CHECKING, List, Literal, Optional
|
2
2
|
|
3
3
|
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query
|
4
4
|
|
5
5
|
from letta.orm.errors import NoResultFound, UniqueConstraintViolationError
|
6
|
+
from letta.schemas.agent import AgentState
|
7
|
+
from letta.schemas.block import Block
|
6
8
|
from letta.schemas.identity import Identity, IdentityCreate, IdentityProperty, IdentityType, IdentityUpdate, IdentityUpsert
|
7
|
-
from letta.server.rest_api.
|
9
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
8
10
|
|
9
11
|
if TYPE_CHECKING:
|
10
12
|
from letta.server.server import SyncServer
|
@@ -18,17 +20,27 @@ async def list_identities(
|
|
18
20
|
project_id: Optional[str] = Query(None),
|
19
21
|
identifier_key: Optional[str] = Query(None),
|
20
22
|
identity_type: Optional[IdentityType] = Query(None),
|
21
|
-
before: Optional[str] = Query(
|
22
|
-
|
23
|
-
|
23
|
+
before: Optional[str] = Query(
|
24
|
+
None,
|
25
|
+
description="Identity ID cursor for pagination. Returns identities that come before this identity ID in the specified sort order",
|
26
|
+
),
|
27
|
+
after: Optional[str] = Query(
|
28
|
+
None,
|
29
|
+
description="Identity ID cursor for pagination. Returns identities that come after this identity ID in the specified sort order",
|
30
|
+
),
|
31
|
+
limit: Optional[int] = Query(50, description="Maximum number of identities to return"),
|
32
|
+
order: Literal["asc", "desc"] = Query(
|
33
|
+
"desc", description="Sort order for identities by creation time. 'asc' for oldest first, 'desc' for newest first"
|
34
|
+
),
|
35
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
24
36
|
server: "SyncServer" = Depends(get_letta_server),
|
25
|
-
|
37
|
+
headers: HeaderParams = Depends(get_headers),
|
26
38
|
):
|
27
39
|
"""
|
28
40
|
Get a list of all identities in the database
|
29
41
|
"""
|
30
42
|
try:
|
31
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
43
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
32
44
|
|
33
45
|
identities = await server.identity_manager.list_identities_async(
|
34
46
|
name=name,
|
@@ -38,6 +50,7 @@ async def list_identities(
|
|
38
50
|
before=before,
|
39
51
|
after=after,
|
40
52
|
limit=limit,
|
53
|
+
ascending=(order == "asc"),
|
41
54
|
actor=actor,
|
42
55
|
)
|
43
56
|
except HTTPException:
|
@@ -52,13 +65,13 @@ async def list_identities(
|
|
52
65
|
@router.get("/count", tags=["identities"], response_model=int, operation_id="count_identities")
|
53
66
|
async def count_identities(
|
54
67
|
server: "SyncServer" = Depends(get_letta_server),
|
55
|
-
|
68
|
+
headers: HeaderParams = Depends(get_headers),
|
56
69
|
):
|
57
70
|
"""
|
58
71
|
Get count of all identities for a user
|
59
72
|
"""
|
60
73
|
try:
|
61
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
74
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
62
75
|
return await server.identity_manager.size_async(actor=actor)
|
63
76
|
except NoResultFound:
|
64
77
|
return 0
|
@@ -72,10 +85,10 @@ async def count_identities(
|
|
72
85
|
async def retrieve_identity(
|
73
86
|
identity_id: str,
|
74
87
|
server: "SyncServer" = Depends(get_letta_server),
|
75
|
-
|
88
|
+
headers: HeaderParams = Depends(get_headers),
|
76
89
|
):
|
77
90
|
try:
|
78
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
91
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
79
92
|
return await server.identity_manager.get_identity_async(identity_id=identity_id, actor=actor)
|
80
93
|
except NoResultFound as e:
|
81
94
|
raise HTTPException(status_code=404, detail=str(e))
|
@@ -85,13 +98,13 @@ async def retrieve_identity(
|
|
85
98
|
async def create_identity(
|
86
99
|
identity: IdentityCreate = Body(...),
|
87
100
|
server: "SyncServer" = Depends(get_letta_server),
|
88
|
-
|
101
|
+
headers: HeaderParams = Depends(get_headers),
|
89
102
|
x_project: Optional[str] = Header(
|
90
103
|
None, alias="X-Project", description="The project slug to associate with the identity (cloud only)."
|
91
104
|
), # Only handled by next js middleware
|
92
105
|
):
|
93
106
|
try:
|
94
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
107
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
95
108
|
return await server.identity_manager.create_identity_async(identity=identity, actor=actor)
|
96
109
|
except HTTPException:
|
97
110
|
raise
|
@@ -111,13 +124,13 @@ async def create_identity(
|
|
111
124
|
async def upsert_identity(
|
112
125
|
identity: IdentityUpsert = Body(...),
|
113
126
|
server: "SyncServer" = Depends(get_letta_server),
|
114
|
-
|
127
|
+
headers: HeaderParams = Depends(get_headers),
|
115
128
|
x_project: Optional[str] = Header(
|
116
129
|
None, alias="X-Project", description="The project slug to associate with the identity (cloud only)."
|
117
130
|
), # Only handled by next js middleware
|
118
131
|
):
|
119
132
|
try:
|
120
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
133
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
121
134
|
return await server.identity_manager.upsert_identity_async(identity=identity, actor=actor)
|
122
135
|
except HTTPException:
|
123
136
|
raise
|
@@ -132,10 +145,10 @@ async def modify_identity(
|
|
132
145
|
identity_id: str,
|
133
146
|
identity: IdentityUpdate = Body(...),
|
134
147
|
server: "SyncServer" = Depends(get_letta_server),
|
135
|
-
|
148
|
+
headers: HeaderParams = Depends(get_headers),
|
136
149
|
):
|
137
150
|
try:
|
138
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
151
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
139
152
|
return await server.identity_manager.update_identity_async(identity_id=identity_id, identity=identity, actor=actor)
|
140
153
|
except HTTPException:
|
141
154
|
raise
|
@@ -150,10 +163,10 @@ async def upsert_identity_properties(
|
|
150
163
|
identity_id: str,
|
151
164
|
properties: List[IdentityProperty] = Body(...),
|
152
165
|
server: "SyncServer" = Depends(get_letta_server),
|
153
|
-
|
166
|
+
headers: HeaderParams = Depends(get_headers),
|
154
167
|
):
|
155
168
|
try:
|
156
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
169
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
157
170
|
return await server.identity_manager.upsert_identity_properties_async(identity_id=identity_id, properties=properties, actor=actor)
|
158
171
|
except HTTPException:
|
159
172
|
raise
|
@@ -167,13 +180,13 @@ async def upsert_identity_properties(
|
|
167
180
|
async def delete_identity(
|
168
181
|
identity_id: str,
|
169
182
|
server: "SyncServer" = Depends(get_letta_server),
|
170
|
-
|
183
|
+
headers: HeaderParams = Depends(get_headers),
|
171
184
|
):
|
172
185
|
"""
|
173
186
|
Delete an identity by its identifier key
|
174
187
|
"""
|
175
188
|
try:
|
176
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
189
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
177
190
|
await server.identity_manager.delete_identity_async(identity_id=identity_id, actor=actor)
|
178
191
|
except HTTPException:
|
179
192
|
raise
|
@@ -181,3 +194,79 @@ async def delete_identity(
|
|
181
194
|
raise HTTPException(status_code=404, detail=str(e))
|
182
195
|
except Exception as e:
|
183
196
|
raise HTTPException(status_code=500, detail=f"{e}")
|
197
|
+
|
198
|
+
|
199
|
+
@router.get("/{identity_id}/agents", response_model=List[AgentState], operation_id="list_agents_for_identity")
|
200
|
+
async def list_agents_for_identity(
|
201
|
+
identity_id: str,
|
202
|
+
before: Optional[str] = Query(
|
203
|
+
None,
|
204
|
+
description="Agent ID cursor for pagination. Returns agents that come before this agent ID in the specified sort order",
|
205
|
+
),
|
206
|
+
after: Optional[str] = Query(
|
207
|
+
None,
|
208
|
+
description="Agent ID cursor for pagination. Returns agents that come after this agent ID in the specified sort order",
|
209
|
+
),
|
210
|
+
limit: Optional[int] = Query(50, description="Maximum number of agents to return"),
|
211
|
+
order: Literal["asc", "desc"] = Query(
|
212
|
+
"desc", description="Sort order for agents by creation time. 'asc' for oldest first, 'desc' for newest first"
|
213
|
+
),
|
214
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
215
|
+
server: "SyncServer" = Depends(get_letta_server),
|
216
|
+
headers: HeaderParams = Depends(get_headers),
|
217
|
+
):
|
218
|
+
"""
|
219
|
+
Get all agents associated with the specified identity.
|
220
|
+
"""
|
221
|
+
try:
|
222
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
223
|
+
return await server.identity_manager.list_agents_for_identity_async(
|
224
|
+
identity_id=identity_id,
|
225
|
+
before=before,
|
226
|
+
after=after,
|
227
|
+
limit=limit,
|
228
|
+
ascending=(order == "asc"),
|
229
|
+
actor=actor,
|
230
|
+
)
|
231
|
+
except NoResultFound as e:
|
232
|
+
raise HTTPException(status_code=404, detail=f"Identity with id={identity_id} not found")
|
233
|
+
except Exception as e:
|
234
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
235
|
+
|
236
|
+
|
237
|
+
@router.get("/{identity_id}/blocks", response_model=List[Block], operation_id="list_blocks_for_identity")
|
238
|
+
async def list_blocks_for_identity(
|
239
|
+
identity_id: str,
|
240
|
+
before: Optional[str] = Query(
|
241
|
+
None,
|
242
|
+
description="Block ID cursor for pagination. Returns blocks that come before this block ID in the specified sort order",
|
243
|
+
),
|
244
|
+
after: Optional[str] = Query(
|
245
|
+
None,
|
246
|
+
description="Block ID cursor for pagination. Returns blocks that come after this block ID in the specified sort order",
|
247
|
+
),
|
248
|
+
limit: Optional[int] = Query(50, description="Maximum number of blocks to return"),
|
249
|
+
order: Literal["asc", "desc"] = Query(
|
250
|
+
"desc", description="Sort order for blocks by creation time. 'asc' for oldest first, 'desc' for newest first"
|
251
|
+
),
|
252
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
253
|
+
server: "SyncServer" = Depends(get_letta_server),
|
254
|
+
headers: HeaderParams = Depends(get_headers),
|
255
|
+
):
|
256
|
+
"""
|
257
|
+
Get all blocks associated with the specified identity.
|
258
|
+
"""
|
259
|
+
try:
|
260
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
261
|
+
return await server.identity_manager.list_blocks_for_identity_async(
|
262
|
+
identity_id=identity_id,
|
263
|
+
before=before,
|
264
|
+
after=after,
|
265
|
+
limit=limit,
|
266
|
+
ascending=(order == "asc"),
|
267
|
+
actor=actor,
|
268
|
+
)
|
269
|
+
except NoResultFound as e:
|
270
|
+
raise HTTPException(status_code=404, detail=f"Identity with id={identity_id} not found")
|
271
|
+
except Exception as e:
|
272
|
+
raise HTTPException(status_code=500, detail=f"{e}")
|
@@ -1,12 +1,12 @@
|
|
1
1
|
from typing import List, Optional
|
2
2
|
|
3
|
-
from fastapi import APIRouter, Body, Depends,
|
3
|
+
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
6
|
from letta.schemas.agent import AgentState, InternalTemplateAgentCreate
|
7
7
|
from letta.schemas.block import Block, InternalTemplateBlockCreate
|
8
8
|
from letta.schemas.group import Group, InternalTemplateGroupCreate
|
9
|
-
from letta.server.rest_api.
|
9
|
+
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
10
10
|
from letta.server.server import SyncServer
|
11
11
|
|
12
12
|
router = APIRouter(prefix="/_internal_templates", tags=["_internal_templates"])
|
@@ -16,13 +16,13 @@ router = APIRouter(prefix="/_internal_templates", tags=["_internal_templates"])
|
|
16
16
|
async def create_group(
|
17
17
|
group: InternalTemplateGroupCreate = Body(...),
|
18
18
|
server: "SyncServer" = Depends(get_letta_server),
|
19
|
-
|
19
|
+
headers: HeaderParams = Depends(get_headers),
|
20
20
|
):
|
21
21
|
"""
|
22
22
|
Create a new multi-agent group with the specified configuration.
|
23
23
|
"""
|
24
24
|
try:
|
25
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
25
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
26
26
|
return await server.group_manager.create_group_async(group, actor=actor)
|
27
27
|
except Exception as e:
|
28
28
|
raise HTTPException(status_code=500, detail=str(e))
|
@@ -32,13 +32,13 @@ async def create_group(
|
|
32
32
|
async def create_agent(
|
33
33
|
agent: InternalTemplateAgentCreate = Body(...),
|
34
34
|
server: "SyncServer" = Depends(get_letta_server),
|
35
|
-
|
35
|
+
headers: HeaderParams = Depends(get_headers),
|
36
36
|
):
|
37
37
|
"""
|
38
38
|
Create a new agent with template-related fields.
|
39
39
|
"""
|
40
40
|
try:
|
41
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
41
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
42
42
|
return await server.agent_manager.create_agent_async(agent, actor=actor)
|
43
43
|
except Exception as e:
|
44
44
|
raise HTTPException(status_code=500, detail=str(e))
|
@@ -48,13 +48,13 @@ async def create_agent(
|
|
48
48
|
async def create_block(
|
49
49
|
block: InternalTemplateBlockCreate = Body(...),
|
50
50
|
server: "SyncServer" = Depends(get_letta_server),
|
51
|
-
|
51
|
+
headers: HeaderParams = Depends(get_headers),
|
52
52
|
):
|
53
53
|
"""
|
54
54
|
Create a new block with template-related fields.
|
55
55
|
"""
|
56
56
|
try:
|
57
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
57
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
58
58
|
block_obj = Block(**block.model_dump())
|
59
59
|
return await server.block_manager.create_or_update_block_async(block_obj, actor=actor)
|
60
60
|
except Exception as e:
|
@@ -68,6 +68,8 @@ class DeploymentEntity(BaseModel):
|
|
68
68
|
type: str
|
69
69
|
name: Optional[str] = None
|
70
70
|
description: Optional[str] = None
|
71
|
+
entity_id: Optional[str] = None
|
72
|
+
project_id: Optional[str] = None
|
71
73
|
|
72
74
|
|
73
75
|
class ListDeploymentEntitiesResponse(BaseModel):
|
@@ -92,7 +94,7 @@ class DeleteDeploymentResponse(BaseModel):
|
|
92
94
|
async def list_deployment_entities(
|
93
95
|
deployment_id: str,
|
94
96
|
server: "SyncServer" = Depends(get_letta_server),
|
95
|
-
|
97
|
+
headers: HeaderParams = Depends(get_headers),
|
96
98
|
entity_types: Optional[List[str]] = Query(None, description="Filter by entity types (block, agent, group)"),
|
97
99
|
):
|
98
100
|
"""
|
@@ -100,7 +102,7 @@ async def list_deployment_entities(
|
|
100
102
|
Optionally filter by entity types.
|
101
103
|
"""
|
102
104
|
try:
|
103
|
-
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)
|
104
106
|
|
105
107
|
entities = []
|
106
108
|
|
@@ -140,6 +142,8 @@ async def list_deployment_entities(
|
|
140
142
|
type="block",
|
141
143
|
name=getattr(block, "template_name", None) or getattr(block, "label", None),
|
142
144
|
description=block.description,
|
145
|
+
entity_id=getattr(block, "entity_id", None),
|
146
|
+
project_id=getattr(block, "project_id", None),
|
143
147
|
)
|
144
148
|
)
|
145
149
|
|
@@ -155,7 +159,16 @@ async def list_deployment_entities(
|
|
155
159
|
agents = result.scalars().all()
|
156
160
|
|
157
161
|
for agent in agents:
|
158
|
-
entities.append(
|
162
|
+
entities.append(
|
163
|
+
DeploymentEntity(
|
164
|
+
id=agent.id,
|
165
|
+
type="agent",
|
166
|
+
name=agent.name,
|
167
|
+
description=agent.description,
|
168
|
+
entity_id=getattr(agent, "entity_id", None),
|
169
|
+
project_id=getattr(agent, "project_id", None),
|
170
|
+
)
|
171
|
+
)
|
159
172
|
|
160
173
|
# Query groups if requested
|
161
174
|
if "group" in types_to_include:
|
@@ -175,6 +188,8 @@ async def list_deployment_entities(
|
|
175
188
|
type="group",
|
176
189
|
name=None, # Groups don't have a name field
|
177
190
|
description=group.description,
|
191
|
+
entity_id=getattr(group, "entity_id", None),
|
192
|
+
project_id=getattr(group, "project_id", None),
|
178
193
|
)
|
179
194
|
)
|
180
195
|
|
@@ -191,14 +206,14 @@ async def list_deployment_entities(
|
|
191
206
|
async def delete_deployment(
|
192
207
|
deployment_id: str,
|
193
208
|
server: "SyncServer" = Depends(get_letta_server),
|
194
|
-
|
209
|
+
headers: HeaderParams = Depends(get_headers),
|
195
210
|
):
|
196
211
|
"""
|
197
212
|
Delete all entities (blocks, agents, groups) with the specified deployment_id.
|
198
213
|
Deletion order: blocks -> agents -> groups to maintain referential integrity.
|
199
214
|
"""
|
200
215
|
try:
|
201
|
-
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
216
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
202
217
|
|
203
218
|
deleted_blocks = []
|
204
219
|
deleted_agents = []
|