@@ -134,9 +134,24 @@ async def stream_generate_and_run(user_query: str, model_name: str):
134134 image_tag = "robot-test-runner:latest"
135135 dockerfile_path = os .path .join (os .path .dirname (os .path .abspath (__file__ )), '..' , 'robot_tests' )
136136
137- # Stage 2a: Building Docker Image
138- yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'running' , 'message' : 'Building container image for test execution...' })} \n \n "
139- client .images .build (path = dockerfile_path , tag = image_tag , rm = True )
137+ # Stage 2a: Check and Build Docker Image (only if needed)
138+ try :
139+ # Check if the image already exists
140+ existing_image = client .images .get (image_tag )
141+ logging .info (f"Docker image '{ image_tag } ' already exists. Skipping build." )
142+ yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'running' , 'message' : 'Using existing container image for test execution...' })} \n \n "
143+ except docker .errors .ImageNotFound :
144+ # Image doesn't exist, need to build it
145+ logging .info (f"Docker image '{ image_tag } ' not found. Building new image." )
146+ yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'running' , 'message' : 'Building container image for test execution (first time only)...' })} \n \n "
147+ try :
148+ client .images .build (path = dockerfile_path , tag = image_tag , rm = True )
149+ logging .info (f"Successfully built Docker image '{ image_tag } '." )
150+ yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'running' , 'message' : 'Container image built successfully!' })} \n \n "
151+ except docker .errors .BuildError as build_err :
152+ logging .error (f"Failed to build Docker image: { build_err } " )
153+ yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'error' , 'message' : f'Docker image build failed: { build_err } ' })} \n \n "
154+ return
140155
141156 # Stage 2b: Running Docker Container
142157 yield f"data: { json .dumps ({'stage' : 'execution' , 'status' : 'running' , 'message' : 'Executing test inside the container...' })} \n \n "
@@ -219,6 +234,76 @@ async def generate_and_run_streaming(query: Query):
219234
220235 return StreamingResponse (stream_generate_and_run (user_query , model_name ), media_type = "text/event-stream" )
221236
237+ @app .post ('/rebuild-docker-image' )
238+ async def rebuild_docker_image ():
239+ """Endpoint to force rebuild the Docker image when needed."""
240+ try :
241+ client = docker .from_env ()
242+ image_tag = "robot-test-runner:latest"
243+ dockerfile_path = os .path .join (os .path .dirname (os .path .abspath (__file__ )), '..' , 'robot_tests' )
244+
245+ # Remove existing image if it exists
246+ try :
247+ existing_image = client .images .get (image_tag )
248+ client .images .remove (image = image_tag , force = True )
249+ logging .info (f"Removed existing Docker image '{ image_tag } '." )
250+ except docker .errors .ImageNotFound :
251+ logging .info (f"No existing Docker image '{ image_tag } ' to remove." )
252+
253+ # Build new image
254+ logging .info (f"Building new Docker image '{ image_tag } '." )
255+ client .images .build (path = dockerfile_path , tag = image_tag , rm = True )
256+ logging .info (f"Successfully rebuilt Docker image '{ image_tag } '." )
257+
258+ return {"status" : "success" , "message" : f"Docker image '{ image_tag } ' rebuilt successfully." }
259+
260+ except docker .errors .DockerException as e :
261+ error_message = f"Docker error: { e } "
262+ logging .error (f"Failed to rebuild Docker image: { e } " )
263+ raise HTTPException (status_code = 500 , detail = error_message )
264+ except Exception as e :
265+ error_message = f"Unexpected error: { e } "
266+ logging .error (f"Unexpected error during Docker image rebuild: { e } " )
267+ raise HTTPException (status_code = 500 , detail = error_message )
268+
269+ @app .get ('/docker-status' )
270+ async def docker_status ():
271+ """Endpoint to check Docker status and image availability."""
272+ try :
273+ client = docker .from_env ()
274+ client .ping ()
275+
276+ image_tag = "robot-test-runner:latest"
277+ try :
278+ image = client .images .get (image_tag )
279+ image_info = {
280+ "exists" : True ,
281+ "id" : image .id ,
282+ "created" : image .attrs .get ('Created' , 'Unknown' ),
283+ "size" : f"{ image .attrs .get ('Size' , 0 ) / (1024 * 1024 ):.1f} MB"
284+ }
285+ except docker .errors .ImageNotFound :
286+ image_info = {"exists" : False }
287+
288+ return {
289+ "status" : "success" ,
290+ "docker_available" : True ,
291+ "image" : image_info
292+ }
293+
294+ except docker .errors .DockerException as e :
295+ return {
296+ "status" : "error" ,
297+ "docker_available" : False ,
298+ "error" : str (e )
299+ }
300+ except Exception as e :
301+ return {
302+ "status" : "error" ,
303+ "docker_available" : False ,
304+ "error" : f"Unexpected error: { e } "
305+ }
306+
222307# --- Static Files and Root Endpoint ---
223308FRONTEND_DIR = os .path .join (os .path .dirname (os .path .abspath (__file__ )), ".." , "frontend" )
224309ROBOT_TESTS_DIR = os .path .join (os .path .dirname (os .path .abspath (__file__ )), ".." , "robot_tests" )
0 commit comments