@@ -294,7 +294,7 @@ async def start_agent(
294
294
body : AgentStartRequest = Body (...),
295
295
user_id : str = Depends (get_current_user_id_from_jwt )
296
296
):
297
- """Start an agent for a specific thread in the background. """
297
+ """Start an agent for a specific thread in the background"""
298
298
structlog .contextvars .bind_contextvars (
299
299
thread_id = thread_id ,
300
300
)
@@ -321,7 +321,9 @@ async def start_agent(
321
321
client = await db .client
322
322
323
323
await verify_thread_access (client , thread_id , user_id )
324
+
324
325
thread_result = await client .table ('threads' ).select ('project_id' , 'account_id' , 'metadata' ).eq ('thread_id' , thread_id ).execute ()
326
+
325
327
if not thread_result .data :
326
328
raise HTTPException (status_code = 404 , detail = "Thread not found" )
327
329
thread_data = thread_result .data [0 ]
@@ -349,7 +351,7 @@ async def start_agent(
349
351
logger .info (f"[AGENT LOAD] Agent loading flow:" )
350
352
logger .info (f" - body.agent_id: { body .agent_id } " )
351
353
logger .info (f" - effective_agent_id: { effective_agent_id } " )
352
-
354
+
353
355
if effective_agent_id :
354
356
logger .info (f"[AGENT LOAD] Querying for agent: { effective_agent_id } " )
355
357
# Get agent
@@ -390,7 +392,7 @@ async def start_agent(
390
392
source = "request" if body .agent_id else "fallback"
391
393
else :
392
394
logger .info (f"[AGENT LOAD] No effective_agent_id, will try default agent" )
393
-
395
+
394
396
if not agent_config :
395
397
logger .info (f"[AGENT LOAD] No agent config yet, querying for default agent" )
396
398
default_agent_result = await client .table ('agents' ).select ('*' ).eq ('account_id' , account_id ).eq ('is_default' , True ).execute ()
@@ -424,22 +426,25 @@ async def start_agent(
424
426
logger .info (f"Using default agent: { agent_config ['name' ]} ({ agent_config ['agent_id' ]} ) - no version data" )
425
427
else :
426
428
logger .warning (f"[AGENT LOAD] No default agent found for account { account_id } " )
427
-
429
+
428
430
logger .info (f"[AGENT LOAD] Final agent_config: { agent_config is not None } " )
429
431
if agent_config :
430
432
logger .info (f"[AGENT LOAD] Agent config keys: { list (agent_config .keys ())} " )
431
433
logger .info (f"Using agent { agent_config ['agent_id' ]} for this agent run (thread remains agent-agnostic)" )
432
434
433
435
can_use , model_message , allowed_models = await can_use_model (client , account_id , model_name )
436
+
434
437
if not can_use :
435
438
raise HTTPException (status_code = 403 , detail = {"message" : model_message , "allowed_models" : allowed_models })
436
439
437
440
can_run , message , subscription = await check_billing_status (client , account_id )
441
+
438
442
if not can_run :
439
443
raise HTTPException (status_code = 402 , detail = {"message" : message , "subscription" : subscription })
440
444
441
445
# Check agent run limit (maximum parallel runs in past 24 hours)
442
446
limit_check = await check_agent_run_limit (client , account_id )
447
+
443
448
if not limit_check ['can_start' ]:
444
449
error_detail = {
445
450
"message" : f"Maximum of { config .MAX_PARALLEL_AGENT_RUNS } parallel agent runs allowed within 24 hours. You currently have { limit_check ['running_count' ]} running." ,
@@ -450,23 +455,6 @@ async def start_agent(
450
455
logger .warning (f"Agent run limit exceeded for account { account_id } : { limit_check ['running_count' ]} running agents" )
451
456
raise HTTPException (status_code = 429 , detail = error_detail )
452
457
453
- try :
454
- project_result = await client .table ('projects' ).select ('*' ).eq ('project_id' , project_id ).execute ()
455
- if not project_result .data :
456
- raise HTTPException (status_code = 404 , detail = "Project not found" )
457
-
458
- project_data = project_result .data [0 ]
459
- sandbox_info = project_data .get ('sandbox' , {})
460
- if not sandbox_info .get ('id' ):
461
- raise HTTPException (status_code = 404 , detail = "No sandbox found for this project" )
462
-
463
- sandbox_id = sandbox_info ['id' ]
464
- sandbox = await get_or_start_sandbox (sandbox_id )
465
- logger .info (f"Successfully started sandbox { sandbox_id } for project { project_id } " )
466
- except Exception as e :
467
- logger .error (f"Failed to start sandbox for project { project_id } : { str (e )} " )
468
- raise HTTPException (status_code = 500 , detail = f"Failed to initialize sandbox: { str (e )} " )
469
-
470
458
agent_run = await client .table ('agent_runs' ).insert ({
471
459
"thread_id" : thread_id , "status" : "running" ,
472
460
"started_at" : datetime .now (timezone .utc ).isoformat (),
@@ -479,6 +467,7 @@ async def start_agent(
479
467
"enable_context_manager" : body .enable_context_manager
480
468
}
481
469
}).execute ()
470
+
482
471
agent_run_id = agent_run .data [0 ]['id' ]
483
472
structlog .contextvars .bind_contextvars (
484
473
agent_run_id = agent_run_id ,
@@ -1084,47 +1073,56 @@ async def initiate_agent_with_files(
1084
1073
project_id = project .data [0 ]['project_id' ]
1085
1074
logger .info (f"Created new project: { project_id } " )
1086
1075
1087
- # 2. Create Sandbox
1076
+ # 2. Create Sandbox (lazy): only create now if files were uploaded and need the
1077
+ # sandbox immediately. Otherwise leave sandbox creation to `_ensure_sandbox()`
1078
+ # which will create it lazily when tools require it.
1088
1079
sandbox_id = None
1089
- try :
1090
- sandbox_pass = str (uuid .uuid4 ())
1091
- sandbox = await create_sandbox (sandbox_pass , project_id )
1092
- sandbox_id = sandbox .id
1093
- logger .info (f"Created new sandbox { sandbox_id } for project { project_id } " )
1094
-
1095
- # Get preview links
1096
- vnc_link = await sandbox .get_preview_link (6080 )
1097
- website_link = await sandbox .get_preview_link (8080 )
1098
- vnc_url = vnc_link .url if hasattr (vnc_link , 'url' ) else str (vnc_link ).split ("url='" )[1 ].split ("'" )[0 ]
1099
- website_url = website_link .url if hasattr (website_link , 'url' ) else str (website_link ).split ("url='" )[1 ].split ("'" )[0 ]
1100
- token = None
1101
- if hasattr (vnc_link , 'token' ):
1102
- token = vnc_link .token
1103
- elif "token='" in str (vnc_link ):
1104
- token = str (vnc_link ).split ("token='" )[1 ].split ("'" )[0 ]
1105
- except Exception as e :
1106
- logger .error (f"Error creating sandbox: { str (e )} " )
1107
- await client .table ('projects' ).delete ().eq ('project_id' , project_id ).execute ()
1108
- if sandbox_id :
1109
- try : await delete_sandbox (sandbox_id )
1110
- except Exception as e : pass
1111
- raise Exception ("Failed to create sandbox" )
1112
-
1080
+ sandbox = None
1081
+ sandbox_pass = None
1082
+ vnc_url = None
1083
+ website_url = None
1084
+ token = None
1113
1085
1114
- # Update project with sandbox info
1115
- update_result = await client .table ('projects' ).update ({
1116
- 'sandbox' : {
1117
- 'id' : sandbox_id , 'pass' : sandbox_pass , 'vnc_preview' : vnc_url ,
1118
- 'sandbox_url' : website_url , 'token' : token
1119
- }
1120
- }).eq ('project_id' , project_id ).execute ()
1086
+ if files :
1087
+ try :
1088
+ sandbox_pass = str (uuid .uuid4 ())
1089
+ sandbox = await create_sandbox (sandbox_pass , project_id )
1090
+ sandbox_id = sandbox .id
1091
+ logger .info (f"Created new sandbox { sandbox_id } for project { project_id } " )
1092
+
1093
+ # Get preview links
1094
+ vnc_link = await sandbox .get_preview_link (6080 )
1095
+ website_link = await sandbox .get_preview_link (8080 )
1096
+ vnc_url = vnc_link .url if hasattr (vnc_link , 'url' ) else str (vnc_link ).split ("url='" )[1 ].split ("'" )[0 ]
1097
+ website_url = website_link .url if hasattr (website_link , 'url' ) else str (website_link ).split ("url='" )[1 ].split ("'" )[0 ]
1098
+ token = None
1099
+ if hasattr (vnc_link , 'token' ):
1100
+ token = vnc_link .token
1101
+ elif "token='" in str (vnc_link ):
1102
+ token = str (vnc_link ).split ("token='" )[1 ].split ("'" )[0 ]
1103
+
1104
+ # Update project with sandbox info
1105
+ update_result = await client .table ('projects' ).update ({
1106
+ 'sandbox' : {
1107
+ 'id' : sandbox_id , 'pass' : sandbox_pass , 'vnc_preview' : vnc_url ,
1108
+ 'sandbox_url' : website_url , 'token' : token
1109
+ }
1110
+ }).eq ('project_id' , project_id ).execute ()
1121
1111
1122
- if not update_result .data :
1123
- logger .error (f"Failed to update project { project_id } with new sandbox { sandbox_id } " )
1124
- if sandbox_id :
1125
- try : await delete_sandbox (sandbox_id )
1126
- except Exception as e : logger .error (f"Error deleting sandbox: { str (e )} " )
1127
- raise Exception ("Database update failed" )
1112
+ if not update_result .data :
1113
+ logger .error (f"Failed to update project { project_id } with new sandbox { sandbox_id } " )
1114
+ if sandbox_id :
1115
+ try : await delete_sandbox (sandbox_id )
1116
+ except Exception as e : logger .error (f"Error deleting sandbox: { str (e )} " )
1117
+ raise Exception ("Database update failed" )
1118
+ except Exception as e :
1119
+ logger .error (f"Error creating sandbox: { str (e )} " )
1120
+ await client .table ('projects' ).delete ().eq ('project_id' , project_id ).execute ()
1121
+ if sandbox_id :
1122
+ try : await delete_sandbox (sandbox_id )
1123
+ except Exception :
1124
+ pass
1125
+ raise Exception ("Failed to create sandbox" )
1128
1126
1129
1127
# 3. Create Thread
1130
1128
thread_data = {
0 commit comments