1515from fastapi .responses import StreamingResponse
1616
1717from ..models import ExecRequest , ExecResponse
18- from ..models .errors import ErrorResponse , ValidationError , ServiceUnavailableError
18+ from ..models .errors import (
19+ CodeInterpreterException ,
20+ ErrorResponse ,
21+ ErrorType ,
22+ ValidationError ,
23+ ServiceUnavailableError ,
24+ )
1925from ..services .orchestrator import ExecutionOrchestrator
2026from ..dependencies .services import (
2127 SessionServiceDep ,
@@ -106,6 +112,14 @@ async def _execute() -> ExecResponse:
106112 )
107113 return response
108114 except asyncio .TimeoutError :
115+ if execution_task .done ():
116+ response = await execution_task
117+ logger .info (
118+ "Code execution completed" ,
119+ request_id = request_id ,
120+ session_id = response .session_id ,
121+ )
122+ return response
109123 # Fall through to streamed keepalives for genuinely long-running work.
110124 pass
111125 except (ValidationError , ServiceUnavailableError ):
@@ -119,6 +133,12 @@ async def _stream_response():
119133 whitespace is ignored by JSON parsers, so this is transparent
120134 to clients.
121135 """
136+ # The endpoint already spent one interval deciding whether to switch to
137+ # streaming. Emit a first keepalive immediately so long-running
138+ # requests stay under client-side socket timeout thresholds.
139+ if not execution_task .done ():
140+ yield b" "
141+
122142 # Send keepalive spaces while execution is running
123143 while not execution_task .done ():
124144 try :
@@ -127,17 +147,24 @@ async def _stream_response():
127147 )
128148 except asyncio .TimeoutError :
129149 # Execution still running — send keepalive space
130- yield b" "
150+ if not execution_task .done ():
151+ yield b" "
152+ except Exception :
153+ # Task raised an exception — it will be handled below.
154+ break
131155
132156 # Ensure the task is complete
133157 try :
134158 response = await execution_task
135159 except Exception as err :
136160 # Once the streaming response has started, surface failures as a JSON
137161 # error payload instead of raising after headers have been sent.
162+ error_type = ErrorType .INTERNAL_SERVER
163+ if isinstance (err , CodeInterpreterException ):
164+ error_type = err .error_type
138165 error_resp = ErrorResponse (
139166 error = str (err ),
140- error_type = "execution" ,
167+ error_type = error_type ,
141168 )
142169 yield error_resp .model_dump_json ().encode ()
143170 return
0 commit comments