letta-nightly 0.11.7.dev20251007104119__py3-none-any.whl → 0.11.7.dev20251008104128__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/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 +899 -0
- letta/agents/voice_agent.py +2 -6
- letta/constants.py +8 -4
- 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 +126 -16
- letta/llm_api/bedrock_client.py +4 -2
- letta/llm_api/deepseek_client.py +4 -1
- letta/llm_api/google_vertex_client.py +123 -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 +1 -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/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 +504 -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/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 +86 -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 +126 -85
- 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/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 +85 -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 +301 -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_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/METADATA +3 -5
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/RECORD +140 -132
- 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.11.7.dev20251008104128.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20251007104119.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ 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
|
@@ -22,81 +22,124 @@ from letta.server.rest_api.streaming_response import (
|
|
22
22
|
cancellation_aware_stream_wrapper,
|
23
23
|
)
|
24
24
|
from letta.server.server import SyncServer
|
25
|
+
from letta.services.lettuce import LettuceClient
|
26
|
+
from letta.services.run_manager import RunManager
|
25
27
|
from letta.settings import settings
|
26
28
|
|
27
29
|
router = APIRouter(prefix="/runs", tags=["runs"])
|
28
30
|
|
29
31
|
|
32
|
+
def convert_statuses_to_enum(statuses: Optional[List[str]]) -> Optional[List[RunStatus]]:
|
33
|
+
"""Convert a list of status strings to RunStatus enum values.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
statuses: List of status strings or None
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
List of RunStatus enum values or None if input is None
|
40
|
+
"""
|
41
|
+
if statuses is None:
|
42
|
+
return None
|
43
|
+
return [RunStatus(status) for status in statuses]
|
44
|
+
|
45
|
+
|
30
46
|
@router.get("/", response_model=List[Run], operation_id="list_runs")
|
31
|
-
def list_runs(
|
47
|
+
async def list_runs(
|
32
48
|
server: "SyncServer" = Depends(get_letta_server),
|
33
|
-
|
49
|
+
agent_id: Optional[str] = Query(None, description="The unique identifier of the agent associated with the run."),
|
50
|
+
agent_ids: Optional[List[str]] = Query(
|
51
|
+
None,
|
52
|
+
description="The unique identifiers of the agents associated with the run. Deprecated in favor of agent_id field.",
|
53
|
+
deprecated=True,
|
54
|
+
),
|
55
|
+
statuses: Optional[List[str]] = Query(None, description="Filter runs by status. Can specify multiple statuses."),
|
34
56
|
background: Optional[bool] = Query(None, description="If True, filters for runs that were created in background mode."),
|
35
57
|
stop_reason: Optional[StopReasonType] = Query(None, description="Filter runs by stop reason."),
|
36
|
-
|
37
|
-
|
38
|
-
|
58
|
+
before: Optional[str] = Query(
|
59
|
+
None, description="Run ID cursor for pagination. Returns runs that come before this run ID in the specified sort order"
|
60
|
+
),
|
61
|
+
after: Optional[str] = Query(
|
62
|
+
None, description="Run ID cursor for pagination. Returns runs that come after this run ID in the specified sort order"
|
63
|
+
),
|
64
|
+
limit: Optional[int] = Query(100, description="Maximum number of runs to return"),
|
65
|
+
order: Literal["asc", "desc"] = Query(
|
66
|
+
"desc", description="Sort order for runs by creation time. 'asc' for oldest first, 'desc' for newest first"
|
67
|
+
),
|
68
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
39
69
|
active: bool = Query(False, description="Filter for active runs."),
|
40
70
|
ascending: bool = Query(
|
41
71
|
False,
|
42
|
-
description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default)",
|
72
|
+
description="Whether to sort agents oldest to newest (True) or newest to oldest (False, default). Deprecated in favor of order field.",
|
73
|
+
deprecated=True,
|
43
74
|
),
|
44
75
|
headers: HeaderParams = Depends(get_headers),
|
45
76
|
):
|
46
77
|
"""
|
47
78
|
List all runs.
|
48
79
|
"""
|
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
|
-
|
80
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
81
|
+
runs_manager = RunManager()
|
82
|
+
|
83
|
+
# Handle backwards compatibility: if statuses not provided but active=True, filter by active statuses
|
84
|
+
if statuses is None and active:
|
85
|
+
statuses = [RunStatus.created, RunStatus.running]
|
86
|
+
|
87
|
+
if agent_id:
|
88
|
+
# NOTE: we are deprecating agent_ids so this will the primary path soon
|
89
|
+
agent_ids = [agent_id]
|
90
|
+
|
91
|
+
# Handle backward compatibility: if ascending is explicitly set, use it; otherwise use order
|
92
|
+
if ascending is not False:
|
93
|
+
# ascending was explicitly set to True
|
94
|
+
sort_ascending = ascending
|
95
|
+
else:
|
96
|
+
# Use the new order parameter
|
97
|
+
sort_ascending = order == "asc"
|
98
|
+
|
99
|
+
# Convert string statuses to RunStatus enum
|
100
|
+
parsed_statuses = convert_statuses_to_enum(statuses)
|
101
|
+
|
102
|
+
runs = await runs_manager.list_runs(
|
103
|
+
actor=actor,
|
104
|
+
agent_ids=agent_ids,
|
105
|
+
statuses=parsed_statuses,
|
106
|
+
limit=limit,
|
107
|
+
before=before,
|
108
|
+
after=after,
|
109
|
+
ascending=sort_ascending,
|
110
|
+
stop_reason=stop_reason,
|
111
|
+
background=background,
|
112
|
+
)
|
71
113
|
return runs
|
72
114
|
|
73
115
|
|
74
116
|
@router.get("/active", response_model=List[Run], operation_id="list_active_runs", deprecated=True)
|
75
|
-
def list_active_runs(
|
117
|
+
async def list_active_runs(
|
76
118
|
server: "SyncServer" = Depends(get_letta_server),
|
77
|
-
|
119
|
+
agent_id: Optional[str] = Query(None, description="The unique identifier of the agent associated with the run."),
|
78
120
|
background: Optional[bool] = Query(None, description="If True, filters for runs that were created in background mode."),
|
79
121
|
headers: HeaderParams = Depends(get_headers),
|
80
122
|
):
|
81
123
|
"""
|
82
124
|
List all active runs.
|
83
125
|
"""
|
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]
|
126
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
127
|
+
runs_manager = RunManager()
|
88
128
|
|
89
|
-
if
|
90
|
-
|
129
|
+
if agent_id:
|
130
|
+
agent_ids = [agent_id]
|
131
|
+
else:
|
132
|
+
agent_ids = None
|
91
133
|
|
92
|
-
|
93
|
-
|
134
|
+
active_runs = await runs_manager.list_runs(
|
135
|
+
actor=actor, statuses=[RunStatus.created, RunStatus.running], agent_ids=agent_ids, background=background
|
136
|
+
)
|
94
137
|
|
95
138
|
return active_runs
|
96
139
|
|
97
140
|
|
98
141
|
@router.get("/{run_id}", response_model=Run, operation_id="retrieve_run")
|
99
|
-
def retrieve_run(
|
142
|
+
async def retrieve_run(
|
100
143
|
run_id: str,
|
101
144
|
headers: HeaderParams = Depends(get_headers),
|
102
145
|
server: "SyncServer" = Depends(get_letta_server),
|
@@ -104,11 +147,29 @@ def retrieve_run(
|
|
104
147
|
"""
|
105
148
|
Get the status of a run.
|
106
149
|
"""
|
107
|
-
actor = server.user_manager.
|
150
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
151
|
+
runs_manager = RunManager()
|
108
152
|
|
109
153
|
try:
|
110
|
-
|
111
|
-
|
154
|
+
run = await runs_manager.get_run_by_id(run_id=run_id, actor=actor)
|
155
|
+
|
156
|
+
use_lettuce = run.metadata and run.metadata.get("lettuce")
|
157
|
+
if use_lettuce and run.status not in [RunStatus.completed, RunStatus.failed, RunStatus.cancelled]:
|
158
|
+
lettuce_client = await LettuceClient.create()
|
159
|
+
status = await lettuce_client.get_status()
|
160
|
+
|
161
|
+
# Map the status to our enum
|
162
|
+
run_status = run.status
|
163
|
+
if status == "RUNNING":
|
164
|
+
run_status = RunStatus.running
|
165
|
+
elif status == "COMPLETED":
|
166
|
+
run_status = RunStatus.completed
|
167
|
+
elif status == "FAILED":
|
168
|
+
run_status = RunStatus.failed
|
169
|
+
elif status == "CANCELLED":
|
170
|
+
run_status = RunStatus.cancelled
|
171
|
+
run.status = run_status
|
172
|
+
return run
|
112
173
|
except NoResultFound:
|
113
174
|
raise HTTPException(status_code=404, detail="Run not found")
|
114
175
|
|
@@ -137,26 +198,15 @@ async def list_run_messages(
|
|
137
198
|
order: Literal["asc", "desc"] = Query(
|
138
199
|
"asc", description="Sort order for messages by creation time. 'asc' for oldest first, 'desc' for newest first"
|
139
200
|
),
|
201
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
140
202
|
):
|
141
203
|
"""Get response messages associated with a run."""
|
142
204
|
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))
|
205
|
+
return await server.run_manager.get_run_messages(run_id=run_id, actor=actor, before=before, after=after, limit=limit, order=order)
|
156
206
|
|
157
207
|
|
158
208
|
@router.get("/{run_id}/usage", response_model=UsageStatistics, operation_id="retrieve_run_usage")
|
159
|
-
def retrieve_run_usage(
|
209
|
+
async def retrieve_run_usage(
|
160
210
|
run_id: str,
|
161
211
|
headers: HeaderParams = Depends(get_headers),
|
162
212
|
server: "SyncServer" = Depends(get_letta_server),
|
@@ -164,10 +214,11 @@ def retrieve_run_usage(
|
|
164
214
|
"""
|
165
215
|
Get usage statistics for a run.
|
166
216
|
"""
|
167
|
-
actor = server.user_manager.
|
217
|
+
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
218
|
+
runs_manager = RunManager()
|
168
219
|
|
169
220
|
try:
|
170
|
-
usage =
|
221
|
+
usage = await runs_manager.get_run_usage(run_id=run_id, actor=actor)
|
171
222
|
return usage
|
172
223
|
except NoResultFound:
|
173
224
|
raise HTTPException(status_code=404, detail=f"Run '{run_id}' not found")
|
@@ -185,31 +236,20 @@ async def list_run_steps(
|
|
185
236
|
before: Optional[str] = Query(None, description="Cursor for pagination"),
|
186
237
|
after: Optional[str] = Query(None, description="Cursor for pagination"),
|
187
238
|
limit: Optional[int] = Query(100, description="Maximum number of messages to return"),
|
188
|
-
order:
|
189
|
-
"desc", description="Sort order
|
239
|
+
order: Literal["asc", "desc"] = Query(
|
240
|
+
"desc", description="Sort order for steps by creation time. 'asc' for oldest first, 'desc' for newest first"
|
190
241
|
),
|
242
|
+
order_by: Literal["created_at"] = Query("created_at", description="Field to sort by"),
|
191
243
|
):
|
192
244
|
"""
|
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.
|
245
|
+
Get steps associated with a run with filtering options.
|
204
246
|
"""
|
205
|
-
if order not in ["asc", "desc"]:
|
206
|
-
raise HTTPException(status_code=400, detail="Order must be 'asc' or 'desc'")
|
207
|
-
|
208
247
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
248
|
+
runs_manager = RunManager()
|
209
249
|
|
210
250
|
try:
|
211
|
-
steps =
|
212
|
-
|
251
|
+
steps = await runs_manager.get_run_steps(
|
252
|
+
run_id=run_id,
|
213
253
|
actor=actor,
|
214
254
|
limit=limit,
|
215
255
|
before=before,
|
@@ -231,10 +271,11 @@ async def delete_run(
|
|
231
271
|
Delete a run by its run_id.
|
232
272
|
"""
|
233
273
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
274
|
+
runs_manager = RunManager()
|
234
275
|
|
235
276
|
try:
|
236
|
-
|
237
|
-
return
|
277
|
+
run = await runs_manager.delete_run_by_id(run_id=run_id, actor=actor)
|
278
|
+
return run
|
238
279
|
except NoResultFound:
|
239
280
|
raise HTTPException(status_code=404, detail="Run not found")
|
240
281
|
|
@@ -278,14 +319,14 @@ async def retrieve_stream(
|
|
278
319
|
server: "SyncServer" = Depends(get_letta_server),
|
279
320
|
):
|
280
321
|
actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id)
|
322
|
+
runs_manager = RunManager()
|
323
|
+
|
281
324
|
try:
|
282
|
-
|
325
|
+
run = await runs_manager.get_run_by_id(run_id=run_id, actor=actor)
|
283
326
|
except NoResultFound:
|
284
327
|
raise HTTPException(status_code=404, detail="Run not found")
|
285
328
|
|
286
|
-
|
287
|
-
|
288
|
-
if "background" not in run.metadata or not run.metadata["background"]:
|
329
|
+
if not run.background:
|
289
330
|
raise HTTPException(status_code=400, detail="Run was not created in background mode, so it cannot be retrieved.")
|
290
331
|
|
291
332
|
if run.created_at < get_utc_time() - timedelta(hours=3):
|
@@ -314,8 +355,8 @@ async def retrieve_stream(
|
|
314
355
|
if settings.enable_cancellation_aware_streaming:
|
315
356
|
stream = cancellation_aware_stream_wrapper(
|
316
357
|
stream_generator=stream,
|
317
|
-
|
318
|
-
|
358
|
+
run_manager=server.run_manager,
|
359
|
+
run_id=run_id,
|
319
360
|
actor=actor,
|
320
361
|
)
|
321
362
|
|
@@ -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
|
|