File size: 6,372 Bytes
944d481 2e4bff8 fd041a3 2e4bff8 8cbcdb6 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 fd041a3 944d481 2e4bff8 fd041a3 2e4bff8 fd041a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | """Hugging Face Inference Endpoints custom handler for Banyan Sapling.
Loads the model exactly like the reference `inference.py` shipped with the
weights package (verified against the H200 reference runtime), with two
serving-only deviations:
- skips the strict package-version assertion loop (requirements.txt pins them)
- skips the full 46 GB sha256 pre-hash (the Hub delivers checksummed storage)
"""
from __future__ import annotations
import json
from pathlib import Path
import sys
import time
from typing import Any, Dict
CURRENT_DIR = Path(__file__).resolve().parent
if str(CURRENT_DIR) not in sys.path:
sys.path.insert(0, str(CURRENT_DIR))
import os
os.environ.setdefault('CUBLAS_WORKSPACE_CONFIG', ':4096:8')
def load_model_for_endpoint(root: Path, device: str = "cuda"):
root = Path(root).resolve()
if str(root) not in sys.path:
sys.path.insert(0, str(root))
import torch
from safetensors.torch import load_file
# The text-only runtime does not require torchvision or torchaudio.
sys.modules.setdefault('torchvision', None)
sys.modules.setdefault('torchaudio', None)
from transformers import AutoConfig, AutoModel, AutoTokenizer
from transformers.models.qwen3_moe.modeling_qwen3_moe import Qwen3MoeRotaryEmbedding
from runtime.backbone import attach_lora, set_shared
from runtime.model import FullModel, identity, seed_all, tensor_hash
from runtime.conformal_calibrator import ConformalCalibrator
seed_all(1707)
config_data = json.loads((root / 'config.json').read_text())
architecture = json.loads((root / 'architecture.json').read_text())
expert_counts = [config_data[k] for k in ('num_local_experts', 'num_experts') if k in config_data]
if (not expert_counts or any(n != 96 for n in expert_counts)
or config_data.get('model_type') != 'qwen3_moe'
or identity(config_data) != architecture['hf_config_sha256']
or architecture.get('modify_rope') is not False
or architecture.get('factorized_lora') is not True):
raise ValueError('Unsupported model architecture')
config = AutoConfig.from_pretrained(root, local_files_only=True)
with torch.device('meta'):
backbone = AutoModel.from_config(config, dtype=torch.bfloat16,
attn_implementation='sdpa', experts_implementation='grouped_mm')
attach_lora(backbone, architecture['config'])
if architecture['shared_ids'] is not None:
set_shared(backbone, architecture['shared_ids'])
model = FullModel(backbone)
state = load_file(str(root / 'model.safetensors'), device=device)
model.load_state_dict(state, strict=True, assign=True)
del state
model.backbone.rotary_emb = Qwen3MoeRotaryEmbedding(config, device=device)
rope = model.backbone.rotary_emb
scaling = rope.attention_scaling
fingerprint = {'inv_freq': tensor_hash({'inv_freq': rope.inv_freq}),
'attention_scaling': scaling.item() if hasattr(scaling, 'item') else scaling}
if fingerprint != architecture['rotary_fingerprint']:
raise ValueError('Native positional encoding does not match the checkpoint')
if any(t.is_meta for t in list(model.parameters()) + list(model.buffers())):
raise ValueError('Incomplete model materialization')
for name, parameter in model.named_parameters():
expected = torch.float32 if name.startswith('head.') or name.endswith(('.A', '.B')) or '.mlp.gate.' in name else torch.bfloat16
if parameter.dtype != expected:
raise ValueError(f'Unexpected precision: {name}')
model.requires_grad_(False)
model.eval()
tokenizer = AutoTokenizer.from_pretrained(root / 'tokenizer', local_files_only=True)
calibrator = ConformalCalibrator.from_dict(json.loads((root / 'calibration.json').read_text()))
manifest_path = root / 'distribution_manifest.json'
if manifest_path.exists():
manifest = json.loads(manifest_path.read_text())
else:
manifest = {'source_release_sha256': '', 'files': {'model.safetensors': ''}}
return model, tokenizer, calibrator, manifest
def predict_for_endpoint(model, tokenizer, calibrator, manifest, request):
import torch
from runtime.backbone import encode
from runtime.model import infer
row = encode(tokenizer, request, 16384)
pad = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else tokenizer.eos_token_id
start = time.perf_counter()
logits = infer(model, [row], pad)[0]
detail = calibrator.predict_detailed(logits.reshape(1, 4), empty_set_policy='rounded_argmax')[0]
probabilities = torch.softmax(logits.double() / calibrator.temperature, -1).tolist()
return {'model': 'banyan-sapling', 'release_stage': 'research-preview',
'source_release_sha256': manifest.get('source_release_sha256', ''),
'weights_sha256': manifest.get('files', {}).get('model.safetensors', ''),
'logits': logits.tolist(), **detail,
'probabilities': dict(zip(calibrator.LABELS, probabilities)),
'usage': {'input_tokens': len(row['input_ids']), 'generated_tokens': 0},
'timing_ms': {'model': (time.perf_counter() - start) * 1000}}
class EndpointHandler:
def __init__(self, path: str = "") -> None:
model_dir = Path(path) if path else CURRENT_DIR
self.model, self.tokenizer, self.calibrator, self.manifest = load_model_for_endpoint(
model_dir, device="cuda"
)
def __call__(self, data: Dict[str, Any]) -> Dict[str, Any]:
payload = data.get("inputs", data)
if isinstance(payload, str):
try:
payload = json.loads(payload)
except Exception:
return {"error": "Invalid payload: expected JSON object with policy, evidence, and claim strings."}
if not isinstance(payload, dict):
return {"error": "Payload must be a dictionary with policy, evidence, and claim fields."}
for field in ("policy", "evidence", "claim"):
val = payload.get(field)
if not val or not str(val).strip():
return {"error": f"Missing required non-empty string field: '{field}'"}
return predict_for_endpoint(self.model, self.tokenizer, self.calibrator, self.manifest, payload)
|