chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from fastapi import (
|
||||
APIRouter,
|
||||
File,
|
||||
Form,
|
||||
HTTPException,
|
||||
Path,
|
||||
Query,
|
||||
Request,
|
||||
UploadFile,
|
||||
)
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from sglang.multimodal_gen.configs.sample.sampling_params import (
|
||||
SamplingParams,
|
||||
generate_request_id,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.entrypoints.openai.protocol import (
|
||||
MeshGenerationsRequest,
|
||||
MeshListResponse,
|
||||
MeshResponse,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.entrypoints.openai.storage import cloud_storage
|
||||
from sglang.multimodal_gen.runtime.entrypoints.openai.stores import MESH_STORE
|
||||
from sglang.multimodal_gen.runtime.entrypoints.openai.utils import (
|
||||
add_common_data_to_response,
|
||||
merge_image_input_list,
|
||||
process_generation_batch,
|
||||
save_image_to_path,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.entrypoints.utils import prepare_request
|
||||
from sglang.multimodal_gen.runtime.pipelines_core.schedule_batch import Req
|
||||
from sglang.multimodal_gen.runtime.server_args import get_global_server_args
|
||||
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
||||
|
||||
logger = init_logger(__name__)
|
||||
router = APIRouter(prefix="/v1/meshes", tags=["meshes"])
|
||||
|
||||
|
||||
def _normalize_format(fmt: Optional[str]) -> str:
|
||||
fmt = (fmt or "glb").lower()
|
||||
return fmt if fmt in ("glb", "obj") else "glb"
|
||||
|
||||
|
||||
def _build_sampling_params_from_request(
|
||||
request_id: str, req: MeshGenerationsRequest, image_path: Optional[str] = None
|
||||
) -> SamplingParams:
|
||||
ext = _normalize_format(req.output_format)
|
||||
|
||||
server_args = get_global_server_args()
|
||||
sampling_kwargs: Dict[str, Any] = {
|
||||
"request_id": request_id,
|
||||
"prompt": req.prompt,
|
||||
"num_frames": 1,
|
||||
"image_path": [image_path] if image_path else None,
|
||||
"save_output": True,
|
||||
"output_file_name": f"{request_id}.{ext}",
|
||||
"seed": req.seed,
|
||||
"generator_device": req.generator_device,
|
||||
}
|
||||
if req.num_inference_steps is not None:
|
||||
sampling_kwargs["num_inference_steps"] = req.num_inference_steps
|
||||
if req.guidance_scale is not None:
|
||||
sampling_kwargs["guidance_scale"] = req.guidance_scale
|
||||
if req.negative_prompt is not None:
|
||||
sampling_kwargs["negative_prompt"] = req.negative_prompt
|
||||
|
||||
return SamplingParams.from_user_sampling_params_args(
|
||||
model_path=server_args.model_path,
|
||||
server_args=server_args,
|
||||
**sampling_kwargs,
|
||||
)
|
||||
|
||||
|
||||
def _mesh_job_from_sampling(
|
||||
request_id: str, req: MeshGenerationsRequest, sampling: SamplingParams
|
||||
) -> Dict[str, Any]:
|
||||
return {
|
||||
"id": request_id,
|
||||
"object": "mesh",
|
||||
"model": req.model or "",
|
||||
"status": "queued",
|
||||
"progress": 0,
|
||||
"created_at": int(time.time()),
|
||||
"format": _normalize_format(req.output_format),
|
||||
"file_path": os.path.abspath(sampling.output_file_path()),
|
||||
}
|
||||
|
||||
|
||||
async def _dispatch_job_async(job_id: str, batch: Req) -> None:
|
||||
from sglang.multimodal_gen.runtime.scheduler_client import async_scheduler_client
|
||||
|
||||
try:
|
||||
save_file_path_list, result = await process_generation_batch(
|
||||
async_scheduler_client, batch
|
||||
)
|
||||
save_file_path = save_file_path_list[0]
|
||||
|
||||
file_size = None
|
||||
if os.path.exists(save_file_path):
|
||||
file_size = os.path.getsize(save_file_path)
|
||||
|
||||
cloud_url = await cloud_storage.upload_and_cleanup(save_file_path)
|
||||
|
||||
update_fields: Dict[str, Any] = {
|
||||
"status": "completed",
|
||||
"progress": 100,
|
||||
"completed_at": int(time.time()),
|
||||
"url": cloud_url,
|
||||
"file_path": save_file_path if not cloud_url else None,
|
||||
"file_size_bytes": file_size,
|
||||
}
|
||||
update_fields = add_common_data_to_response(
|
||||
update_fields, request_id=job_id, result=result
|
||||
)
|
||||
await MESH_STORE.update_fields(job_id, update_fields)
|
||||
except Exception as e:
|
||||
logger.error(f"{e}")
|
||||
await MESH_STORE.update_fields(
|
||||
job_id, {"status": "failed", "error": {"message": str(e)}}
|
||||
)
|
||||
|
||||
|
||||
@router.post("", response_model=MeshResponse)
|
||||
async def create_mesh(
|
||||
request: Request,
|
||||
image: Optional[List[UploadFile]] = File(None),
|
||||
image_array: Optional[List[UploadFile]] = File(None, alias="image[]"),
|
||||
url: Optional[List[str]] = Form(None),
|
||||
url_array: Optional[List[str]] = Form(None, alias="url[]"),
|
||||
prompt: Optional[str] = Form("generate 3d mesh"),
|
||||
model: Optional[str] = Form(None),
|
||||
seed: Optional[int] = Form(None),
|
||||
generator_device: Optional[str] = Form("cuda"),
|
||||
guidance_scale: Optional[float] = Form(None),
|
||||
num_inference_steps: Optional[int] = Form(None),
|
||||
negative_prompt: Optional[str] = Form(None),
|
||||
output_format: Optional[str] = Form("glb"),
|
||||
):
|
||||
content_type = request.headers.get("content-type", "").lower()
|
||||
request_id = generate_request_id()
|
||||
server_args = get_global_server_args()
|
||||
|
||||
input_path = None
|
||||
|
||||
if "multipart/form-data" in content_type:
|
||||
images = image or image_array
|
||||
urls = url or url_array
|
||||
image_list = merge_image_input_list(images, urls)
|
||||
|
||||
if not image_list:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="Field 'image' or 'url' is required for mesh generation",
|
||||
)
|
||||
|
||||
uploads_dir = os.path.join("outputs", "uploads")
|
||||
os.makedirs(uploads_dir, exist_ok=True)
|
||||
img = image_list[0]
|
||||
filename = img.filename if hasattr(img, "filename") else "input_image"
|
||||
try:
|
||||
input_path = await save_image_to_path(
|
||||
img, os.path.join(uploads_dir, f"{request_id}_{filename}")
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=400, detail=f"Failed to process image source: {str(e)}"
|
||||
)
|
||||
|
||||
req = MeshGenerationsRequest(
|
||||
prompt=prompt or "generate 3d mesh",
|
||||
model=model,
|
||||
seed=seed,
|
||||
generator_device=generator_device,
|
||||
num_inference_steps=num_inference_steps,
|
||||
negative_prompt=negative_prompt,
|
||||
output_format=output_format,
|
||||
**(
|
||||
{"guidance_scale": guidance_scale} if guidance_scale is not None else {}
|
||||
),
|
||||
)
|
||||
else:
|
||||
try:
|
||||
body = await request.json()
|
||||
except Exception:
|
||||
body = {}
|
||||
try:
|
||||
payload: Dict[str, Any] = dict(body or {})
|
||||
|
||||
if payload.get("input_image"):
|
||||
img_src = payload.pop("input_image")
|
||||
uploads_dir = os.path.join("outputs", "uploads")
|
||||
os.makedirs(uploads_dir, exist_ok=True)
|
||||
input_path = await save_image_to_path(
|
||||
img_src,
|
||||
os.path.join(uploads_dir, f"{request_id}_input_image"),
|
||||
)
|
||||
|
||||
req = MeshGenerationsRequest(**payload)
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid request body: {e}")
|
||||
|
||||
if not input_path:
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail="An input image is required for mesh generation",
|
||||
)
|
||||
|
||||
sampling_params = _build_sampling_params_from_request(request_id, req, input_path)
|
||||
job = _mesh_job_from_sampling(request_id, req, sampling_params)
|
||||
await MESH_STORE.upsert(request_id, job)
|
||||
|
||||
batch = prepare_request(
|
||||
server_args=server_args,
|
||||
sampling_params=sampling_params,
|
||||
)
|
||||
|
||||
asyncio.create_task(_dispatch_job_async(request_id, batch))
|
||||
return MeshResponse(**job)
|
||||
|
||||
|
||||
@router.get("", response_model=MeshListResponse)
|
||||
async def list_meshes(
|
||||
after: Optional[str] = Query(None),
|
||||
limit: Optional[int] = Query(None, ge=1, le=100),
|
||||
order: Optional[str] = Query("desc"),
|
||||
):
|
||||
order = (order or "desc").lower()
|
||||
if order not in ("asc", "desc"):
|
||||
order = "desc"
|
||||
jobs = await MESH_STORE.list_values()
|
||||
|
||||
reverse = order != "asc"
|
||||
jobs.sort(key=lambda j: j.get("created_at", 0), reverse=reverse)
|
||||
|
||||
if after is not None:
|
||||
try:
|
||||
idx = next(i for i, j in enumerate(jobs) if j["id"] == after)
|
||||
jobs = jobs[idx + 1 :]
|
||||
except StopIteration:
|
||||
jobs = []
|
||||
|
||||
if limit is not None:
|
||||
jobs = jobs[:limit]
|
||||
items = [MeshResponse(**j) for j in jobs]
|
||||
return MeshListResponse(data=items)
|
||||
|
||||
|
||||
@router.get("/{mesh_id}", response_model=MeshResponse)
|
||||
async def retrieve_mesh(mesh_id: str = Path(...)):
|
||||
job = await MESH_STORE.get(mesh_id)
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Mesh not found")
|
||||
return MeshResponse(**job)
|
||||
|
||||
|
||||
@router.delete("/{mesh_id}", response_model=MeshResponse)
|
||||
async def delete_mesh(mesh_id: str = Path(...)):
|
||||
job = await MESH_STORE.pop(mesh_id)
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Mesh not found")
|
||||
job["status"] = "deleted"
|
||||
return MeshResponse(**job)
|
||||
|
||||
|
||||
@router.get("/{mesh_id}/content")
|
||||
async def download_mesh_content(
|
||||
mesh_id: str = Path(...), variant: Optional[str] = Query(None)
|
||||
):
|
||||
job = await MESH_STORE.get(mesh_id)
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Mesh not found")
|
||||
|
||||
if job.get("url"):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Mesh has been uploaded to cloud storage. Please use the cloud URL: {job.get('url')}",
|
||||
)
|
||||
|
||||
file_path = job.get("file_path")
|
||||
if not file_path or not os.path.exists(file_path):
|
||||
raise HTTPException(status_code=404, detail="Generation is still in-progress")
|
||||
|
||||
ext = os.path.splitext(file_path)[1].lower()
|
||||
media_type = {
|
||||
".glb": "model/gltf-binary",
|
||||
".obj": "text/plain",
|
||||
}.get(ext, "application/octet-stream")
|
||||
|
||||
return FileResponse(
|
||||
path=file_path, media_type=media_type, filename=os.path.basename(file_path)
|
||||
)
|
||||
Reference in New Issue
Block a user