99from uuid import uuid4
1010
1111import asgi_correlation_id
12+ import requests
1213import uvicorn
1314from fastapi import (
1415 BackgroundTasks ,
125126)
126127from inference .core .env import (
127128 ALLOW_ORIGINS ,
129+ API_BASE_URL ,
128130 API_KEY ,
129131 API_LOGGING_ENABLED ,
130132 BUILDER_ORIGIN ,
165167 NOTEBOOK_PORT ,
166168 PRELOAD_MODELS ,
167169 PROFILE ,
170+ ROBOFLOW_INTERNAL_SERVICE_NAME ,
171+ ROBOFLOW_INTERNAL_SERVICE_SECRET ,
168172 ROBOFLOW_SERVICE_SECRET ,
173+ SAM3_EXEC_MODE ,
169174 WEBRTC_WORKER_ENABLED ,
170175 WORKFLOWS_MAX_CONCURRENT_STEPS ,
171176 WORKFLOWS_PROFILER_BUFFER_SIZE ,
228233from inference .core .managers .metrics import get_container_stats
229234from inference .core .managers .prometheus import InferenceInstrumentator
230235from inference .core .roboflow_api import (
236+ build_roboflow_api_headers ,
231237 get_roboflow_workspace ,
232238 get_roboflow_workspace_async ,
233239 get_workflow_specification ,
@@ -2379,7 +2385,7 @@ def sam2_segment_image(
23792385 )
23802386 return model_response
23812387
2382- if CORE_MODEL_SAM3_ENABLED :
2388+ if CORE_MODEL_SAM3_ENABLED and not GCP_SERVERLESS :
23832389
23842390 @app .post (
23852391 "/sam3/embed_image" ,
@@ -2401,6 +2407,12 @@ def sam3_embed_image(
24012407 ):
24022408 logger .debug (f"Reached /sam3/embed_image" )
24032409
2410+ if SAM3_EXEC_MODE == "remote" :
2411+ raise HTTPException (
2412+ status_code = 501 ,
2413+ detail = "SAM3 embedding is not supported in remote execution mode." ,
2414+ )
2415+
24042416 self .model_manager .add_model (
24052417 "sam3/sam3_interactive" ,
24062418 api_key = api_key ,
@@ -2434,6 +2446,12 @@ def sam3_visual_segment(
24342446 ):
24352447 logger .debug (f"Reached /sam3/visual_segment" )
24362448
2449+ if SAM3_EXEC_MODE == "remote" :
2450+ raise HTTPException (
2451+ status_code = 501 ,
2452+ detail = "SAM3 visual segmentation is not supported in remote execution mode." ,
2453+ )
2454+
24372455 self .model_manager .add_model (
24382456 "sam3/sam3_interactive" ,
24392457 api_key = api_key ,
@@ -2447,6 +2465,8 @@ def sam3_visual_segment(
24472465 )
24482466 return model_response
24492467
2468+ if CORE_MODEL_SAM3_ENABLED :
2469+
24502470 @app .post (
24512471 "/sam3/concept_segment" ,
24522472 response_model = Sam3SegmentationResponse ,
@@ -2465,6 +2485,99 @@ def sam3_segment_image(
24652485 countinference : Optional [bool ] = None ,
24662486 service_secret : Optional [str ] = None ,
24672487 ):
2488+ if SAM3_EXEC_MODE == "remote" :
2489+ if not inference_request .model_id .startswith ("sam3/" ):
2490+ raise HTTPException (
2491+ status_code = 501 ,
2492+ detail = "Fine-tuned SAM3 models are not supported in remote execution mode yet. Please use a workflow or self-host the server." ,
2493+ )
2494+ endpoint = f"{ API_BASE_URL } /inferenceproxy/seg-preview"
2495+
2496+ # Construct payload for remote API
2497+ # The remote API expects:
2498+ # {
2499+ # "image": {"type": "base64", "value": ...},
2500+ # "prompts": [{"type": "text", "text": ...}, ...],
2501+ # "output_prob_thresh": ...
2502+ # }
2503+
2504+ # Extract prompts from request
2505+ http_prompts = []
2506+ for prompt in inference_request .prompts :
2507+ p_dict = prompt .dict (exclude_none = True )
2508+ # Ensure type is set if missing (default to text if text is present)
2509+ if "type" not in p_dict :
2510+ if "text" in p_dict :
2511+ p_dict ["type" ] = "text"
2512+ http_prompts .append (p_dict )
2513+
2514+ # Prepare image
2515+ # inference_request.image is InferenceRequestImage
2516+ if inference_request .image .type == "base64" :
2517+ http_image = {
2518+ "type" : "base64" ,
2519+ "value" : inference_request .image .value ,
2520+ }
2521+ elif inference_request .image .type == "url" :
2522+ http_image = {
2523+ "type" : "url" ,
2524+ "value" : inference_request .image .value ,
2525+ }
2526+ elif inference_request .image .type == "numpy" :
2527+ # Numpy not supported for remote proxy easily without serialization,
2528+ # but InferenceRequestImage usually comes as base64/url in HTTP API.
2529+ # If it is numpy, we might need to handle it, but for now assume base64/url.
2530+ # If it's numpy, it's likely from internal call, but this is HTTP API.
2531+ http_image = {
2532+ "type" : "numpy" ,
2533+ "value" : inference_request .image .value ,
2534+ }
2535+ else :
2536+ http_image = {
2537+ "type" : inference_request .image .type ,
2538+ "value" : inference_request .image .value ,
2539+ }
2540+
2541+ payload = {
2542+ "image" : http_image ,
2543+ "prompts" : http_prompts ,
2544+ "output_prob_thresh" : inference_request .output_prob_thresh ,
2545+ }
2546+
2547+ try :
2548+ headers = {"Content-Type" : "application/json" }
2549+ if ROBOFLOW_INTERNAL_SERVICE_NAME :
2550+ headers ["X-Roboflow-Internal-Service-Name" ] = (
2551+ ROBOFLOW_INTERNAL_SERVICE_NAME
2552+ )
2553+ if ROBOFLOW_INTERNAL_SERVICE_SECRET :
2554+ headers ["X-Roboflow-Internal-Service-Secret" ] = (
2555+ ROBOFLOW_INTERNAL_SERVICE_SECRET
2556+ )
2557+
2558+ headers = build_roboflow_api_headers (
2559+ explicit_headers = headers
2560+ )
2561+
2562+ response = requests .post (
2563+ f"{ endpoint } ?api_key={ api_key } " ,
2564+ json = payload ,
2565+ headers = headers ,
2566+ timeout = 60 ,
2567+ )
2568+ response .raise_for_status ()
2569+ resp_json = response .json ()
2570+
2571+ # The remote API returns the same structure as Sam3SegmentationResponse
2572+ return Sam3SegmentationResponse (** resp_json )
2573+
2574+ except Exception as e :
2575+ logger .error (f"SAM3 remote request failed: { e } " )
2576+ raise HTTPException (
2577+ status_code = 500 ,
2578+ detail = f"SAM3 remote request failed: { str (e )} " ,
2579+ )
2580+
24682581 if inference_request .model_id .startswith ("sam3/" ):
24692582 self .model_manager .add_model (
24702583 inference_request .model_id ,
0 commit comments