Download inference_runtime.py from FullyAutomated/granite-system1-350m-experimental: direct link, hf CLI and curl.
- Browser
- Download file 9.14 kB
-
https://huggingface.co/FullyAutomated/granite-system1-350m-experimental/resolve/main/inference_runtime.py
- Command line
-
hf download hf://FullyAutomated/granite-system1-350m-experimental/inference_runtime.py
-
curl -L -o inference_runtime.py https://huggingface.co/FullyAutomated/granite-system1-350m-experimental/resolve/main/inference_runtime.py
9.14 kB
| """Inference-only formula excerpts from the source-bound canonical runtime.""" | |
| from dataclasses import dataclass | |
| import hashlib | |
| import json | |
| import re | |
| from pathlib import Path | |
| import torch | |
| from torch import nn | |
| MAX_TOKENS = 32768 | |
| TRAIN_TOKENS = 768 | |
| def file_sha256(path): | |
| digest = hashlib.sha256() | |
| with Path(path).open('rb') as stream: | |
| for chunk in iter(lambda: stream.read(4 * 1024 * 1024), b''): | |
| digest.update(chunk) | |
| return digest.hexdigest() | |
| def audit_loading_info(info, source_keys): | |
| if not isinstance(info, dict) or not all(k in info for k in ('missing_keys', 'unexpected_keys', 'mismatched_keys')): | |
| raise ValueError('missing loading audit fields') | |
| unexpected = set(info['unexpected_keys']) | |
| allowed = {'lm_head.weight'} if 'lm_head.weight' in source_keys else set() | |
| if info['missing_keys'] or info['mismatched_keys'] or info.get('error_msgs') or unexpected - allowed: | |
| raise ValueError(f'base loading audit failed: {info}') | |
| return {'missing_keys': [], 'mismatched_keys': [], 'unexpected_keys': sorted(unexpected), | |
| 'allowed_unexpected_keys': sorted(unexpected), | |
| 'exception_reason': ('LM vocabulary head is deliberately excluded by AutoModel; source key confirmed' | |
| if unexpected else None)} | |
| def load_base(lock): | |
| """Only this boundary touches pretrained assets; local and fail-closed.""" | |
| from safetensors import safe_open | |
| from transformers import AutoModel, AutoTokenizer | |
| if lock.get('model') != 'ibm-granite/granite-4.0-350m': | |
| raise ValueError('wrong locked model') | |
| if not isinstance(lock.get('revision'), str) or not re.fullmatch('[0-9a-f]{40}', lock['revision']): | |
| raise ValueError('invalid locked revision') | |
| sha = lock.get('model_safetensors_sha256') | |
| if not isinstance(sha, str) or not re.fullmatch('[0-9a-f]{64}', sha): | |
| raise ValueError('invalid source hash') | |
| local = Path(lock['local_path']) | |
| if not local.is_absolute() or lock.get('max_context_tokens') != MAX_TOKENS: | |
| raise ValueError('invalid local path or context lock') | |
| source = local / 'model.safetensors' | |
| actual = file_sha256(source) | |
| if actual != sha: | |
| raise ValueError('source model hash mismatch') | |
| with safe_open(str(source), framework='pt', device='cpu') as handle: | |
| source_keys = set(handle.keys()) | |
| model, info = AutoModel.from_pretrained(str(local), local_files_only=True, | |
| trust_remote_code=False, attn_implementation='sdpa', | |
| dtype=torch.float32, output_loading_info=True) | |
| audit = audit_loading_info(info, source_keys) | |
| if model.config.model_type != 'granitemoehybrid' or model.config.hidden_size != 1024: | |
| raise ValueError('unexpected base architecture (requires GraniteMoeHybrid hidden=1024)') | |
| model.to(device='cpu', dtype=torch.float32).eval() | |
| tokenizer = AutoTokenizer.from_pretrained(str(local), local_files_only=True, trust_remote_code=False) | |
| assets = ('config.json', 'tokenizer.json', 'tokenizer_config.json', | |
| 'special_tokens_map.json', 'vocab.json', 'merges.txt', 'added_tokens.json', 'tokenizer.model') | |
| asset_hashes = {name: file_sha256(local/name) for name in assets if (local/name).is_file()} | |
| return model, tokenizer, dict(audit, source_sha256=actual, source_file=str(source), asset_sha256=asset_hashes) | |
| def architecture(model): | |
| return {'schema_version': 1, 'backbone_class': type(model.backbone).__name__, | |
| 'backbone_config': json.loads(model.backbone.config.to_json_string(use_diff=False)), | |
| 'hidden_size': model.backbone.config.hidden_size, 'head_hidden': 128, | |
| 'head_activation': 'GELU', 'head_outputs': 1, 'shared_option_head': True, | |
| 'separate_none_head': True, 'rank': 8, 'alpha': 16, 'dropout': 0, | |
| 'adapter_targets': model.adapter_targets, | |
| 'option_readout': 'option_segment_last_token', 'none_readout': 'final_segment_last_token', | |
| 'prompt_version': 'context_question_json_options_decision_v1', | |
| 'tokenization': 'encode_each_segment_add_special_tokens_false', | |
| 'max_input_tokens': MAX_TOKENS, 'train_max_tokens': TRAIN_TOKENS, | |
| 'dtype': 'float32', 'device': 'cpu', 'attention': 'sdpa', 'use_cache': False} | |
| def tensor_hash(items): | |
| digest = hashlib.sha256() | |
| for name, tensor in sorted(items): | |
| value = tensor.detach().cpu().contiguous() | |
| digest.update(name.encode()) | |
| digest.update(str(value.dtype).encode()) | |
| digest.update(str(tuple(value.shape)).encode()) | |
| digest.update(value.reshape(-1).view(torch.uint8).numpy().tobytes()) | |
| return digest.hexdigest() | |
| def frozen_hash(model): | |
| return tensor_hash([(n, p) for n, p in model.named_parameters() if not p.requires_grad] | |
| + list(model.named_buffers())) | |
| def trainable_state(model): | |
| return {n: p.detach().cpu().contiguous() for n, p in model.named_parameters() if p.requires_grad} | |
| class LoRALinear(nn.Module): | |
| """Frozen linear + rank-eight update; zero B preserves initial backbone.""" | |
| def __init__(self, base, rank=8, alpha=16): | |
| super().__init__() | |
| if not isinstance(base, nn.Linear): | |
| raise ValueError('LoRA target must be nn.Linear') | |
| self.base = base | |
| self.base.requires_grad_(False) | |
| self.rank, self.alpha = rank, alpha | |
| self.A = nn.Parameter(torch.empty(rank, base.in_features, dtype=base.weight.dtype)) | |
| self.B = nn.Parameter(torch.zeros(base.out_features, rank, dtype=base.weight.dtype)) | |
| nn.init.kaiming_uniform_(self.A, a=5 ** 0.5) | |
| def forward(self, x): | |
| return self.base(x) + nn.functional.linear(nn.functional.linear(x, self.A), self.B) * (self.alpha / self.rank) | |
| class OptionScorer(nn.Module): | |
| def __init__(self, backbone): | |
| super().__init__() | |
| self.backbone = backbone | |
| self.backbone.requires_grad_(False) | |
| attention = [(i, layer.self_attn) for i, layer in enumerate(backbone.layers) | |
| if hasattr(layer, 'self_attn') and layer.self_attn is not None] | |
| if len(attention) < 4: | |
| raise ValueError('backbone requires at least four attention layers') | |
| self.adapter_targets = [] | |
| for index, attn in attention[-4:]: | |
| for name in ('q_proj', 'v_proj'): | |
| setattr(attn, name, LoRALinear(getattr(attn, name))) | |
| self.adapter_targets.append(f'layers.{index}.self_attn.{name}') | |
| hidden = backbone.config.hidden_size | |
| self.option_head = nn.Sequential(nn.Linear(hidden, 128), nn.GELU(), nn.Linear(128, 1)) | |
| self.none_head = nn.Sequential(nn.Linear(hidden, 128), nn.GELU(), nn.Linear(128, 1)) | |
| self.train(False) | |
| def train(self, mode=True): | |
| super().train(mode) | |
| self.backbone.eval() | |
| for module in self.backbone.modules(): | |
| if isinstance(module, LoRALinear): | |
| module.training = mode | |
| return self | |
| def forward(self, encoded): | |
| ids = torch.tensor([encoded.input_ids], dtype=torch.long, device='cpu') | |
| hidden = self.backbone(input_ids=ids, attention_mask=torch.ones_like(ids), | |
| use_cache=False, return_dict=True).last_hidden_state[0] | |
| options = self.option_head(hidden[encoded.option_positions]).squeeze(-1) | |
| none = self.none_head(hidden[encoded.final_position]).reshape(1) | |
| return torch.cat((options, none)) | |
| def validate_request(request, *, labeled=False): | |
| if not isinstance(request, dict): | |
| raise ValueError('request must be an object') | |
| for key in ('context', 'question'): | |
| if not isinstance(request.get(key), str) or (key == 'question' and not request[key].strip()): | |
| raise ValueError(f'{key} must be a string (question nonempty)') | |
| options = request.get('options') | |
| if not isinstance(options, list) or not 2 <= len(options) <= 16: | |
| raise ValueError('options must contain 2..16 choices') | |
| ids = [] | |
| for option in options: | |
| if not isinstance(option, dict): | |
| raise ValueError('option must be an object') | |
| for key in ('id', 'description'): | |
| if not isinstance(option.get(key), str) or not option[key].strip(): | |
| raise ValueError(f'option {key} must be nonempty string') | |
| ids.append(option['id']) | |
| if len(set(ids)) != len(ids): | |
| raise ValueError('option ids must be unique') | |
| if 'target' in request and request['target'] is not None and request['target'] not in ids: | |
| raise ValueError('target must be option id or null') | |
| if labeled: | |
| for key in ('id', 'family'): | |
| if not isinstance(request.get(key), str) or not request[key].strip(): | |
| raise ValueError(f'{key} must be nonempty') | |
| if 'target' not in request: | |
| raise ValueError('labeled row requires target') | |
| return request | |
| class EncodedRequest: | |
| input_ids: list | |
| option_positions: list | |
| final_position: int | |
| text: str | |