88Flask service with build-time model selection (no runtime model parameter needed).
99"""
1010
11+ import argparse
1112import base64
1213import os
1314import signal
15+ import subprocess
1416import sys
1517import tempfile
1618import time
@@ -257,9 +259,105 @@ def signalHandler(sig, frame):
257259 log .info ("Received SIGINT (Ctrl+C), shutting down gracefully..." )
258260 sys .exit (0 )
259261
262+ def runDevelopmentServer ():
263+ """Run Flask development server"""
264+ log .info ("Starting in DEVELOPMENT mode..." )
265+ log .info ("Flask development server starting on http://0.0.0.0:8000" )
266+ log .info ("Press Ctrl+C to stop the server" )
267+
268+ try :
269+ # Run Flask development server
270+ app .run (
271+ host = "0.0.0.0" ,
272+ port = 8000 ,
273+ debug = True ,
274+ threaded = True
275+ )
276+ except KeyboardInterrupt :
277+ log .info ("Server interrupted by user" )
278+ except Exception as e :
279+ log .error (f"Server error: { e } " )
280+ finally :
281+ log .info ("Server shutdown complete" )
282+
283+ def runProductionServer (cert_file = None , key_file = None ):
284+ """Run Gunicorn production server with TLS"""
285+ log .info ("Starting in PRODUCTION mode with TLS..." )
286+
287+ # Check if certificates exist
288+ if not os .path .exists (cert_file ):
289+ log .error (f"TLS certificate file not found: { cert_file } " )
290+ sys .exit (1 )
291+
292+ if not os .path .exists (key_file ):
293+ log .error (f"TLS key file not found: { key_file } " )
294+ sys .exit (1 )
295+
296+ log .info (f"Using TLS certificate: { cert_file } " )
297+ log .info (f"Using TLS key: { key_file } " )
298+ log .info ("Gunicorn HTTPS server starting on https://0.0.0.0:8000" )
299+
300+ # Determine the service module based on model type
301+ model_type = os .getenv ("MODEL_TYPE" , "mapanything" )
302+ service_module = f"{ model_type } _service:app"
303+
304+ # Gunicorn command arguments
305+ gunicorn_cmd = [
306+ "gunicorn" ,
307+ "--bind" , "0.0.0.0:8000" ,
308+ "--workers" , "1" ,
309+ "--worker-class" , "sync" ,
310+ "--timeout" , "300" ,
311+ "--keep-alive" , "5" ,
312+ "--max-requests" , "1000" ,
313+ "--max-requests-jitter" , "100" ,
314+ "--access-logfile" , "-" ,
315+ "--error-logfile" , "-" ,
316+ "--log-level" , "info" ,
317+ "--certfile" , cert_file ,
318+ "--keyfile" , key_file ,
319+ service_module
320+ ]
321+
322+ log .info (f"Starting Gunicorn with service module: { service_module } " )
323+
324+ try :
325+ # Run Gunicorn with TLS
326+ subprocess .run (gunicorn_cmd , check = True )
327+ except subprocess .CalledProcessError as e :
328+ log .error (f"Gunicorn failed to start: { e } " )
329+ sys .exit (1 )
330+ except KeyboardInterrupt :
331+ log .info ("Server interrupted by user" )
332+ except Exception as e :
333+ log .error (f"Server error: { e } " )
334+ sys .exit (1 )
335+
260336def startApp ():
261- """Start the application with model initialization"""
262- global device , loaded_model , model_name
337+ """Start the application with command line argument parsing"""
338+ parser = argparse .ArgumentParser (description = "3D Mapping Models API Server" )
339+ parser .add_argument (
340+ "--dev-mode" ,
341+ action = "store_true" ,
342+ help = "Run in development mode with Flask development server (default: production mode with Gunicorn + TLS)"
343+ )
344+ parser .add_argument (
345+ "--development" ,
346+ action = "store_true" ,
347+ help = "Alias for --dev-mode"
348+ )
349+ parser .add_argument (
350+ "--cert-file" ,
351+ default = "/run/secrets/certs/scenescape-mapping.crt" ,
352+ help = "Path to TLS certificate file (default: /run/secrets/certs/scenescape-mapping.crt)"
353+ )
354+ parser .add_argument (
355+ "--key-file" ,
356+ default = "/run/secrets/certs/scenescape-mapping.key" ,
357+ help = "Path to TLS private key file (default: /run/secrets/certs/scenescape-mapping.key)"
358+ )
359+
360+ args = parser .parse_args ()
263361
264362 # Set up signal handler for graceful shutdown
265363 signal .signal (signal .SIGINT , signalHandler )
@@ -268,23 +366,23 @@ def startApp():
268366 log .info ("Starting 3D Mapping API server..." )
269367
270368 # Initialize model before starting server
369+ global device , loaded_model , model_name
271370 device = "cpu"
272371 log .info (f"Using device: { device } " )
273372
274373 try :
374+ # Only initialize model if not already loaded
275375 loaded_model , model_name = initializeModel ()
276376 log .info ("API Service startup completed successfully" )
277377
278- log .info ("Flask server starting on http://0.0.0.0:8000" )
279- log .info ("Press Ctrl+C to stop the server" )
378+ # Determine which server to run
379+ dev_mode = args .dev_mode or args .development or os .getenv ("DEV_MODE" , "" ).lower () in ("true" , "1" , "yes" )
380+
381+ if dev_mode :
382+ runDevelopmentServer ()
383+ else :
384+ runProductionServer (cert_file = args .cert_file , key_file = args .key_file )
280385
281- # Run Flask development server
282- app .run (
283- host = "0.0.0.0" ,
284- port = 8000 ,
285- debug = False ,
286- threaded = True
287- )
288386 except KeyboardInterrupt :
289387 log .info ("Server interrupted by user" )
290388 except Exception as e :
0 commit comments