letta-nightly 0.11.7.dev20251007104119__py3-none-any.whl → 0.12.0.dev20251009104148__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/adapters/letta_llm_adapter.py +1 -0
- letta/adapters/letta_llm_request_adapter.py +0 -1
- letta/adapters/letta_llm_stream_adapter.py +7 -2
- letta/adapters/simple_llm_request_adapter.py +88 -0
- letta/adapters/simple_llm_stream_adapter.py +192 -0
- letta/agents/agent_loop.py +6 -0
- letta/agents/ephemeral_summary_agent.py +2 -1
- letta/agents/helpers.py +142 -6
- letta/agents/letta_agent.py +13 -33
- letta/agents/letta_agent_batch.py +2 -4
- letta/agents/letta_agent_v2.py +87 -77
- letta/agents/letta_agent_v3.py +927 -0
- letta/agents/voice_agent.py +2 -6
- letta/constants.py +8 -4
- letta/database_utils.py +161 -0
- letta/errors.py +40 -0
- letta/functions/function_sets/base.py +84 -4
- letta/functions/function_sets/multi_agent.py +0 -3
- letta/functions/schema_generator.py +113 -71
- letta/groups/dynamic_multi_agent.py +3 -2
- letta/groups/helpers.py +1 -2
- letta/groups/round_robin_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent_v2.py +1 -1
- letta/groups/sleeptime_multi_agent_v3.py +17 -17
- letta/groups/supervisor_multi_agent.py +84 -80
- letta/helpers/converters.py +3 -0
- letta/helpers/message_helper.py +4 -0
- letta/helpers/tool_rule_solver.py +92 -5
- letta/interfaces/anthropic_streaming_interface.py +409 -0
- letta/interfaces/gemini_streaming_interface.py +296 -0
- letta/interfaces/openai_streaming_interface.py +752 -1
- letta/llm_api/anthropic_client.py +127 -16
- letta/llm_api/bedrock_client.py +4 -2
- letta/llm_api/deepseek_client.py +4 -1
- letta/llm_api/google_vertex_client.py +124 -42
- letta/llm_api/groq_client.py +4 -1
- letta/llm_api/llm_api_tools.py +11 -4
- letta/llm_api/llm_client_base.py +6 -2
- letta/llm_api/openai.py +32 -2
- letta/llm_api/openai_client.py +423 -18
- letta/llm_api/xai_client.py +4 -1
- letta/main.py +9 -5
- letta/memory.py +1 -0
- letta/orm/__init__.py +2 -1
- letta/orm/agent.py +10 -0
- letta/orm/block.py +7 -16
- letta/orm/blocks_agents.py +8 -2
- letta/orm/files_agents.py +2 -0
- letta/orm/job.py +7 -5
- letta/orm/mcp_oauth.py +1 -0
- letta/orm/message.py +21 -6
- letta/orm/organization.py +2 -0
- letta/orm/provider.py +6 -2
- letta/orm/run.py +71 -0
- letta/orm/run_metrics.py +82 -0
- letta/orm/sandbox_config.py +7 -1
- letta/orm/sqlalchemy_base.py +0 -306
- letta/orm/step.py +6 -5
- letta/orm/step_metrics.py +5 -5
- letta/otel/tracing.py +28 -3
- letta/plugins/defaults.py +4 -4
- letta/prompts/system_prompts/__init__.py +2 -0
- letta/prompts/system_prompts/letta_v1.py +25 -0
- letta/schemas/agent.py +3 -2
- letta/schemas/agent_file.py +9 -3
- letta/schemas/block.py +23 -10
- letta/schemas/enums.py +21 -2
- letta/schemas/job.py +17 -4
- letta/schemas/letta_message_content.py +71 -2
- letta/schemas/letta_stop_reason.py +5 -5
- letta/schemas/llm_config.py +53 -3
- letta/schemas/memory.py +1 -1
- letta/schemas/message.py +564 -117
- letta/schemas/openai/responses_request.py +64 -0
- letta/schemas/providers/__init__.py +2 -0
- letta/schemas/providers/anthropic.py +16 -0
- letta/schemas/providers/ollama.py +115 -33
- letta/schemas/providers/openrouter.py +52 -0
- letta/schemas/providers/vllm.py +2 -1
- letta/schemas/run.py +48 -42
- letta/schemas/run_metrics.py +21 -0
- letta/schemas/step.py +2 -2
- letta/schemas/step_metrics.py +1 -1
- letta/schemas/tool.py +15 -107
- letta/schemas/tool_rule.py +88 -5
- letta/serialize_schemas/marshmallow_agent.py +1 -0
- letta/server/db.py +79 -408
- letta/server/rest_api/app.py +61 -10
- letta/server/rest_api/dependencies.py +14 -0
- letta/server/rest_api/redis_stream_manager.py +19 -8
- letta/server/rest_api/routers/v1/agents.py +364 -292
- letta/server/rest_api/routers/v1/blocks.py +14 -20
- letta/server/rest_api/routers/v1/identities.py +45 -110
- letta/server/rest_api/routers/v1/internal_templates.py +21 -0
- letta/server/rest_api/routers/v1/jobs.py +23 -6
- letta/server/rest_api/routers/v1/messages.py +1 -1
- letta/server/rest_api/routers/v1/runs.py +149 -99
- letta/server/rest_api/routers/v1/sandbox_configs.py +10 -19
- letta/server/rest_api/routers/v1/tools.py +281 -594
- letta/server/rest_api/routers/v1/voice.py +1 -1
- letta/server/rest_api/streaming_response.py +29 -29
- letta/server/rest_api/utils.py +122 -64
- letta/server/server.py +160 -887
- letta/services/agent_manager.py +236 -919
- letta/services/agent_serialization_manager.py +16 -0
- letta/services/archive_manager.py +0 -100
- letta/services/block_manager.py +211 -168
- letta/services/context_window_calculator/token_counter.py +1 -1
- letta/services/file_manager.py +1 -1
- letta/services/files_agents_manager.py +24 -33
- letta/services/group_manager.py +0 -142
- letta/services/helpers/agent_manager_helper.py +7 -2
- letta/services/helpers/run_manager_helper.py +69 -0
- letta/services/job_manager.py +96 -411
- letta/services/lettuce/__init__.py +6 -0
- letta/services/lettuce/lettuce_client_base.py +86 -0
- letta/services/mcp_manager.py +38 -6
- letta/services/message_manager.py +165 -362
- letta/services/organization_manager.py +0 -36
- letta/services/passage_manager.py +0 -345
- letta/services/provider_manager.py +0 -80
- letta/services/run_manager.py +364 -0
- letta/services/sandbox_config_manager.py +0 -234
- letta/services/step_manager.py +62 -39
- letta/services/summarizer/summarizer.py +9 -7
- letta/services/telemetry_manager.py +0 -16
- letta/services/tool_executor/builtin_tool_executor.py +35 -0
- letta/services/tool_executor/core_tool_executor.py +397 -2
- letta/services/tool_executor/files_tool_executor.py +3 -3
- letta/services/tool_executor/multi_agent_tool_executor.py +30 -15
- letta/services/tool_executor/tool_execution_manager.py +6 -8
- letta/services/tool_executor/tool_executor_base.py +3 -3
- letta/services/tool_manager.py +85 -339
- letta/services/tool_sandbox/base.py +24 -13
- letta/services/tool_sandbox/e2b_sandbox.py +16 -1
- letta/services/tool_schema_generator.py +123 -0
- letta/services/user_manager.py +0 -99
- letta/settings.py +20 -4
- letta/system.py +5 -1
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.12.0.dev20251009104148.dist-info}/METADATA +3 -5
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.12.0.dev20251009104148.dist-info}/RECORD +146 -135
- letta/agents/temporal/activities/__init__.py +0 -4
- letta/agents/temporal/activities/example_activity.py +0 -7
- letta/agents/temporal/activities/prepare_messages.py +0 -10
- letta/agents/temporal/temporal_agent_workflow.py +0 -56
- letta/agents/temporal/types.py +0 -25
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.12.0.dev20251009104148.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.12.0.dev20251009104148.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.12.0.dev20251009104148.dist-info}/licenses/LICENSE +0 -0
@@ -7,12 +7,13 @@ from pydantic import Field
|
|
7
7
|
from letta.data_sources.redis_client import NoopAsyncRedisClient, get_redis_client
|
8
8
|
from letta.helpers.datetime_helpers import get_utc_time
|
9
9
|
from letta.orm.errors import NoResultFound
|
10
|
-
from letta.schemas.enums import
|
10
|
+
from letta.schemas.enums import RunStatus
|
11
11
|
from letta.schemas.letta_message import LettaMessageUnion
|
12
12
|
from letta.schemas.letta_request import RetrieveStreamRequest
|
13
13
|
from letta.schemas.letta_stop_reason import StopReasonType
|
14
14
|
from letta.schemas.openai.chat_completion_response import UsageStatistics
|
15
15
|
from letta.schemas.run import Run
|
16
|
+
from letta.schemas.run_metrics import RunMetrics
|
16
17
|
from letta.schemas.step import Step
|
17
18
|
from letta.server.rest_api.dependencies import HeaderParams, get_headers, get_letta_server
|
18
19
|
from letta.server.rest_api.redis_stream_manager import redis_sse_stream_generator
|
@@ -22,81 +23,124 @@ from letta.server.rest_api.streaming_response import (
|
|
22
23
|
cancellation_aware_stream_wrapper,
|
23
24
|
)
|
24
25
|
from letta.server.server import SyncServer
|
26
|
+
from letta.services.lettuce import LettuceClient
|
27
|
+
from letta.services.run_manager import RunManager
|
25
28
|
from letta.settings import settings
|
26
29
|
|
27
30
|
router = APIRouter(prefix="/runs", tags=["runs"])
|
28
31
|
|
29
32
|
|
33
|
+
def convert_statuses_to_enum(statuses: Optional[List[str]]) -> Optional[List[RunStatus]]:
|
34
|
+
"""Convert a list of status strings to RunStatus enum values.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
statuses: List of status strings or None
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
List of RunStatus enum values or None if input is None
|
41
|
+
"""
|
42
|
+
if statuses is None:
|
43
|
+
return None
|
44
|
+
return [RunStatus(status) for status in statuses]
|
45
|
+
|
46
|
+
|
30
47
|
@router.get("/", response_model=List[Run], operation_id="list_runs")
|
31
|
-
def list_runs(
|
48
|
+
async def list_runs(
|
32
49
|
server: "SyncServer" = Depends(get_letta_server),
|
33
|
-
|
50
|
+
agent_id: Optional[str] = Query(None, description="The unique identifier of the agent associated with the run."),
|
51
|
+
agent_ids: Optional[List[str]] = Query(
|
52
|
+
None,
|
53
|
+
description="The unique identifiers of the agents associated with the run. Deprecated in favor of agent_id field.",
|
54
|
+
deprecated=True,
|
55
|
+
),
|
56
|
+
statuses: Optional[List[str]] = Query(None, description="Filter runs by status. Can specify multiple statuses."),
|
34
57
|
background: Optional[bool] = Query(None, description="If True, filters for runs that were created in background mode."),
|
35
58
|
stop_reason: Optional[StopReasonType] = Query(None, description="Filter runs by stop reason."),
|
36
|
-
|
37
|
-
|
38
|
-
|
59
|
+
before: Optional[str] = Query(
|
60
|
+
None, description="Run ID cursor for pagination. Returns runs that come before this run ID in the specified sort order"
|
61
|
+
),
|
62
|
+
after: Optional[str] = Query(
|
63
|
+
None, description="Run ID cursor for pagination. Returns runs that come after this run ID in the specified sort order"
|
64
|
+
),
|
65
|
+
limit: Optional[int] = Query(100, description="Maximum number of runs to return"),
|
66
|
+
order: Literal["asc", "desc"] = Query(
|
67
|
+
"desc", description="Sort order for runs by creation time. 'asc' for oldest first, 'desc' for newest first"
|
68
|
+
),
|
69
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
39
70
|
active: bool = Query(False, description="Filter for active runs."),
|
40
71
|
ascending: bool = Query(
|
41
72
|
False,
|
42
|
-
description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default)",
|
73
|
+
description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default). Deprecated in favor of order field.",
|
74
|
+
deprecated=True,
|
43
75
|
),
|
44
76
|
headers: HeaderParams = Depends(get_headers),
|
45
77
|
):
|
46
78
|
"""
|
47
79
|
List all runs.
|
48
80
|
"""
|
49
|
-
actor = server.user_manager.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
81
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
82
|
+
runs_manager = RunManager()
|
83
|
+
|
84
|
+
# Handle backwards compatibility: if statuses not provided but active=True, filter by active statuses
|
85
|
+
if statuses is None and active:
|
86
|
+
statuses = [RunStatus.created, RunStatus.running]
|
87
|
+
|
88
|
+
if agent_id:
|
89
|
+
# NOTE: we are deprecating agent_ids so this will the primary path soon
|
90
|
+
agent_ids = [agent_id]
|
91
|
+
|
92
|
+
# Handle backward compatibility: if ascending is explicitly set, use it; otherwise use order
|
93
|
+
if ascending is not False:
|
94
|
+
# ascending was explicitly set to True
|
95
|
+
sort_ascending = ascending
|
96
|
+
else:
|
97
|
+
# Use the new order parameter
|
98
|
+
sort_ascending = order == "asc"
|
99
|
+
|
100
|
+
# Convert string statuses to RunStatus enum
|
101
|
+
parsed_statuses = convert_statuses_to_enum(statuses)
|
102
|
+
|
103
|
+
runs = await runs_manager.list_runs(
|
104
|
+
actor=actor,
|
105
|
+
agent_ids=agent_ids,
|
106
|
+
statuses=parsed_statuses,
|
107
|
+
limit=limit,
|
108
|
+
before=before,
|
109
|
+
after=after,
|
110
|
+
ascending=sort_ascending,
|
111
|
+
stop_reason=stop_reason,
|
112
|
+
background=background,
|
113
|
+
)
|
71
114
|
return runs
|
72
115
|
|
73
116
|
|
74
117
|
@router.get("/active", response_model=List[Run], operation_id="list_active_runs", deprecated=True)
|
75
|
-
def list_active_runs(
|
118
|
+
async def list_active_runs(
|
76
119
|
server: "SyncServer" = Depends(get_letta_server),
|
77
|
-
|
120
|
+
agent_id: Optional[str] = Query(None, description="The unique identifier of the agent associated with the run."),
|
78
121
|
background: Optional[bool] = Query(None, description="If True, filters for runs that were created in background mode."),
|
79
122
|
headers: HeaderParams = Depends(get_headers),
|
80
123
|
):
|
81
124
|
"""
|
82
125
|
List all active runs.
|
83
126
|
"""
|
84
|
-
actor = server.user_manager.
|
85
|
-
|
86
|
-
active_runs = server.job_manager.list_jobs(actor=actor, statuses=[JobStatus.created, JobStatus.running], job_type=JobType.RUN)
|
87
|
-
active_runs = [Run.from_job(job) for job in active_runs]
|
127
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
128
|
+
runs_manager = RunManager()
|
88
129
|
|
89
|
-
if
|
90
|
-
|
130
|
+
if agent_id:
|
131
|
+
agent_ids = [agent_id]
|
132
|
+
else:
|
133
|
+
agent_ids = None
|
91
134
|
|
92
|
-
|
93
|
-
|
135
|
+
active_runs = await runs_manager.list_runs(
|
136
|
+
actor=actor, statuses=[RunStatus.created, RunStatus.running], agent_ids=agent_ids, background=background
|
137
|
+
)
|
94
138
|
|
95
139
|
return active_runs
|
96
140
|
|
97
141
|
|
98
142
|
@router.get("/{run_id}", response_model=Run, operation_id="retrieve_run")
|
99
|
-
def retrieve_run(
|
143
|
+
async def retrieve_run(
|
100
144
|
run_id: str,
|
101
145
|
headers: HeaderParams = Depends(get_headers),
|
102
146
|
server: "SyncServer" = Depends(get_letta_server),
|
@@ -104,11 +148,29 @@ def retrieve_run(
|
|
104
148
|
"""
|
105
149
|
Get the status of a run.
|
106
150
|
"""
|
107
|
-
actor = server.user_manager.
|
151
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
152
|
+
runs_manager = RunManager()
|
108
153
|
|
109
154
|
try:
|
110
|
-
|
111
|
-
|
155
|
+
run = await runs_manager.get_run_by_id(run_id=run_id, actor=actor)
|
156
|
+
|
157
|
+
use_lettuce = run.metadata and run.metadata.get("lettuce")
|
158
|
+
if use_lettuce and run.status not in [RunStatus.completed, RunStatus.failed, RunStatus.cancelled]:
|
159
|
+
lettuce_client = await LettuceClient.create()
|
160
|
+
status = await lettuce_client.get_status()
|
161
|
+
|
162
|
+
# Map the status to our enum
|
163
|
+
run_status = run.status
|
164
|
+
if status == "RUNNING":
|
165
|
+
run_status = RunStatus.running
|
166
|
+
elif status == "COMPLETED":
|
167
|
+
run_status = RunStatus.completed
|
168
|
+
elif status == "FAILED":
|
169
|
+
run_status = RunStatus.failed
|
170
|
+
elif status == "CANCELLED":
|
171
|
+
run_status = RunStatus.cancelled
|
172
|
+
run.status = run_status
|
173
|
+
return run
|
112
174
|
except NoResultFound:
|
113
175
|
raise HTTPException(status_code=404, detail="Run not found")
|
114
176
|
|
@@ -137,26 +199,15 @@ async def list_run_messages(
|
|
137
199
|
order: Literal["asc", "desc"] = Query(
|
138
200
|
"asc", description="Sort order for messages by creation time. 'asc' for oldest first, 'desc' for newest first"
|
139
201
|
),
|
202
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
140
203
|
):
|
141
204
|
"""Get response messages associated with a run."""
|
142
205
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
143
|
-
|
144
|
-
try:
|
145
|
-
messages = server.job_manager.get_run_messages(
|
146
|
-
run_id=run_id,
|
147
|
-
actor=actor,
|
148
|
-
limit=limit,
|
149
|
-
before=before,
|
150
|
-
after=after,
|
151
|
-
ascending=(order == "asc"),
|
152
|
-
)
|
153
|
-
return messages
|
154
|
-
except NoResultFound as e:
|
155
|
-
raise HTTPException(status_code=404, detail=str(e))
|
206
|
+
return await server.run_manager.get_run_messages(run_id=run_id, actor=actor, before=before, after=after, limit=limit, order=order)
|
156
207
|
|
157
208
|
|
158
209
|
@router.get("/{run_id}/usage", response_model=UsageStatistics, operation_id="retrieve_run_usage")
|
159
|
-
def retrieve_run_usage(
|
210
|
+
async def retrieve_run_usage(
|
160
211
|
run_id: str,
|
161
212
|
headers: HeaderParams = Depends(get_headers),
|
162
213
|
server: "SyncServer" = Depends(get_letta_server),
|
@@ -164,15 +215,33 @@ def retrieve_run_usage(
|
|
164
215
|
"""
|
165
216
|
Get usage statistics for a run.
|
166
217
|
"""
|
167
|
-
actor = server.user_manager.
|
218
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
219
|
+
runs_manager = RunManager()
|
168
220
|
|
169
221
|
try:
|
170
|
-
usage =
|
222
|
+
usage = await runs_manager.get_run_usage(run_id=run_id, actor=actor)
|
171
223
|
return usage
|
172
224
|
except NoResultFound:
|
173
225
|
raise HTTPException(status_code=404, detail=f"Run '{run_id}' not found")
|
174
226
|
|
175
227
|
|
228
|
+
@router.get("/{run_id}/metrics", response_model=RunMetrics, operation_id="retrieve_metrics_for_run")
|
229
|
+
async def retrieve_metrics_for_run(
|
230
|
+
run_id: str,
|
231
|
+
headers: HeaderParams = Depends(get_headers),
|
232
|
+
server: "SyncServer" = Depends(get_letta_server),
|
233
|
+
):
|
234
|
+
"""
|
235
|
+
Get run metrics by run ID.
|
236
|
+
"""
|
237
|
+
try:
|
238
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
239
|
+
runs_manager = RunManager()
|
240
|
+
return await runs_manager.get_run_metrics_async(run_id=run_id, actor=actor)
|
241
|
+
except NoResultFound:
|
242
|
+
raise HTTPException(status_code=404, detail="Run metrics not found")
|
243
|
+
|
244
|
+
|
176
245
|
@router.get(
|
177
246
|
"/{run_id}/steps",
|
178
247
|
response_model=List[Step],
|
@@ -185,40 +254,25 @@ async def list_run_steps(
|
|
185
254
|
before: Optional[str] = Query(None, description="Cursor for pagination"),
|
186
255
|
after: Optional[str] = Query(None, description="Cursor for pagination"),
|
187
256
|
limit: Optional[int] = Query(100, description="Maximum number of messages to return"),
|
188
|
-
order:
|
189
|
-
"desc", description="Sort order
|
257
|
+
order: Literal["asc", "desc"] = Query(
|
258
|
+
"desc", description="Sort order for steps by creation time. 'asc' for oldest first, 'desc' for newest first"
|
190
259
|
),
|
260
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
191
261
|
):
|
192
262
|
"""
|
193
|
-
Get
|
194
|
-
|
195
|
-
Args:
|
196
|
-
run_id: ID of the run
|
197
|
-
before: A cursor for use in pagination. `before` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with obj_foo, your subsequent call can include before=obj_foo in order to fetch the previous page of the list.
|
198
|
-
after: A cursor for use in pagination. `after` is an object ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with obj_foo, your subsequent call can include after=obj_foo in order to fetch the next page of the list.
|
199
|
-
limit: Maximum number of steps to return
|
200
|
-
order: Sort order by the created_at timestamp of the objects. asc for ascending order and desc for descending order.
|
201
|
-
|
202
|
-
Returns:
|
203
|
-
A list of steps associated with the run.
|
263
|
+
Get steps associated with a run with filtering options.
|
204
264
|
"""
|
205
|
-
if order not in ["asc", "desc"]:
|
206
|
-
raise HTTPException(status_code=400, detail="Order must be 'asc' or 'desc'")
|
207
|
-
|
208
265
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
266
|
+
runs_manager = RunManager()
|
209
267
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
)
|
219
|
-
return steps
|
220
|
-
except NoResultFound as e:
|
221
|
-
raise HTTPException(status_code=404, detail=str(e))
|
268
|
+
return await runs_manager.get_run_steps(
|
269
|
+
run_id=run_id,
|
270
|
+
actor=actor,
|
271
|
+
limit=limit,
|
272
|
+
before=before,
|
273
|
+
after=after,
|
274
|
+
ascending=(order == "asc"),
|
275
|
+
)
|
222
276
|
|
223
277
|
|
224
278
|
@router.delete("/{run_id}", response_model=Run, operation_id="delete_run")
|
@@ -231,12 +285,8 @@ async def delete_run(
|
|
231
285
|
Delete a run by its run_id.
|
232
286
|
"""
|
233
287
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
234
|
-
|
235
|
-
|
236
|
-
job = await server.job_manager.delete_job_by_id_async(job_id=run_id, actor=actor)
|
237
|
-
return Run.from_job(job)
|
238
|
-
except NoResultFound:
|
239
|
-
raise HTTPException(status_code=404, detail="Run not found")
|
288
|
+
runs_manager = RunManager()
|
289
|
+
return await runs_manager.delete_run_by_id(run_id=run_id, actor=actor)
|
240
290
|
|
241
291
|
|
242
292
|
@router.post(
|
@@ -278,14 +328,14 @@ async def retrieve_stream(
|
|
278
328
|
server: "SyncServer" = Depends(get_letta_server),
|
279
329
|
):
|
280
330
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
331
|
+
runs_manager = RunManager()
|
332
|
+
|
281
333
|
try:
|
282
|
-
|
334
|
+
run = await runs_manager.get_run_by_id(run_id=run_id, actor=actor)
|
283
335
|
except NoResultFound:
|
284
336
|
raise HTTPException(status_code=404, detail="Run not found")
|
285
337
|
|
286
|
-
|
287
|
-
|
288
|
-
if "background" not in run.metadata or not run.metadata["background"]:
|
338
|
+
if not run.background:
|
289
339
|
raise HTTPException(status_code=400, detail="Run was not created in background mode, so it cannot be retrieved.")
|
290
340
|
|
291
341
|
if run.created_at < get_utc_time() - timedelta(hours=3):
|
@@ -314,8 +364,8 @@ async def retrieve_stream(
|
|
314
364
|
if settings.enable_cancellation_aware_streaming:
|
315
365
|
stream = cancellation_aware_stream_wrapper(
|
316
366
|
stream_generator=stream,
|
317
|
-
|
318
|
-
|
367
|
+
run_manager=server.run_manager,
|
368
|
+
run_id=run_id,
|
319
369
|
actor=actor,
|
320
370
|
)
|
321
371
|
|
@@ -2,8 +2,9 @@ import os
|
|
2
2
|
import shutil
|
3
3
|
from typing import List, Optional
|
4
4
|
|
5
|
-
from fastapi import APIRouter, Depends,
|
5
|
+
from fastapi import APIRouter, Depends, Query
|
6
6
|
|
7
|
+
from letta.errors import LettaInvalidArgumentError
|
7
8
|
from letta.log import get_logger
|
8
9
|
from letta.schemas.enums import SandboxType
|
9
10
|
from letta.schemas.environment_variables import (
|
@@ -68,9 +69,8 @@ async def create_custom_local_sandbox_config(
|
|
68
69
|
"""
|
69
70
|
# Ensure the incoming config is of type LOCAL
|
70
71
|
if local_sandbox_config.type != SandboxType.LOCAL:
|
71
|
-
raise
|
72
|
-
|
73
|
-
detail=f"Provided config must be of type '{SandboxType.LOCAL.value}'.",
|
72
|
+
raise LettaInvalidArgumentError(
|
73
|
+
f"Provided config must be of type '{SandboxType.LOCAL.value}'.", argument_name="local_sandbox_config.type"
|
74
74
|
)
|
75
75
|
|
76
76
|
# Retrieve the user (actor)
|
@@ -138,25 +138,16 @@ async def force_recreate_local_sandbox_venv(
|
|
138
138
|
|
139
139
|
# Check if venv exists, and delete if necessary
|
140
140
|
if os.path.isdir(venv_path):
|
141
|
-
|
142
|
-
|
143
|
-
logger.info(f"Deleted existing virtual environment at: {venv_path}")
|
144
|
-
except Exception as e:
|
145
|
-
raise HTTPException(status_code=500, detail=f"Failed to delete existing venv: {e}")
|
141
|
+
shutil.rmtree(venv_path)
|
142
|
+
logger.info(f"Deleted existing virtual environment at: {venv_path}")
|
146
143
|
|
147
144
|
# Recreate the virtual environment
|
148
|
-
|
149
|
-
|
150
|
-
logger.info(f"Successfully recreated virtual environment at: {venv_path}")
|
151
|
-
except Exception as e:
|
152
|
-
raise HTTPException(status_code=500, detail=f"Failed to recreate venv: {e}")
|
145
|
+
create_venv_for_local_sandbox(sandbox_dir_path=sandbox_dir, venv_path=str(venv_path), env=os.environ.copy(), force_recreate=True)
|
146
|
+
logger.info(f"Successfully recreated virtual environment at: {venv_path}")
|
153
147
|
|
154
148
|
# Install pip requirements
|
155
|
-
|
156
|
-
|
157
|
-
logger.info(f"Successfully installed pip requirements for venv at: {venv_path}")
|
158
|
-
except Exception as e:
|
159
|
-
raise HTTPException(status_code=500, detail=f"Failed to install pip requirements: {e}")
|
149
|
+
install_pip_requirements_for_sandbox(local_configs=local_configs, env=os.environ.copy())
|
150
|
+
logger.info(f"Successfully installed pip requirements for venv at: {venv_path}")
|
160
151
|
|
161
152
|
return sbx_config
|
162
153
|
|