Skip to content

Commit 7495be1

Browse files
Merge pull request #1743 from roboflow/proxy-sam3-endpoint
Proxy sam3 endpoint
2 parents ec49df8 + aabbd12 commit 7495be1

File tree

2 files changed

+115
-2
lines changed

2 files changed

+115
-2
lines changed

inference/core/interfaces/http/http_api.py

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from uuid import uuid4
1010

1111
import asgi_correlation_id
12+
import requests
1213
import uvicorn
1314
from fastapi import (
1415
BackgroundTasks,
@@ -125,6 +126,7 @@
125126
)
126127
from inference.core.env import (
127128
ALLOW_ORIGINS,
129+
API_BASE_URL,
128130
API_KEY,
129131
API_LOGGING_ENABLED,
130132
BUILDER_ORIGIN,
@@ -165,7 +167,10 @@
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,
@@ -228,6 +233,7 @@
228233
from inference.core.managers.metrics import get_container_stats
229234
from inference.core.managers.prometheus import InferenceInstrumentator
230235
from 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,

inference/core/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.62.1"
1+
__version__ = "0.62.2"
22

33

44
if __name__ == "__main__":

0 commit comments

Comments
 (0)