"""Foreground supervisor: runtime and model checks, startup attempts, readiness, termination. The service writes files only below --state-dir (plus shared memory in /dev/shm): /-startup/ attempts.jsonl, attempt-N.log, state-N/ (quota state), tmp/, home/ /cache/ compilation caches shared by runs that use the same state directory """ import argparse from contextlib import contextmanager import hashlib import importlib.metadata import json import os from pathlib import Path import re import signal import socket import subprocess import sys import tempfile import threading import time from datetime import datetime, timezone import urllib.request ROOT=Path(__file__).resolve().parent IMAGE='vllm/vllm-openai@sha256:a4a4c0437bf7240089da5f08aa370c4aee17ae5290f7a3b468825ee26c4c3a6b' # Model identity: official W4A16 CT base files + our unmerged adapter (MODEL_MANIFEST.json). _MANIFEST=json.loads((ROOT/'MODEL_MANIFEST.json').read_text(encoding='utf-8')) MODEL_KEY='metask-gemma4-31b-lora-w4a16' MODEL_REVISION=_MANIFEST['repo_id']+'@'+str(_MANIFEST.get('revision') or 'unpublished') # q=0.056: routed; the quota counter below --state-dir caps the share of slow (drafting) decisions. SERVED_MODE='routed' VERSIONS={'vllm':'0.31.0','torch':'2.13.0','transformers':'5.17.0','tokenizers':'0.23.2', 'triton':'3.7.1','compressed-tensors':'0.17.0'} MODEL_SHA256={n:v['sha256'] for n,v in _MANIFEST['files'].items() if not n.endswith('.safetensors')} SHARD_BYTES={n:v['size'] for n,v in _MANIFEST['files'].items() if n.endswith('.safetensors')} SHARD_SHA256={n:v['sha256'] for n,v in _MANIFEST['files'].items() if n.endswith('.safetensors')} # Unmerged LoRA: /adapter holds exactly these files; configs name it by ADAPTER_REF. ADAPTER_REF='model-subdir:adapter' _ADAPTER=_MANIFEST.get('adapter') ADAPTER_SUBDIR=(_ADAPTER or {}).get('subdir') or 'adapter' ADAPTER_FILES={n:dict(v) for n,v in ((_ADAPTER or {}).get('files') or {}).items()} RUN_ID=re.compile(r'[A-Za-z0-9][A-Za-z0-9._-]{0,63}') BINDS=('127.0.0.1','0.0.0.0') STARTUP_TIMEOUT=1800 # vLLM places engine IPC sockets in TMPDIR; Unix socket paths are limited to 107 bytes. MAX_TMP_PATH=70 # Startup attempts: vLLM's own choice (auto), then TRITON_ATTN. This checkpoint mixes attention # head sizes 256 and 512; where FlashAttention 4 is unavailable (for example compute capability # 8.x, the measured RTX 4090) vLLM 0.31 resolves auto to TRITON_ATTN, and FLASH_ATTN cannot start. ATTEMPTS=(None,'TRITON_ATTN') # Model folder entries a loader may read; among them only the pinned files are accepted. LOADER_SUFFIXES=('.json','.safetensors','.bin','.pt','.pth','.gguf','.model','.tiktoken','.jinja','.py','.txt') # Inherited path variables must stay inside --state-dir; unset ones default below state/cache or HOME. PATH_VARIABLES=('XDG_CACHE_HOME','VLLM_CACHE_ROOT','TRITON_CACHE_DIR','TORCHINDUCTOR_CACHE_DIR','HF_HOME', 'CUDA_CACHE_PATH','NUMBA_CACHE_DIR','XDG_CONFIG_HOME','XDG_DATA_HOME','XDG_STATE_HOME', 'VLLM_CONFIG_ROOT','TRITON_HOME','FLASHINFER_WORKSPACE_BASE','TORCH_HOME','TORCH_EXTENSIONS_DIR', 'MPLCONFIGDIR','HF_HUB_CACHE','HUGGINGFACE_HUB_CACHE','TRANSFORMERS_CACHE','HF_ASSETS_CACHE') def check_preparation(config=None,allow_nonformal=False): c=json.loads(Path(config or ROOT/'config.json').read_text(encoding='utf-8')) if not c.get('package',{}).get('nonformal_fitted') or c['package'].get('sample_only'): raise ValueError('Service fit pending; preparation placeholders cannot serve') if not _MANIFEST.get('identity_sha256') or not _MANIFEST['files'] or not ADAPTER_FILES: raise ValueError('Model identity pending') if c.get('pipeline',{}).get('model_identity_sha256')!=_MANIFEST['identity_sha256']: raise ValueError('Configuration and model manifest identities differ') if c['model'].get('adapter')!=ADAPTER_REF: raise ValueError('Configuration adapter reference differs from the model manifest') if c.get('routing',{}).get('mode')!=SERVED_MODE: raise ValueError('Configuration mode differs from the served fast-only mode') if not allow_nonformal: if not re.fullmatch('[0-9a-f]{40}',str(_MANIFEST.get('revision') or '')) or _MANIFEST['repo_id'].startswith('OWNER/'): raise ValueError('The model manifest lacks the own Hugging Face repository and full revision') if not c['package'].get('finalized') or not (ROOT/'FREEZE.json').is_file(): raise ValueError('Package has not been frozen (no FREEZE.json); use a frozen release package') def check_adapter(model): """Read-only check of /adapter: exactly the pinned files, regular, pinned size and full SHA-256.""" if _ADAPTER is None: return None if not ADAPTER_FILES: raise ValueError('Adapter identity pending: rebuild with the staged model folder') folder=Path(model)/ADAPTER_SUBDIR if folder.is_symlink() or not folder.is_dir(): raise ValueError(f'Missing adapter folder {ADAPTER_SUBDIR}/ in the model folder; it must hold '+', '.join(sorted(ADAPTER_FILES))) names={p.name for p in folder.iterdir()} if names!=set(ADAPTER_FILES): raise ValueError(f'{ADAPTER_SUBDIR}/ must contain exactly: '+', '.join(sorted(ADAPTER_FILES))) for name,entry in sorted(ADAPTER_FILES.items()): path=_model_file(folder,name) if path.stat().st_size!=entry['size'] or sha256(path)!=entry['sha256']: raise ValueError(f'Adapter file differs from {MODEL_REVISION}: {name}') return folder.resolve() def materialize_adapter(config,adapter): """Replace ADAPTER_REF by the verified adapter folder in model.adapter and the threshold identity.""" c=json.loads(json.dumps(config)) blocks=[c.get('routing',{})]+[p.get('routing',{}) for p in c.get('profiles',{}).values()] if adapter is None: if c['model'].get('adapter') is not None or any((p.get('model') or {}).get('adapter') is not None for p in c.get('profiles',{}).values()): raise ValueError('Configuration names an adapter, but this arm serves none') return c if c['model'].get('adapter')!=ADAPTER_REF: raise ValueError('Adapter configuration must use the model-relative adapter reference') value=str(adapter) c['model']['adapter']=value for profile in c.get('profiles',{}).values(): model=profile.get('model',{}) if 'adapter' in model: if model['adapter']!=ADAPTER_REF:raise ValueError('Profile names a different adapter') model['adapter']=value for block in blocks: fit=block.get('margin_threshold_fitted_for') if isinstance(fit,dict) and 'adapter' in fit: if fit['adapter']!=ADAPTER_REF:raise ValueError('Threshold was fitted for a different adapter reference') fit['adapter']=value return c def write_runtime_config(startup,config): """The materialized per-run configuration lives only below --state-dir.""" path=Path(startup)/'config.runtime.json' with path.open('x',encoding='utf-8',newline='\n') as out: json.dump(config,out,ensure_ascii=False,indent=2,allow_nan=False) out.write('\n') return path def check_python(version_info=None): version=tuple((version_info or sys.version_info)[:2]) if version<(3,10):raise ValueError('Python >= 3.10 required') def check_versions(): found={} for name,expected in VERSIONS.items(): try:actual=importlib.metadata.version(name) except importlib.metadata.PackageNotFoundError: raise ValueError(f'{name} is not installed; run in {IMAGE}') from None if actual.split('+',1)[0]!=expected: raise ValueError(f'{name} {actual} found, {expected} required; run in {IMAGE}') found[name]=actual return found def _absolute(value,flag): path=Path(value) if not path.is_absolute():raise ValueError(f'{flag} must be an absolute path') return path.resolve() def _overlap(a,b): return a==b or a.is_relative_to(b) or b.is_relative_to(a) def check_paths(model,state_dir): model=_absolute(model,'--model');state=_absolute(state_dir,'--state-dir') for path,flag in ((model,'--model'),(state,'--state-dir')): if _overlap(path,ROOT):raise ValueError(f'{flag} must be outside the package and must not contain it') if _overlap(model,state):raise ValueError('--model and --state-dir must not contain each other') return model,state def sha256(path): digest=hashlib.sha256() with Path(path).open('rb') as source: for chunk in iter(lambda:source.read(1<<20),b''):digest.update(chunk) return digest.hexdigest() MODEL_FOLDER_RULE=('MODEL must contain regular files ' '(a Hugging Face cache snapshots/ directory cannot be mounted alone)') def _model_file(model,name): path=model/name if path.is_symlink(): try:path.resolve(strict=True) except (OSError,RuntimeError): # RuntimeError: a link loop before Python 3.13 raise ValueError(f'Model file is a dangling or looping symbolic link: {name}; {MODEL_FOLDER_RULE}') from None raise ValueError(f'Model file is a symbolic link: {name}; {MODEL_FOLDER_RULE}') if not path.is_file():raise ValueError('Missing model file: '+name) return path def check_model(model,full=False): """Read-only identity check of the model folder against MODEL_REVISION; nothing is written. The pinned files must be regular files with the pinned SHA-256 (shards: size, or full hash). Any other entry a loader may read (weights, tokenizer, processor or template files) is refused. """ model=Path(model) if not MODEL_SHA256 or not SHARD_BYTES:raise ValueError('Model identity pending') if not model.is_dir():raise ValueError('Model folder not found: '+str(model)) pinned={*MODEL_SHA256,*SHARD_BYTES,'PREPARATION.json'} for entry in sorted(model.iterdir()): name=entry.name if name not in pinned and (name.lower().endswith(LOADER_SUFFIXES) or name.lower().startswith('tokenizer')): raise ValueError(f'Unexpected file in model folder: {name}; use the files of {MODEL_REVISION} only') for name,expected in MODEL_SHA256.items(): path=_model_file(model,name) if sha256(path)!=expected:raise ValueError(f'Model file differs from {MODEL_REVISION}: {name}') for name,size in SHARD_BYTES.items(): path=_model_file(model,name) if path.stat().st_size!=size:raise ValueError(f'Model shard size differs from {MODEL_REVISION}: {name}') if full and sha256(path)!=SHARD_SHA256[name]:raise ValueError(f'Model shard differs from {MODEL_REVISION}: {name}') check_adapter(model) EXISTING_RUN='run_id already exists in --state-dir; use a new --state-dir (with the README commands: a new WORK)' def prepare_state(state,run_id=None): """Claim /-startup with exclusive creation; nothing is overwritten or removed.""" state=Path(state) rid=run_id or datetime.now(timezone.utc).strftime('run-%Y%m%dT%H%M%S%fZ') if not RUN_ID.fullmatch(rid):raise ValueError('run_id must match [A-Za-z0-9][A-Za-z0-9._-]{0,63}') startup=state/(rid+'-startup') if len(os.fsencode(startup/'tmp'))>MAX_TMP_PATH: raise ValueError(f'--state-dir path is too long for engine sockets; keep {startup/"tmp"} within {MAX_TMP_PATH} bytes') try:state.mkdir(parents=True,exist_ok=True) except OSError:raise ValueError('State directory is not writable: '+str(state)) from None if (state/rid).exists() or startup.exists():raise ValueError(EXISTING_RUN) cache=state/'cache' if cache.is_symlink() or (cache.exists() and not cache.is_dir()): raise ValueError(f'{cache} must be a directory, not a link or a file') try: startup.mkdir() for name in ('tmp','home'):(startup/name).mkdir() cache.mkdir(exist_ok=True) with (startup/'attempts.jsonl').open('x',encoding='utf-8') as probe: probe.flush();os.fsync(probe.fileno()) except FileExistsError:raise ValueError(EXISTING_RUN) from None except OSError:raise ValueError('State directory is not writable: '+str(state)) from None if cache.is_symlink() or not cache.resolve().is_relative_to(state.resolve()): raise ValueError(f'{cache} must be a directory inside --state-dir, not a link') return rid,startup def check_environment(base,state): """Refuse inherited cache or configuration paths outside --state-dir; nothing is written. An inherited path variable is accepted only if it is absolute and inside --state-dir, so writes cannot leave the state directory. start() runs this before claiming the run name. """ root=Path(state).resolve() for key in PATH_VARIABLES: if key not in base:continue value=Path(base[key]) if not value.is_absolute() or not value.resolve().is_relative_to(root): raise ValueError(f'{key}={base[key]} is outside --state-dir; do not set cache or configuration paths') def child_environment(base,state,startup): """Engine environment: offline, no telemetry, every write location below --state-dir.""" check_environment(base,state) env=dict(base) env['VLLM_BATCH_INVARIANT']='1' env['VLLM_WORKER_MULTIPROC_METHOD']='spawn' # vLLM 0.31 no longer reads the legacy attention variable; an inherited value would only log a warning. env.pop('VLLM_ATTENTION_BACKEND',None);env.pop('JEV_ATTENTION_BACKEND',None) env.update(VLLM_USE_FLASHINFER_SAMPLER='0',VLLM_NO_USAGE_STATS='1',DO_NOT_TRACK='1', HF_HUB_OFFLINE='1',TRANSFORMERS_OFFLINE='1',PYTHONDONTWRITEBYTECODE='1',PYTHONUNBUFFERED='1', VLLM_HOST_IP='127.0.0.1',HOME=str(startup/'home'),TMPDIR=str(startup/'tmp')) cache=Path(state)/'cache' for key,sub in (('XDG_CACHE_HOME',None),('VLLM_CACHE_ROOT','vllm'),('TRITON_CACHE_DIR','triton'), ('TORCHINDUCTOR_CACHE_DIR','inductor'),('HF_HOME','hf'),('CUDA_CACHE_PATH','nv'), ('NUMBA_CACHE_DIR','numba')): env.setdefault(key,str(cache/sub if sub else cache)) return env def validate_health(health, no_cache=False): bad=health.get('warnings',[]) if bad or health.get('status','ok')!='ok' or health.get('prefix_caching') is not (not no_cache) or health.get('supports_slow_session') is not True: raise RuntimeError('Service readiness rejected: health warnings or cache/session capability; health='+json.dumps(health)[:1500]) def start(model, port, mode='routed', config=None, no_cache=False, state_dir=None, run_id=None, bind='127.0.0.1', allow_nonformal=False): check_preparation(config,allow_nonformal) check_python() check_versions() if bind not in BINDS:raise ValueError('--bind must be one of '+', '.join(BINDS)) if state_dir is None:raise ValueError('--state-dir is required') model,state=check_paths(model,state_dir) check_environment(os.environ,state) # an input check: refused before anything is written check_model(model) from model_setup import report_processor_configs report_processor_configs(model) cfg=Path(config or ROOT/'config.json').resolve() c=json.loads(cfg.read_text(encoding='utf-8')) if mode=='routed' and not c.get('package',{}).get('finalized') and not allow_nonformal: raise ValueError('Real fitted parameters must be finalized before routed serving') try: with socket.socket() as probe: probe.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) probe.bind((bind,port)) except OSError:raise ValueError(f'Port {port} on {bind} is not available') from None runtime=materialize_adapter(c,check_adapter(model)) rid,startup=prepare_state(state,run_id) cfg=write_runtime_config(startup,runtime) tempfile.tempdir=str(startup/'tmp') env=child_environment(os.environ,state,startup) args=[sys.executable,'-B',str(ROOT/'server.py'),'--config',str(cfg),'--model',str(model), '--bind',bind,'--port',str(port),'--backend','vllm','--quant','awq', '--mode',mode,'--model-key',MODEL_KEY,'--state-dir',str(state),'--run-id',rid] if no_cache:args.append('--no-prefix-caching') return start_attempts(args,env,startup,port,mode,no_cache) def start_attempts(args,env,startup,port,mode='routed',no_cache=False,timeout=STARTUP_TIMEOUT): """auto first; TRITON_ATTN only if the engine fails to start. Each attempt has its own deadline.""" startup=Path(startup) opener=urllib.request.build_opener(urllib.request.ProxyHandler({})) # readiness never uses a proxy def event(attempt,backend,result,log=None): # Best effort: a record that cannot be written or printed never blocks starting or stopping. record=dict(attempt=attempt,requested_backend=backend or 'auto',result=result) if log is not None:record['log']=str(log) line=json.dumps(record) try: with (startup/'attempts.jsonl').open('a',encoding='utf-8',newline='\n') as out: out.write(line+'\n') except OSError:pass try:print(line,flush=True) except OSError:pass for attempt,backend in enumerate(ATTEMPTS,1): child_env=dict(env) child_env.pop('VLLM_ATTENTION_BACKEND',None);child_env.pop('JEV_ATTENTION_BACKEND',None) if backend:child_env['JEV_ATTENTION_BACKEND']=backend log_path=startup/f'attempt-{attempt}.log' child_env['JEV_ATTENTION_LOG']=str(log_path) child_args=list(args) # Separate quota state per attempt: a failed warmup never counts toward the served run. child_args[child_args.index('--state-dir')+1]=str(startup/f'state-{attempt}') event(attempt,backend,'starting',log_path) proc=None try: with log_path.open('xb') as out: proc=subprocess.Popen(child_args,cwd=ROOT,env=child_env, stdout=out,stderr=subprocess.STDOUT,start_new_session=True) deadline=time.monotonic()+timeout while time.monotonic()