2026-03-26 20:50:00 +08:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
import json
|
|
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Configuration
|
2026-03-27 00:54:43 +08:00
|
|
|
SCRIPT_DIR = Path("/opt/script")
|
|
|
|
|
CONFIG_PATH = Path("/config/config.json")
|
|
|
|
|
LOCAL_MODEL_DIR = os.getenv("LOCAL_MODEL_DIR", "/opt/model")
|
2026-03-26 20:50:00 +08:00
|
|
|
HOST = os.getenv("HOST", "0.0.0.0")
|
|
|
|
|
PORT = os.getenv("PORT", "8000")
|
|
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
def log(msg):
|
|
|
|
|
"""Print log message with timestamp"""
|
|
|
|
|
print(f"[START-VLLM] {msg}", flush=True)
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
def load_config():
|
|
|
|
|
"""Load configuration from config.json"""
|
|
|
|
|
log(f"Loading config from {CONFIG_PATH}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
if not CONFIG_PATH.exists():
|
|
|
|
|
log(f"ERROR: Config file not found at {CONFIG_PATH}")
|
|
|
|
|
sys.exit(1)
|
2026-03-26 20:50:00 +08:00
|
|
|
|
|
|
|
|
try:
|
2026-03-27 01:32:30 +08:00
|
|
|
with open(CONFIG_PATH, "r") as f:
|
|
|
|
|
config_data = json.load(f)
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
model_table = config_data["models"]
|
|
|
|
|
default_model = config_data["default_model"]
|
|
|
|
|
models_to_run = list(model_table.keys())
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"Loaded {len(models_to_run)} models from config")
|
|
|
|
|
log(f"Default model: {default_model}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
return model_table, default_model, models_to_run
|
2026-03-26 20:50:00 +08:00
|
|
|
except Exception as e:
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"ERROR: Failed to load config: {e}")
|
|
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|
2026-03-27 00:58:15 +08:00
|
|
|
sys.exit(1)
|
2026-03-27 01:32:30 +08:00
|
|
|
|
|
|
|
|
def detect_gpus():
|
|
|
|
|
"""Detect AMD GPUs"""
|
|
|
|
|
try:
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
["rocm-smi", "--showid", "--csv"],
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
timeout=10
|
|
|
|
|
)
|
|
|
|
|
if result.returncode == 0:
|
|
|
|
|
count = result.stdout.count("GPU")
|
|
|
|
|
if count > 0:
|
|
|
|
|
return count
|
|
|
|
|
except Exception as e:
|
|
|
|
|
log(f"Warning: rocm-smi failed: {e}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Fallback to /dev/dri
|
|
|
|
|
try:
|
|
|
|
|
render_devices = list(Path("/dev/dri").glob("renderD*"))
|
|
|
|
|
if render_devices:
|
|
|
|
|
return len(render_devices)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
log("Warning: Could not detect GPUs, assuming 1 GPU")
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
def find_model_path(model_id):
|
|
|
|
|
"""Find local model path"""
|
|
|
|
|
log(f"Looking for model: {model_id}")
|
|
|
|
|
log(f"LOCAL_MODEL_DIR: {LOCAL_MODEL_DIR}")
|
|
|
|
|
|
|
|
|
|
if not os.path.exists(LOCAL_MODEL_DIR):
|
|
|
|
|
log(f"ERROR: LOCAL_MODEL_DIR does not exist: {LOCAL_MODEL_DIR}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# Try exact match
|
|
|
|
|
candidate = os.path.join(LOCAL_MODEL_DIR, model_id)
|
|
|
|
|
if os.path.isdir(candidate):
|
|
|
|
|
log(f"Found model at: {candidate}")
|
|
|
|
|
return candidate
|
|
|
|
|
|
|
|
|
|
# Try without prefix
|
|
|
|
|
repo_name = model_id.split('/')[-1] if '/' in model_id else model_id
|
|
|
|
|
candidate = os.path.join(LOCAL_MODEL_DIR, repo_name)
|
|
|
|
|
if os.path.isdir(candidate):
|
|
|
|
|
log(f"Found model at: {candidate}")
|
|
|
|
|
return candidate
|
|
|
|
|
|
|
|
|
|
# Try case-insensitive match
|
|
|
|
|
try:
|
|
|
|
|
for entry in os.listdir(LOCAL_MODEL_DIR):
|
|
|
|
|
if entry.lower() == repo_name.lower():
|
|
|
|
|
entry_path = os.path.join(LOCAL_MODEL_DIR, entry)
|
|
|
|
|
if os.path.isdir(entry_path):
|
|
|
|
|
log(f"Found model at: {entry_path}")
|
|
|
|
|
return entry_path
|
|
|
|
|
except Exception as e:
|
|
|
|
|
log(f"ERROR: Failed to list directory: {e}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"ERROR: Model not found: {model_id}")
|
|
|
|
|
log(f"Available models: {os.listdir(LOCAL_MODEL_DIR)}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def launch_model(model_id, config, model_path, gpu_count):
|
|
|
|
|
"""Launch vLLM server"""
|
|
|
|
|
log(f"Launching model: {model_id}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Get configuration
|
|
|
|
|
valid_tp = config.get("valid_tp", [1])
|
|
|
|
|
max_tp = max(valid_tp) if valid_tp else 1
|
|
|
|
|
tp_size = min(gpu_count, max_tp)
|
2026-03-27 01:28:45 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
ctx = int(config.get("ctx", 8192))
|
|
|
|
|
max_seqs = int(config.get("max_num_seqs", 64))
|
|
|
|
|
gpu_util = float(config.get("gpu_util", 0.98))
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"Config: TP={tp_size}, Ctx={ctx}, Seqs={max_seqs}, Util={gpu_util}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Build command
|
2026-03-26 20:50:00 +08:00
|
|
|
cmd = [
|
|
|
|
|
"vllm", "serve", model_path,
|
|
|
|
|
"--host", HOST,
|
|
|
|
|
"--port", PORT,
|
2026-03-27 01:32:30 +08:00
|
|
|
"--tensor-parallel-size", str(tp_size),
|
|
|
|
|
"--max-num-seqs", str(max_seqs),
|
|
|
|
|
"--max-model-len", str(ctx),
|
|
|
|
|
"--gpu-memory-utilization", str(gpu_util),
|
2026-03-26 20:50:00 +08:00
|
|
|
"--dtype", "auto"
|
|
|
|
|
]
|
|
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
if config.get("trust_remote"):
|
|
|
|
|
cmd.append("--trust-remote-code")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
if config.get("enforce_eager"):
|
|
|
|
|
cmd.append("--enforce-eager")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"Command: {' '.join(cmd)}")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Set environment
|
|
|
|
|
env = os.environ.copy()
|
|
|
|
|
env.update(config.get("env", {}))
|
2026-03-27 01:11:48 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Launch vLLM
|
|
|
|
|
log("Starting vLLM server...")
|
2026-03-27 01:11:48 +08:00
|
|
|
try:
|
2026-03-27 01:32:30 +08:00
|
|
|
result = subprocess.run(cmd, env=env)
|
2026-03-27 01:11:48 +08:00
|
|
|
if result.returncode != 0:
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"ERROR: vLLM exited with code {result.returncode}")
|
2026-03-27 01:11:48 +08:00
|
|
|
sys.exit(result.returncode)
|
|
|
|
|
except Exception as e:
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"ERROR: Failed to start vLLM: {e}")
|
|
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|
2026-03-27 01:11:48 +08:00
|
|
|
sys.exit(1)
|
2026-03-26 20:50:00 +08:00
|
|
|
|
|
|
|
|
def main():
|
2026-03-27 01:32:30 +08:00
|
|
|
"""Main entry point"""
|
|
|
|
|
log("Starting vLLM launcher...")
|
|
|
|
|
|
|
|
|
|
# Load configuration
|
|
|
|
|
model_table, default_model, models_to_run = load_config()
|
|
|
|
|
|
|
|
|
|
# Detect GPUs
|
2026-03-26 20:50:00 +08:00
|
|
|
gpu_count = detect_gpus()
|
2026-03-27 01:32:30 +08:00
|
|
|
log(f"Detected {gpu_count} GPU(s)")
|
2026-03-26 20:50:00 +08:00
|
|
|
|
2026-03-27 01:32:30 +08:00
|
|
|
# Check if we should use default model
|
2026-03-26 21:37:10 +08:00
|
|
|
use_default = os.getenv("USE_DEFAULT_MODEL", "false").lower() == "true"
|
|
|
|
|
|
|
|
|
|
if use_default:
|
2026-03-27 01:32:30 +08:00
|
|
|
log("Using default model mode")
|
|
|
|
|
model_id = default_model
|
2026-03-26 21:37:10 +08:00
|
|
|
else:
|
2026-03-27 01:32:30 +08:00
|
|
|
# Interactive mode - for now just use default
|
|
|
|
|
log("Interactive mode not supported in container, using default model")
|
|
|
|
|
model_id = default_model
|
|
|
|
|
|
|
|
|
|
# Check if model is in config
|
|
|
|
|
if model_id not in model_table:
|
|
|
|
|
log(f"ERROR: Model {model_id} not found in config")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
config = model_table[model_id]
|
|
|
|
|
|
|
|
|
|
# Find model path
|
|
|
|
|
model_path = find_model_path(model_id)
|
|
|
|
|
if not model_path:
|
|
|
|
|
log("ERROR: Could not find local model. Offline mode is active.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
# Launch model
|
|
|
|
|
launch_model(model_id, config, model_path, gpu_count)
|
2026-03-26 20:50:00 +08:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|