letta-nightly 0.7.20.dev20250520104253__py3-none-any.whl → 0.7.21.dev20250521233415__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/__init__.py +1 -1
- letta/agent.py +290 -3
- letta/agents/base_agent.py +0 -55
- letta/agents/helpers.py +5 -0
- letta/agents/letta_agent.py +314 -64
- letta/agents/letta_agent_batch.py +102 -55
- letta/agents/voice_agent.py +5 -5
- letta/client/client.py +9 -18
- letta/constants.py +55 -1
- letta/functions/function_sets/builtin.py +27 -0
- letta/functions/mcp_client/stdio_client.py +1 -1
- letta/groups/sleeptime_multi_agent_v2.py +1 -1
- letta/interfaces/anthropic_streaming_interface.py +10 -1
- letta/interfaces/openai_streaming_interface.py +9 -2
- letta/llm_api/anthropic.py +21 -2
- letta/llm_api/anthropic_client.py +33 -6
- letta/llm_api/google_ai_client.py +136 -423
- letta/llm_api/google_vertex_client.py +173 -22
- letta/llm_api/llm_api_tools.py +27 -0
- letta/llm_api/llm_client.py +1 -1
- letta/llm_api/llm_client_base.py +32 -21
- letta/llm_api/openai.py +57 -0
- letta/llm_api/openai_client.py +7 -11
- letta/memory.py +0 -1
- letta/orm/__init__.py +1 -0
- letta/orm/enums.py +1 -0
- letta/orm/provider_trace.py +26 -0
- letta/orm/step.py +1 -0
- letta/schemas/provider_trace.py +43 -0
- letta/schemas/providers.py +210 -65
- letta/schemas/step.py +1 -0
- letta/schemas/tool.py +4 -0
- letta/server/db.py +37 -19
- letta/server/rest_api/routers/v1/__init__.py +2 -0
- letta/server/rest_api/routers/v1/agents.py +57 -34
- letta/server/rest_api/routers/v1/blocks.py +3 -3
- letta/server/rest_api/routers/v1/identities.py +24 -26
- letta/server/rest_api/routers/v1/jobs.py +3 -3
- letta/server/rest_api/routers/v1/llms.py +13 -8
- letta/server/rest_api/routers/v1/sandbox_configs.py +6 -6
- letta/server/rest_api/routers/v1/tags.py +3 -3
- letta/server/rest_api/routers/v1/telemetry.py +18 -0
- letta/server/rest_api/routers/v1/tools.py +6 -6
- letta/server/rest_api/streaming_response.py +105 -0
- letta/server/rest_api/utils.py +4 -0
- letta/server/server.py +140 -0
- letta/services/agent_manager.py +251 -18
- letta/services/block_manager.py +52 -37
- letta/services/helpers/noop_helper.py +10 -0
- letta/services/identity_manager.py +43 -38
- letta/services/job_manager.py +29 -0
- letta/services/message_manager.py +111 -0
- letta/services/sandbox_config_manager.py +36 -0
- letta/services/step_manager.py +146 -0
- letta/services/telemetry_manager.py +58 -0
- letta/services/tool_executor/tool_execution_manager.py +49 -5
- letta/services/tool_executor/tool_execution_sandbox.py +47 -0
- letta/services/tool_executor/tool_executor.py +236 -7
- letta/services/tool_manager.py +160 -1
- letta/services/tool_sandbox/e2b_sandbox.py +65 -3
- letta/settings.py +10 -2
- letta/tracing.py +5 -5
- {letta_nightly-0.7.20.dev20250520104253.dist-info → letta_nightly-0.7.21.dev20250521233415.dist-info}/METADATA +3 -2
- {letta_nightly-0.7.20.dev20250520104253.dist-info → letta_nightly-0.7.21.dev20250521233415.dist-info}/RECORD +67 -60
- {letta_nightly-0.7.20.dev20250520104253.dist-info → letta_nightly-0.7.21.dev20250521233415.dist-info}/LICENSE +0 -0
- {letta_nightly-0.7.20.dev20250520104253.dist-info → letta_nightly-0.7.21.dev20250521233415.dist-info}/WHEEL +0 -0
- {letta_nightly-0.7.20.dev20250520104253.dist-info → letta_nightly-0.7.21.dev20250521233415.dist-info}/entry_points.txt +0 -0
@@ -33,6 +33,7 @@ from letta.schemas.user import User
|
|
33
33
|
from letta.serialize_schemas.pydantic_agent_schema import AgentSchema
|
34
34
|
from letta.server.rest_api.utils import get_letta_server
|
35
35
|
from letta.server.server import SyncServer
|
36
|
+
from letta.services.telemetry_manager import NoopTelemetryManager
|
36
37
|
from letta.settings import settings
|
37
38
|
|
38
39
|
# These can be forward refs, but because Fastapi needs them at runtime the must be imported normally
|
@@ -106,14 +107,15 @@ async def list_agents(
|
|
106
107
|
|
107
108
|
|
108
109
|
@router.get("/count", response_model=int, operation_id="count_agents")
|
109
|
-
def count_agents(
|
110
|
+
async def count_agents(
|
110
111
|
server: SyncServer = Depends(get_letta_server),
|
111
112
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
112
113
|
):
|
113
114
|
"""
|
114
115
|
Get the count of all agents associated with a given user.
|
115
116
|
"""
|
116
|
-
|
117
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
118
|
+
return await server.agent_manager.size_async(actor=actor)
|
117
119
|
|
118
120
|
|
119
121
|
class IndentedORJSONResponse(Response):
|
@@ -124,7 +126,7 @@ class IndentedORJSONResponse(Response):
|
|
124
126
|
|
125
127
|
|
126
128
|
@router.get("/{agent_id}/export", response_class=IndentedORJSONResponse, operation_id="export_agent_serialized")
|
127
|
-
def export_agent_serialized(
|
129
|
+
async def export_agent_serialized(
|
128
130
|
agent_id: str,
|
129
131
|
server: "SyncServer" = Depends(get_letta_server),
|
130
132
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
@@ -135,7 +137,7 @@ def export_agent_serialized(
|
|
135
137
|
"""
|
136
138
|
Export the serialized JSON representation of an agent, formatted with indentation.
|
137
139
|
"""
|
138
|
-
actor = server.user_manager.
|
140
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
139
141
|
|
140
142
|
try:
|
141
143
|
agent = server.agent_manager.serialize(agent_id=agent_id, actor=actor)
|
@@ -200,7 +202,7 @@ async def import_agent_serialized(
|
|
200
202
|
|
201
203
|
|
202
204
|
@router.get("/{agent_id}/context", response_model=ContextWindowOverview, operation_id="retrieve_agent_context_window")
|
203
|
-
def retrieve_agent_context_window(
|
205
|
+
async def retrieve_agent_context_window(
|
204
206
|
agent_id: str,
|
205
207
|
server: "SyncServer" = Depends(get_letta_server),
|
206
208
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
@@ -208,9 +210,12 @@ def retrieve_agent_context_window(
|
|
208
210
|
"""
|
209
211
|
Retrieve the context window of a specific agent.
|
210
212
|
"""
|
211
|
-
actor = server.user_manager.
|
212
|
-
|
213
|
-
|
213
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
214
|
+
try:
|
215
|
+
return await server.get_agent_context_window_async(agent_id=agent_id, actor=actor)
|
216
|
+
except Exception as e:
|
217
|
+
traceback.print_exc()
|
218
|
+
raise e
|
214
219
|
|
215
220
|
|
216
221
|
class CreateAgentRequest(CreateAgent):
|
@@ -341,7 +346,7 @@ async def retrieve_agent(
|
|
341
346
|
"""
|
342
347
|
Get the state of the agent.
|
343
348
|
"""
|
344
|
-
actor = server.user_manager.
|
349
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
345
350
|
|
346
351
|
try:
|
347
352
|
return await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, actor=actor)
|
@@ -367,7 +372,7 @@ def delete_agent(
|
|
367
372
|
|
368
373
|
|
369
374
|
@router.get("/{agent_id}/sources", response_model=List[Source], operation_id="list_agent_sources")
|
370
|
-
def list_agent_sources(
|
375
|
+
async def list_agent_sources(
|
371
376
|
agent_id: str,
|
372
377
|
server: "SyncServer" = Depends(get_letta_server),
|
373
378
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
@@ -375,8 +380,8 @@ def list_agent_sources(
|
|
375
380
|
"""
|
376
381
|
Get the sources associated with an agent.
|
377
382
|
"""
|
378
|
-
actor = server.user_manager.
|
379
|
-
return server.agent_manager.
|
383
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
384
|
+
return await server.agent_manager.list_attached_sources_async(agent_id=agent_id, actor=actor)
|
380
385
|
|
381
386
|
|
382
387
|
# TODO: remove? can also get with agent blocks
|
@@ -424,14 +429,14 @@ async def list_blocks(
|
|
424
429
|
"""
|
425
430
|
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
426
431
|
try:
|
427
|
-
agent = await server.agent_manager.get_agent_by_id_async(agent_id, actor)
|
432
|
+
agent = await server.agent_manager.get_agent_by_id_async(agent_id=agent_id, include_relationships=["memory"], actor=actor)
|
428
433
|
return agent.memory.blocks
|
429
434
|
except NoResultFound as e:
|
430
435
|
raise HTTPException(status_code=404, detail=str(e))
|
431
436
|
|
432
437
|
|
433
438
|
@router.patch("/{agent_id}/core-memory/blocks/{block_label}", response_model=Block, operation_id="modify_core_memory_block")
|
434
|
-
def modify_block(
|
439
|
+
async def modify_block(
|
435
440
|
agent_id: str,
|
436
441
|
block_label: str,
|
437
442
|
block_update: BlockUpdate = Body(...),
|
@@ -441,10 +446,11 @@ def modify_block(
|
|
441
446
|
"""
|
442
447
|
Updates a core memory block of an agent.
|
443
448
|
"""
|
444
|
-
actor = server.user_manager.
|
449
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
445
450
|
|
446
|
-
block = server.agent_manager.
|
447
|
-
|
451
|
+
block = await server.agent_manager.modify_block_by_label_async(
|
452
|
+
agent_id=agent_id, block_label=block_label, block_update=block_update, actor=actor
|
453
|
+
)
|
448
454
|
|
449
455
|
# This should also trigger a system prompt change in the agent
|
450
456
|
server.agent_manager.rebuild_system_prompt(agent_id=agent_id, actor=actor, force=True, update_timestamp=False)
|
@@ -481,7 +487,7 @@ def detach_block(
|
|
481
487
|
|
482
488
|
|
483
489
|
@router.get("/{agent_id}/archival-memory", response_model=List[Passage], operation_id="list_passages")
|
484
|
-
def list_passages(
|
490
|
+
async def list_passages(
|
485
491
|
agent_id: str,
|
486
492
|
server: "SyncServer" = Depends(get_letta_server),
|
487
493
|
after: Optional[str] = Query(None, description="Unique ID of the memory to start the query range at."),
|
@@ -496,11 +502,11 @@ def list_passages(
|
|
496
502
|
"""
|
497
503
|
Retrieve the memories in an agent's archival memory store (paginated query).
|
498
504
|
"""
|
499
|
-
actor = server.user_manager.
|
505
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
500
506
|
|
501
|
-
return server.
|
502
|
-
user_id=actor.id,
|
507
|
+
return await server.get_agent_archival_async(
|
503
508
|
agent_id=agent_id,
|
509
|
+
actor=actor,
|
504
510
|
after=after,
|
505
511
|
before=before,
|
506
512
|
query_text=search,
|
@@ -564,7 +570,7 @@ AgentMessagesResponse = Annotated[
|
|
564
570
|
|
565
571
|
|
566
572
|
@router.get("/{agent_id}/messages", response_model=AgentMessagesResponse, operation_id="list_messages")
|
567
|
-
def list_messages(
|
573
|
+
async def list_messages(
|
568
574
|
agent_id: str,
|
569
575
|
server: "SyncServer" = Depends(get_letta_server),
|
570
576
|
after: Optional[str] = Query(None, description="Message after which to retrieve the returned messages."),
|
@@ -579,10 +585,9 @@ def list_messages(
|
|
579
585
|
"""
|
580
586
|
Retrieve message history for an agent.
|
581
587
|
"""
|
582
|
-
actor = server.user_manager.
|
588
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
583
589
|
|
584
|
-
return server.
|
585
|
-
user_id=actor.id,
|
590
|
+
return await server.get_agent_recall_async(
|
586
591
|
agent_id=agent_id,
|
587
592
|
after=after,
|
588
593
|
before=before,
|
@@ -593,6 +598,7 @@ def list_messages(
|
|
593
598
|
use_assistant_message=use_assistant_message,
|
594
599
|
assistant_message_tool_name=assistant_message_tool_name,
|
595
600
|
assistant_message_tool_kwarg=assistant_message_tool_kwarg,
|
601
|
+
actor=actor,
|
596
602
|
)
|
597
603
|
|
598
604
|
|
@@ -634,7 +640,7 @@ async def send_message(
|
|
634
640
|
agent_eligible = not agent.enable_sleeptime and not agent.multi_agent_group and agent.agent_type != AgentType.sleeptime_agent
|
635
641
|
experimental_header = request_obj.headers.get("X-EXPERIMENTAL") or "false"
|
636
642
|
feature_enabled = settings.use_experimental or experimental_header.lower() == "true"
|
637
|
-
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "
|
643
|
+
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "together", "google_ai", "google_vertex"]
|
638
644
|
|
639
645
|
if agent_eligible and feature_enabled and model_compatible:
|
640
646
|
experimental_agent = LettaAgent(
|
@@ -644,6 +650,8 @@ async def send_message(
|
|
644
650
|
block_manager=server.block_manager,
|
645
651
|
passage_manager=server.passage_manager,
|
646
652
|
actor=actor,
|
653
|
+
step_manager=server.step_manager,
|
654
|
+
telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(),
|
647
655
|
)
|
648
656
|
|
649
657
|
result = await experimental_agent.step(request.messages, max_steps=10, use_assistant_message=request.use_assistant_message)
|
@@ -692,7 +700,8 @@ async def send_message_streaming(
|
|
692
700
|
agent_eligible = not agent.enable_sleeptime and not agent.multi_agent_group and agent.agent_type != AgentType.sleeptime_agent
|
693
701
|
experimental_header = request_obj.headers.get("X-EXPERIMENTAL") or "false"
|
694
702
|
feature_enabled = settings.use_experimental or experimental_header.lower() == "true"
|
695
|
-
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai"]
|
703
|
+
model_compatible = agent.llm_config.model_endpoint_type in ["anthropic", "openai", "together", "google_ai", "google_vertex"]
|
704
|
+
model_compatible_token_streaming = agent.llm_config.model_endpoint_type in ["anthropic", "openai"]
|
696
705
|
|
697
706
|
if agent_eligible and feature_enabled and model_compatible and request.stream_tokens:
|
698
707
|
experimental_agent = LettaAgent(
|
@@ -702,14 +711,28 @@ async def send_message_streaming(
|
|
702
711
|
block_manager=server.block_manager,
|
703
712
|
passage_manager=server.passage_manager,
|
704
713
|
actor=actor,
|
714
|
+
step_manager=server.step_manager,
|
715
|
+
telemetry_manager=server.telemetry_manager if settings.llm_api_logging else NoopTelemetryManager(),
|
705
716
|
)
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
717
|
+
from letta.server.rest_api.streaming_response import StreamingResponseWithStatusCode
|
718
|
+
|
719
|
+
if request.stream_tokens and model_compatible_token_streaming:
|
720
|
+
result = StreamingResponseWithStatusCode(
|
721
|
+
experimental_agent.step_stream(
|
722
|
+
input_messages=request.messages,
|
723
|
+
max_steps=10,
|
724
|
+
use_assistant_message=request.use_assistant_message,
|
725
|
+
request_start_timestamp_ns=request_start_timestamp_ns,
|
726
|
+
),
|
727
|
+
media_type="text/event-stream",
|
728
|
+
)
|
729
|
+
else:
|
730
|
+
result = StreamingResponseWithStatusCode(
|
731
|
+
experimental_agent.step_stream_no_tokens(
|
732
|
+
request.messages, max_steps=10, use_assistant_message=request.use_assistant_message
|
733
|
+
),
|
734
|
+
media_type="text/event-stream",
|
735
|
+
)
|
713
736
|
else:
|
714
737
|
result = await server.send_message_to_agent(
|
715
738
|
agent_id=agent_id,
|
@@ -99,7 +99,7 @@ def retrieve_block(
|
|
99
99
|
|
100
100
|
|
101
101
|
@router.get("/{block_id}/agents", response_model=List[AgentState], operation_id="list_agents_for_block")
|
102
|
-
def list_agents_for_block(
|
102
|
+
async def list_agents_for_block(
|
103
103
|
block_id: str,
|
104
104
|
server: SyncServer = Depends(get_letta_server),
|
105
105
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
@@ -108,9 +108,9 @@ def list_agents_for_block(
|
|
108
108
|
Retrieves all agents associated with the specified block.
|
109
109
|
Raises a 404 if the block does not exist.
|
110
110
|
"""
|
111
|
-
actor = server.user_manager.
|
111
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
112
112
|
try:
|
113
|
-
agents = server.block_manager.
|
113
|
+
agents = await server.block_manager.get_agents_for_block_async(block_id=block_id, actor=actor)
|
114
114
|
return agents
|
115
115
|
except NoResultFound:
|
116
116
|
raise HTTPException(status_code=404, detail=f"Block with id={block_id} not found")
|
@@ -13,7 +13,7 @@ router = APIRouter(prefix="/identities", tags=["identities"])
|
|
13
13
|
|
14
14
|
|
15
15
|
@router.get("/", tags=["identities"], response_model=List[Identity], operation_id="list_identities")
|
16
|
-
def list_identities(
|
16
|
+
async def list_identities(
|
17
17
|
name: Optional[str] = Query(None),
|
18
18
|
project_id: Optional[str] = Query(None),
|
19
19
|
identifier_key: Optional[str] = Query(None),
|
@@ -28,9 +28,9 @@ def list_identities(
|
|
28
28
|
Get a list of all identities in the database
|
29
29
|
"""
|
30
30
|
try:
|
31
|
-
actor = server.user_manager.
|
31
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
32
32
|
|
33
|
-
identities = server.identity_manager.
|
33
|
+
identities = await server.identity_manager.list_identities_async(
|
34
34
|
name=name,
|
35
35
|
project_id=project_id,
|
36
36
|
identifier_key=identifier_key,
|
@@ -50,7 +50,7 @@ def list_identities(
|
|
50
50
|
|
51
51
|
|
52
52
|
@router.get("/count", tags=["identities"], response_model=int, operation_id="count_identities")
|
53
|
-
def count_identities(
|
53
|
+
async def count_identities(
|
54
54
|
server: "SyncServer" = Depends(get_letta_server),
|
55
55
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
56
56
|
):
|
@@ -58,7 +58,8 @@ def count_identities(
|
|
58
58
|
Get count of all identities for a user
|
59
59
|
"""
|
60
60
|
try:
|
61
|
-
|
61
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
62
|
+
return await server.identity_manager.size_async(actor=actor)
|
62
63
|
except NoResultFound:
|
63
64
|
return 0
|
64
65
|
except HTTPException:
|
@@ -68,28 +69,28 @@ def count_identities(
|
|
68
69
|
|
69
70
|
|
70
71
|
@router.get("/{identity_id}", tags=["identities"], response_model=Identity, operation_id="retrieve_identity")
|
71
|
-
def retrieve_identity(
|
72
|
+
async def retrieve_identity(
|
72
73
|
identity_id: str,
|
73
74
|
server: "SyncServer" = Depends(get_letta_server),
|
74
75
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
75
76
|
):
|
76
77
|
try:
|
77
|
-
actor = server.user_manager.
|
78
|
-
return server.identity_manager.
|
78
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
79
|
+
return await server.identity_manager.get_identity_async(identity_id=identity_id, actor=actor)
|
79
80
|
except NoResultFound as e:
|
80
81
|
raise HTTPException(status_code=404, detail=str(e))
|
81
82
|
|
82
83
|
|
83
84
|
@router.post("/", tags=["identities"], response_model=Identity, operation_id="create_identity")
|
84
|
-
def create_identity(
|
85
|
+
async def create_identity(
|
85
86
|
identity: IdentityCreate = Body(...),
|
86
87
|
server: "SyncServer" = Depends(get_letta_server),
|
87
88
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
88
89
|
x_project: Optional[str] = Header(None, alias="X-Project"), # Only handled by next js middleware
|
89
90
|
):
|
90
91
|
try:
|
91
|
-
actor = server.user_manager.
|
92
|
-
return server.identity_manager.
|
92
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
93
|
+
return await server.identity_manager.create_identity_async(identity=identity, actor=actor)
|
93
94
|
except HTTPException:
|
94
95
|
raise
|
95
96
|
except UniqueConstraintViolationError:
|
@@ -105,15 +106,15 @@ def create_identity(
|
|
105
106
|
|
106
107
|
|
107
108
|
@router.put("/", tags=["identities"], response_model=Identity, operation_id="upsert_identity")
|
108
|
-
def upsert_identity(
|
109
|
+
async def upsert_identity(
|
109
110
|
identity: IdentityUpsert = Body(...),
|
110
111
|
server: "SyncServer" = Depends(get_letta_server),
|
111
112
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
112
113
|
x_project: Optional[str] = Header(None, alias="X-Project"), # Only handled by next js middleware
|
113
114
|
):
|
114
115
|
try:
|
115
|
-
actor = server.user_manager.
|
116
|
-
return server.identity_manager.
|
116
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
117
|
+
return await server.identity_manager.upsert_identity_async(identity=identity, actor=actor)
|
117
118
|
except HTTPException:
|
118
119
|
raise
|
119
120
|
except NoResultFound as e:
|
@@ -123,36 +124,33 @@ def upsert_identity(
|
|
123
124
|
|
124
125
|
|
125
126
|
@router.patch("/{identity_id}", tags=["identities"], response_model=Identity, operation_id="update_identity")
|
126
|
-
def modify_identity(
|
127
|
+
async def modify_identity(
|
127
128
|
identity_id: str,
|
128
129
|
identity: IdentityUpdate = Body(...),
|
129
130
|
server: "SyncServer" = Depends(get_letta_server),
|
130
131
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
131
132
|
):
|
132
133
|
try:
|
133
|
-
actor = server.user_manager.
|
134
|
-
return server.identity_manager.
|
134
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
135
|
+
return await server.identity_manager.update_identity_async(identity_id=identity_id, identity=identity, actor=actor)
|
135
136
|
except HTTPException:
|
136
137
|
raise
|
137
138
|
except NoResultFound as e:
|
138
139
|
raise HTTPException(status_code=404, detail=str(e))
|
139
140
|
except Exception as e:
|
140
|
-
import traceback
|
141
|
-
|
142
|
-
print(traceback.format_exc())
|
143
141
|
raise HTTPException(status_code=500, detail=f"{e}")
|
144
142
|
|
145
143
|
|
146
144
|
@router.put("/{identity_id}/properties", tags=["identities"], operation_id="upsert_identity_properties")
|
147
|
-
def upsert_identity_properties(
|
145
|
+
async def upsert_identity_properties(
|
148
146
|
identity_id: str,
|
149
147
|
properties: List[IdentityProperty] = Body(...),
|
150
148
|
server: "SyncServer" = Depends(get_letta_server),
|
151
149
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
152
150
|
):
|
153
151
|
try:
|
154
|
-
actor = server.user_manager.
|
155
|
-
return server.identity_manager.
|
152
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
153
|
+
return await server.identity_manager.upsert_identity_properties_async(identity_id=identity_id, properties=properties, actor=actor)
|
156
154
|
except HTTPException:
|
157
155
|
raise
|
158
156
|
except NoResultFound as e:
|
@@ -162,7 +160,7 @@ def upsert_identity_properties(
|
|
162
160
|
|
163
161
|
|
164
162
|
@router.delete("/{identity_id}", tags=["identities"], operation_id="delete_identity")
|
165
|
-
def delete_identity(
|
163
|
+
async def delete_identity(
|
166
164
|
identity_id: str,
|
167
165
|
server: "SyncServer" = Depends(get_letta_server),
|
168
166
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
@@ -171,8 +169,8 @@ def delete_identity(
|
|
171
169
|
Delete an identity by its identifier key
|
172
170
|
"""
|
173
171
|
try:
|
174
|
-
actor = server.user_manager.
|
175
|
-
server.identity_manager.
|
172
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
173
|
+
await server.identity_manager.delete_identity_async(identity_id=identity_id, actor=actor)
|
176
174
|
except HTTPException:
|
177
175
|
raise
|
178
176
|
except NoResultFound as e:
|
@@ -33,16 +33,16 @@ def list_jobs(
|
|
33
33
|
|
34
34
|
|
35
35
|
@router.get("/active", response_model=List[Job], operation_id="list_active_jobs")
|
36
|
-
def list_active_jobs(
|
36
|
+
async def list_active_jobs(
|
37
37
|
server: "SyncServer" = Depends(get_letta_server),
|
38
38
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
39
39
|
):
|
40
40
|
"""
|
41
41
|
List all active jobs.
|
42
42
|
"""
|
43
|
-
actor = server.user_manager.
|
43
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
44
44
|
|
45
|
-
return server.job_manager.
|
45
|
+
return await server.job_manager.list_jobs_async(actor=actor, statuses=[JobStatus.created, JobStatus.running])
|
46
46
|
|
47
47
|
|
48
48
|
@router.get("/{job_id}", response_model=Job, operation_id="retrieve_job")
|
@@ -14,30 +14,35 @@ router = APIRouter(prefix="/models", tags=["models", "llms"])
|
|
14
14
|
|
15
15
|
|
16
16
|
@router.get("/", response_model=List[LLMConfig], operation_id="list_models")
|
17
|
-
def list_llm_models(
|
17
|
+
async def list_llm_models(
|
18
18
|
provider_category: Optional[List[ProviderCategory]] = Query(None),
|
19
19
|
provider_name: Optional[str] = Query(None),
|
20
20
|
provider_type: Optional[ProviderType] = Query(None),
|
21
21
|
server: "SyncServer" = Depends(get_letta_server),
|
22
|
-
actor_id: Optional[str] = Header(None, alias="user_id"),
|
22
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
23
|
+
# Extract user_id from header, default to None if not present
|
23
24
|
):
|
25
|
+
"""List available LLM models using the asynchronous implementation for improved performance"""
|
24
26
|
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
25
|
-
|
27
|
+
|
28
|
+
models = await server.list_llm_models_async(
|
26
29
|
provider_category=provider_category,
|
27
30
|
provider_name=provider_name,
|
28
31
|
provider_type=provider_type,
|
29
32
|
actor=actor,
|
30
33
|
)
|
31
|
-
|
34
|
+
|
32
35
|
return models
|
33
36
|
|
34
37
|
|
35
38
|
@router.get("/embedding", response_model=List[EmbeddingConfig], operation_id="list_embedding_models")
|
36
|
-
def list_embedding_models(
|
39
|
+
async def list_embedding_models(
|
37
40
|
server: "SyncServer" = Depends(get_letta_server),
|
38
|
-
actor_id: Optional[str] = Header(None, alias="user_id"),
|
41
|
+
actor_id: Optional[str] = Header(None, alias="user_id"),
|
42
|
+
# Extract user_id from header, default to None if not present
|
39
43
|
):
|
44
|
+
"""List available embedding models using the asynchronous implementation for improved performance"""
|
40
45
|
actor = server.user_manager.get_user_or_default(user_id=actor_id)
|
41
|
-
models = server.
|
42
|
-
|
46
|
+
models = await server.list_embedding_models_async(actor=actor)
|
47
|
+
|
43
48
|
return models
|
@@ -100,15 +100,15 @@ def delete_sandbox_config(
|
|
100
100
|
|
101
101
|
|
102
102
|
@router.get("/", response_model=List[PydanticSandboxConfig])
|
103
|
-
def list_sandbox_configs(
|
103
|
+
async def list_sandbox_configs(
|
104
104
|
limit: int = Query(1000, description="Number of results to return"),
|
105
105
|
after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
|
106
106
|
sandbox_type: Optional[SandboxType] = Query(None, description="Filter for this specific sandbox type"),
|
107
107
|
server: SyncServer = Depends(get_letta_server),
|
108
108
|
actor_id: str = Depends(get_user_id),
|
109
109
|
):
|
110
|
-
actor = server.user_manager.
|
111
|
-
return server.sandbox_config_manager.
|
110
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
111
|
+
return await server.sandbox_config_manager.list_sandbox_configs_async(actor, limit=limit, after=after, sandbox_type=sandbox_type)
|
112
112
|
|
113
113
|
|
114
114
|
@router.post("/local/recreate-venv", response_model=PydanticSandboxConfig)
|
@@ -190,12 +190,12 @@ def delete_sandbox_env_var(
|
|
190
190
|
|
191
191
|
|
192
192
|
@router.get("/{sandbox_config_id}/environment-variable", response_model=List[PydanticEnvVar])
|
193
|
-
def list_sandbox_env_vars(
|
193
|
+
async def list_sandbox_env_vars(
|
194
194
|
sandbox_config_id: str,
|
195
195
|
limit: int = Query(1000, description="Number of results to return"),
|
196
196
|
after: Optional[str] = Query(None, description="Pagination cursor to fetch the next set of results"),
|
197
197
|
server: SyncServer = Depends(get_letta_server),
|
198
198
|
actor_id: str = Depends(get_user_id),
|
199
199
|
):
|
200
|
-
actor = server.user_manager.
|
201
|
-
return server.sandbox_config_manager.
|
200
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
201
|
+
return await server.sandbox_config_manager.list_sandbox_env_vars_async(sandbox_config_id, actor, limit=limit, after=after)
|
@@ -12,7 +12,7 @@ router = APIRouter(prefix="/tags", tags=["tag", "admin"])
|
|
12
12
|
|
13
13
|
|
14
14
|
@router.get("/", tags=["admin"], response_model=List[str], operation_id="list_tags")
|
15
|
-
def list_tags(
|
15
|
+
async def list_tags(
|
16
16
|
after: Optional[str] = Query(None),
|
17
17
|
limit: Optional[int] = Query(50),
|
18
18
|
server: "SyncServer" = Depends(get_letta_server),
|
@@ -22,6 +22,6 @@ def list_tags(
|
|
22
22
|
"""
|
23
23
|
Get a list of all tags in the database
|
24
24
|
"""
|
25
|
-
actor = server.user_manager.
|
26
|
-
tags = server.agent_manager.
|
25
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
26
|
+
tags = await server.agent_manager.list_tags_async(actor=actor, after=after, limit=limit, query_text=query_text)
|
27
27
|
return tags
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from fastapi import APIRouter, Depends, Header
|
2
|
+
|
3
|
+
from letta.schemas.provider_trace import ProviderTrace
|
4
|
+
from letta.server.rest_api.utils import get_letta_server
|
5
|
+
from letta.server.server import SyncServer
|
6
|
+
|
7
|
+
router = APIRouter(prefix="/telemetry", tags=["telemetry"])
|
8
|
+
|
9
|
+
|
10
|
+
@router.get("/{step_id}", response_model=ProviderTrace, operation_id="retrieve_provider_trace")
|
11
|
+
async def retrieve_provider_trace_by_step_id(
|
12
|
+
step_id: str,
|
13
|
+
server: SyncServer = Depends(get_letta_server),
|
14
|
+
actor_id: str | None = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
15
|
+
):
|
16
|
+
return await server.telemetry_manager.get_provider_trace_by_step_id_async(
|
17
|
+
step_id=step_id, actor=await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
18
|
+
)
|
@@ -59,7 +59,7 @@ def count_tools(
|
|
59
59
|
|
60
60
|
|
61
61
|
@router.get("/{tool_id}", response_model=Tool, operation_id="retrieve_tool")
|
62
|
-
def retrieve_tool(
|
62
|
+
async def retrieve_tool(
|
63
63
|
tool_id: str,
|
64
64
|
server: SyncServer = Depends(get_letta_server),
|
65
65
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
@@ -67,8 +67,8 @@ def retrieve_tool(
|
|
67
67
|
"""
|
68
68
|
Get a tool by ID
|
69
69
|
"""
|
70
|
-
actor = server.user_manager.
|
71
|
-
tool = server.tool_manager.
|
70
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
71
|
+
tool = await server.tool_manager.get_tool_by_id_async(tool_id=tool_id, actor=actor)
|
72
72
|
if tool is None:
|
73
73
|
# return 404 error
|
74
74
|
raise HTTPException(status_code=404, detail=f"Tool with id {tool_id} not found.")
|
@@ -196,15 +196,15 @@ def modify_tool(
|
|
196
196
|
|
197
197
|
|
198
198
|
@router.post("/add-base-tools", response_model=List[Tool], operation_id="add_base_tools")
|
199
|
-
def upsert_base_tools(
|
199
|
+
async def upsert_base_tools(
|
200
200
|
server: SyncServer = Depends(get_letta_server),
|
201
201
|
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
|
202
202
|
):
|
203
203
|
"""
|
204
204
|
Upsert base tools
|
205
205
|
"""
|
206
|
-
actor = server.user_manager.
|
207
|
-
return server.tool_manager.
|
206
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=actor_id)
|
207
|
+
return await server.tool_manager.upsert_base_tools_async(actor=actor)
|
208
208
|
|
209
209
|
|
210
210
|
@router.post("/run", response_model=ToolReturnMessage, operation_id="run_tool_from_source")
|