Text Classification
Transformers
Safetensors
qwen3_5
skillscreen
agent-security
nli
sbr
SkillScreen-2B-SBR / score.py
JerroldH's picture
Add verified seed-42 full model and original adapter
ebc28fd verified
Raw History Blame Contribute Delete
3.13 kB
"""Score inert skill text with SkillScreen. Default decision threshold is 0.5."""
import argparse
import json
from pathlib import Path
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoModelForSequenceClassification, AutoTokenizer
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('skill', type=Path, help='SKILL.md, or package JSON for the Package model')
parser.add_argument('--model', default=str(Path(__file__).resolve().parent))
parser.add_argument('--revision', default='main')
args = parser.parse_args()
if not torch.cuda.is_available():
raise RuntimeError('This verified recipe requires CUDA with BF16 support')
local = Path(args.model).is_dir()
path = Path(args.model) / 'scoring_config.json' if local else Path(
hf_hub_download(args.model, 'scoring_config.json', revision=args.revision))
settings = json.loads(path.read_text())
options = {'local_files_only': True} if local else {'revision':args.revision}
tokenizer = AutoTokenizer.from_pretrained(args.model, trust_remote_code=False, **options)
assert tokenizer.num_special_tokens_to_add(False) == 0
if settings['input']['scope'] == 'SKILL.md + related file contents':
from package_inputs import encode_package
row = json.loads(args.skill.read_text(encoding='utf-8'))
ids, original, _ = encode_package(tokenizer, row,
settings['input']['hypothesis'], settings['input']['max_length'])
else:
text = args.skill.read_text(encoding='utf-8')
if len(text) > 262144:
raise ValueError('For inputs above 262,144 characters, use the research pipeline bounded encoder')
suffix = '\nHypothesis: ' + settings['input']['hypothesis']
suffix_ids = tokenizer.encode(suffix, add_special_tokens=False)
ids = tokenizer.encode('Premise: ' + text + suffix, add_special_tokens=False)
original = len(ids)
limit = settings['input']['max_length']
if original > limit:
ids = ids[:limit - len(suffix_ids)] + suffix_ids
model = AutoModelForSequenceClassification.from_pretrained(args.model,
dtype=torch.bfloat16, attn_implementation='sdpa', trust_remote_code=False, **options)
assert model.config.label2id == {'contradiction':0,'entailment':1,'neutral':2}
tokenizer.pad_token_id = model.config.pad_token_id
model.config.use_cache = model.config.text_config.use_cache = False
model = model.cuda().eval()
inputs = torch.tensor([ids], dtype=torch.long, device='cuda')
with torch.inference_mode(), torch.autocast('cuda', dtype=torch.bfloat16):
logits = model(input_ids=inputs, attention_mask=torch.ones_like(inputs)).logits[0].float().double()
if not torch.isfinite(logits).all():
raise ValueError('Nonfinite model score')
probability = logits.softmax(-1)[1].item()
print(json.dumps({'malicious_probability':probability,'suspicious':probability >= 0.5,
'truncated':original > settings['input']['max_length']}, indent=2))
if __name__ == '__main__':
main()