Zero-Shot Classification
Laya
Safetensors
PyTorch
English
Spanish
intent-routing
tool-routing
semantic-router
zero-shot
bilingual
english
spanish
out-of-scope-detection
knowledge-distillation
modernbert
teacher-model
Eval Results (legacy)
Instructions to use lapislabsai/lapis-sal-421M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Laya
How to use lapislabsai/lapis-sal-421M with Laya:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Download lapis_runtime.py from lapislabsai/lapis-sal-421M: direct link, hf CLI and curl.
- Browser
- Download file 6.42 kB
-
https://huggingface.co/lapislabsai/lapis-sal-421M/resolve/main/lapis_runtime.py
- Command line
-
hf download hf://lapislabsai/lapis-sal-421M/lapis_runtime.py
-
curl -L -o lapis_runtime.py https://huggingface.co/lapislabsai/lapis-sal-421M/resolve/main/lapis_runtime.py
6.42 kB
| """Shared local loaders and provenance helpers; no downloads or publication.""" | |
| from __future__ import annotations | |
| import hashlib | |
| import importlib.util | |
| from importlib.metadata import distribution, version, PackageNotFoundError | |
| import json | |
| from pathlib import Path | |
| import random | |
| import sys | |
| def sha256(path): | |
| digest = hashlib.sha256() | |
| with Path(path).open('rb') as source: | |
| for block in iter(lambda: source.read(1024 * 1024), b''): | |
| digest.update(block) | |
| return digest.hexdigest() | |
| def model_fingerprint(model_dir, backend): | |
| model_dir = Path(model_dir) | |
| names = { | |
| 'torch': ('model.safetensors', 'encoder/config.json', 'tokenizer/tokenizer.json', 'rl_agent_config.json'), | |
| 'onnx': ('laya.onnx', 'laya.onnx.data', 'tokenizer.json', 'rl_agent_config.json'), | |
| 'mlx': ('mlx/model.safetensors', 'mlx/config.json', 'tokenizer.json', 'rl_agent_config.json'), | |
| }[backend] | |
| return {name: sha256(model_dir / name) for name in names} | |
| def versions(): | |
| result = {'python': sys.version.split()[0]} | |
| for name in ('torch', 'transformers', 'laya', 'mlx', 'onnxruntime', 'onnx', | |
| 'onnxscript', 'tokenizers', 'safetensors', 'numpy'): | |
| try: | |
| result[name] = version(name) | |
| except PackageNotFoundError: | |
| continue | |
| try: | |
| direct = distribution('laya').read_text('direct_url.json') | |
| if direct: | |
| result['laya_commit'] = json.loads(direct).get('vcs_info', {}).get('commit_id') | |
| except PackageNotFoundError: | |
| pass | |
| return result | |
| def require_pinned_reference(): | |
| installed = versions() | |
| expected = {'torch': '2.14.0', 'transformers': '5.17.0', 'laya': '0.3.20'} | |
| wrong = {name: installed.get(name) for name, value in expected.items() | |
| if installed.get(name, '').split('+')[0] != value} | |
| commit = installed.get('laya_commit') | |
| if commit and commit != '4066d5d5fbf08b66c6757ddeedbd797bd7655bc0': | |
| wrong['laya_commit'] = commit | |
| if wrong: | |
| raise RuntimeError(f'Reference environment differs from the trained checkpoint: {wrong}. ' | |
| 'Use Python 3.12 and pip install -r requirements.txt.') | |
| def load_module(path, name): | |
| spec = importlib.util.spec_from_file_location(name, str(path)) | |
| if spec is None or spec.loader is None: | |
| raise ImportError(f'Cannot load {path}') | |
| module = importlib.util.module_from_spec(spec) | |
| sys.modules[name] = module | |
| spec.loader.exec_module(module) | |
| return module | |
| class TorchSession: | |
| """Same session interface as ONNX/MLX, with a strict local checkpoint load.""" | |
| def __init__(self, checkpoint, device='cpu', threads=4): | |
| require_pinned_reference() | |
| import torch | |
| from safetensors.torch import load_file | |
| from laya.common import build_model | |
| checkpoint = Path(checkpoint) | |
| self.torch = torch | |
| self.device = torch.device(device) | |
| torch.set_num_threads(threads) | |
| cfg = json.loads((checkpoint / 'rl_agent_config.json').read_text()) | |
| self.model = build_model(cfg, encoder_dir=str(checkpoint / 'encoder')) | |
| self.model.encoder.config.reference_compile = False | |
| self.model.load_state_dict(load_file(str(checkpoint / 'model.safetensors')), strict=True) | |
| self.model.float().eval().to(self.device) | |
| def run(self, names, feeds): | |
| if list(names) != ['logits']: | |
| raise ValueError(f'Unsupported outputs: {names}') | |
| torch = self.torch | |
| with torch.inference_mode(): | |
| f = {key: torch.as_tensor(value, device=self.device, | |
| dtype=torch.bool if key == 'marker_mask' else torch.long) | |
| for key, value in feeds.items()} | |
| return [self.model(**f)[0].float().cpu().numpy()] | |
| def load_router(model_dir, backend='onnx', repo_dir=None, threads=4, device='cpu'): | |
| """Load checkpoint or deployment using identical production prompt/decision logic.""" | |
| model_dir = Path(model_dir).resolve() | |
| module_dir = Path(__file__).resolve().parent | |
| project_root = module_dir.parent if module_dir.name == 'scripts' else module_dir | |
| repo_dir = Path(repo_dir) if repo_dir else project_root / 'laya-intent-router' | |
| candidates = (repo_dir / 'laya_intent_router.py', Path(__file__).parent / 'laya_intent_router.py', | |
| model_dir / 'laya_intent_router.py') | |
| runtime = next((path for path in candidates if path.is_file()), None) | |
| if runtime is None: | |
| raise FileNotFoundError('laya_intent_router.py is missing; specify repo_dir or use a complete release package') | |
| mod = load_module(runtime, 'lapis_router_runtime') | |
| if backend != 'torch': | |
| return mod.LayaIntentRouter(str(model_dir), backend=backend, threads=threads, shortlist_k=0) | |
| from tokenizers import Tokenizer | |
| router = mod.LayaIntentRouter.__new__(mod.LayaIntentRouter) | |
| router.cfg = json.loads((model_dir / 'rl_agent_config.json').read_text()) | |
| router.tok = Tokenizer.from_file(str(model_dir / 'tokenizer/tokenizer.json')) | |
| router.cls, router.sep, router.mask = (router.tok.token_to_id(t) for t in ('[CLS]', '[SEP]', '[MASK]')) | |
| router.backend = 'torch' | |
| router.sess = TorchSession(model_dir, device=device, threads=threads) | |
| router.threshold = router.cfg.get('match_threshold', 0.625) | |
| router.temps = router.cfg['temperature_by_options'] | |
| router.k, router.shortlist = 0, None | |
| return router | |
| def load_episode_samples(path, n, seed): | |
| if n < 1: | |
| raise ValueError('Sample count must be positive') | |
| eps = [json.loads(line) for line in Path(path).read_text().splitlines() if line.strip()] | |
| if not eps: | |
| raise ValueError(f'No episodes in {path}') | |
| if any(str(ep.get('split', '')).lower() == 'test' for ep in eps): | |
| raise ValueError('Use development episodes, not the reserved test split') | |
| return random.Random(seed).sample(eps, min(n, len(eps))) | |
| def episode_paths(ep): | |
| paths = ep['paths'] | |
| if isinstance(paths, dict): | |
| return {key: [value] if isinstance(value, str) else value for key, value in paths.items()} | |
| result = {} | |
| for path in paths: | |
| key = path.get('path_id') or path.get('id') or path.get('name') | |
| value = path.get('text') or path.get('texts') or path.get('descriptions') | |
| result[key] = [value] if isinstance(value, str) else list(value) | |
| return result | |